Appearance
Notifications
Overview
The Notifications System provides comprehensive notification management for the Admin Frontend application. It includes system notifications, user notifications, email notifications, notification settings, and real-time notification display. The system supports both workspace-level (organization) and user-level notification preferences.
Architecture
System Components
- Notification Display: Main notification bell component with system and announcements tabs
- Notification Settings: User and organization-level notification preferences
- Email Notifications: Email notification alerts and preferences
- Notification Store: State management for notifications
- Notification Badges: Unread notification counts
- Permission Management: Access control for notification management
File Structure
Core Notification Components
Vue Component Files (.vue)
components/theme/global/DamNotification.vue- Main notification bell component- System notifications tab
- Announcements tab
- Notification list display
- Mark as read functionality
- Notification actions
- Badge count display
components/dam/EmailNotificationsAlert.vue- Email notifications alert component- Email notification preferences alert
- Email verification status
- Email settings link
- Alert dismissal
components/dam/StaticPlanNotificationSettings.vue- Static plan notification settings component- Plan-based notification settings
- Static notification preferences
- Plan limitations display
SVG Icon Components
components/svg/CollageDaysNotification.vue- Days notification icon- Used in notification UI
- Consistent iconography
Notification Settings & Management
Vue Page Files (.vue)
pages/_workspace_id/workspace-settings/dam/_instance_id/notifications/index.vue- Organization notification settings page- Admin-only access
- Organization-level notification settings
- Notification preferences management
- Email notification settings
Route: /:workspace_id/workspace-settings/dam/:instance_id/notifications
Vue Component Files (.vue)
components/dam/Profile/NotificationSetting.vue- User profile notification settings component- User-level notification preferences
- Notification type toggles
- Email notification preferences
- Save preferences
components/dam/table/profileNotificationSettings.vue- Profile notification settings table component- Notification settings table display
- Settings management interface
- Bulk settings update
Notification Display & Integration
Vue Component Files (.vue)
components/theme/global/LeftMenu.vue- Left menu component- May include notification access
- Notification link/icon
components/theme/settings/AccountSettingsLeftMenu.vue- Account settings menu- May include notification settings link
- Navigation to notification settings
pages/_workspace_id/workspace-settings/index.vue- Workspace settings page- May reference notifications
- Notification settings access
pages/profile.vue- Profile page- May include notification settings
- User notification preferences
Notification Store & State Management
JavaScript Files (.js)
store/dam.js- DAM store- Notification state management
- Notification actions (fetch, mark read, mark all read)
- Badge count state
- Notification list state
Supporting Files
JavaScript Files (.js)
plugins/helper.js- Helper functions plugincanManageNotificationspermission check- Notification utility functions
- Permission validation
middleware/can-access-dam-module.js- DAM module access middleware- May check notification permissions
- Access control for notification features
Notification Structure
System Notification Object
javascript
{
id: 1,
workspace_id: 123,
type: "system", // "system" or "announcement"
title: "System Maintenance",
message: "Scheduled maintenance on January 20, 2024",
priority: "high", // "low", "medium", "high"
is_read: false,
created_at: "2024-01-15T10:00:00Z",
read_at: null,
action_url: "/workspace/123/settings",
action_text: "View Settings"
}User Notification Settings Object
javascript
{
id: 1,
user_id: 456,
workspace_id: 123,
notification_type: "asset_shared",
email_enabled: true,
in_app_enabled: true,
created_at: "2024-01-15T10:00:00Z",
updated_at: "2024-01-20T14:30:00Z"
}Notification Badge Count Object
javascript
{
unread_count: 5,
system_notifications: 3,
announcements: 2,
last_checked: "2024-01-20T14:30:00Z"
}Component Implementation
Main Notification Component
File: components/theme/global/DamNotification.vue
Main notification bell component with system notifications and announcements tabs.
Features
- Notification bell icon with badge count
- System notifications tab
- Announcements tab
- Notification list display
- Mark as read functionality
- Mark all as read
- Notification actions
- Real-time updates
Props
javascript
{
workspaceId: {
type: [String, Number],
required: true
},
userId: {
type: [String, Number],
required: true
}
}Data
javascript
data() {
return {
dialog: false,
activeTab: 'system', // 'system' or 'announcements'
systemNotifications: [],
announcements: [],
unreadCount: 0,
loading: false
}
}Methods
javascript
// Load notifications
async loadNotifications() {
this.loading = true
try {
// Load system notifications
const systemResponse = await this.$axios.get(
'/digital-assets/notification/system-notification-list',
{ params: { workspace_id: this.workspaceId } }
)
this.systemNotifications = systemResponse.data.notifications
// Load announcements
const announcementsResponse = await this.$axios.get(
'/digital-assets/notification/announcement-list',
{ params: { workspace_id: this.workspaceId } }
)
this.announcements = announcementsResponse.data.announcements
// Update unread count
this.updateUnreadCount()
} catch (error) {
this.$toast.error('Failed to load notifications')
} finally {
this.loading = false
}
}
// Mark notification as read
async markAsRead(notificationId) {
try {
await this.$axios.post(
'/digital-assets/notification/mark-read',
{ notification_id: notificationId }
)
// Update notification in list
const notification = this.getNotificationById(notificationId)
if (notification) {
notification.is_read = true
notification.read_at = new Date().toISOString()
}
this.updateUnreadCount()
} catch (error) {
this.$toast.error('Failed to mark notification as read')
}
}
// Mark all as read
async markAllAsRead() {
try {
await this.$axios.post(
'/digital-assets/notification/mark-all-read',
{ workspace_id: this.workspaceId }
)
// Update all notifications
this.systemNotifications.forEach(n => {
n.is_read = true
n.read_at = new Date().toISOString()
})
this.announcements.forEach(n => {
n.is_read = true
n.read_at = new Date().toISOString()
})
this.updateUnreadCount()
} catch (error) {
this.$toast.error('Failed to mark all as read')
}
}
// Update unread count
updateUnreadCount() {
const systemUnread = this.systemNotifications.filter(n => !n.is_read).length
const announcementsUnread = this.announcements.filter(n => !n.is_read).length
this.unreadCount = systemUnread + announcementsUnread
// Update badge in store
this.$store.commit('dam/setNotificationBadge', this.unreadCount)
}
// Get notification by ID
getNotificationById(id) {
return [...this.systemNotifications, ...this.announcements]
.find(n => n.id === id)
}
// Handle notification action
handleNotificationAction(notification) {
if (notification.action_url) {
this.$router.push(notification.action_url)
this.dialog = false
}
}Usage
vue
<template>
<div>
<v-badge
:content="unreadCount"
:value="unreadCount > 0"
color="error"
overlap
>
<v-btn icon @click="dialog = true">
<v-icon>mdi-bell</v-icon>
</v-btn>
</v-badge>
<v-dialog v-model="dialog" max-width="500">
<v-card>
<v-card-title>
Notifications
<v-spacer />
<v-btn
v-if="unreadCount > 0"
text
small
@click="markAllAsRead"
>
Mark all as read
</v-btn>
</v-card-title>
<v-tabs v-model="activeTab">
<v-tab value="system">
System
<v-chip
v-if="systemUnreadCount > 0"
small
color="error"
class="ml-2"
>
{{ systemUnreadCount }}
</v-chip>
</v-tab>
<v-tab value="announcements">
Announcements
<v-chip
v-if="announcementsUnreadCount > 0"
small
color="error"
class="ml-2"
>
{{ announcementsUnreadCount }}
</v-chip>
</v-tab>
</v-tabs>
<v-tabs-items v-model="activeTab">
<v-tab-item value="system">
<v-list>
<v-list-item
v-for="notification in systemNotifications"
:key="notification.id"
:class="{ 'unread': !notification.is_read }"
@click="handleNotificationClick(notification)"
>
<v-list-item-content>
<v-list-item-title>{{ notification.title }}</v-list-item-title>
<v-list-item-subtitle>{{ notification.message }}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-btn
v-if="!notification.is_read"
icon
small
@click.stop="markAsRead(notification.id)"
>
<v-icon small>mdi-check</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
</v-list>
</v-tab-item>
<v-tab-item value="announcements">
<v-list>
<v-list-item
v-for="notification in announcements"
:key="notification.id"
:class="{ 'unread': !notification.is_read }"
@click="handleNotificationClick(notification)"
>
<v-list-item-content>
<v-list-item-title>{{ notification.title }}</v-list-item-title>
<v-list-item-subtitle>{{ notification.message }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-tab-item>
</v-tabs-items>
</v-card>
</v-dialog>
</div>
</template>Email Notifications Alert Component
File: components/dam/EmailNotificationsAlert.vue
Email notifications alert component.
Features
- Email verification status
- Email notification preferences alert
- Link to email settings
- Alert dismissal
Props
javascript
{
workspaceId: {
type: [String, Number],
required: true
},
userId: {
type: [String, Number],
required: true
}
}Methods
javascript
// Check email verification
async checkEmailVerification() {
const response = await this.$axios.get(
`/user/${this.userId}/email-verification-status`
)
return response.data.is_verified
}
// Dismiss alert
dismissAlert() {
localStorage.setItem('email_notification_alert_dismissed', 'true')
this.showAlert = false
}Notification Settings Component
File: components/dam/Profile/NotificationSetting.vue
User profile notification settings component.
Features
- User-level notification preferences
- Notification type toggles
- Email notification preferences
- In-app notification preferences
- Save preferences
Props
javascript
{
userId: {
type: [String, Number],
required: true
},
workspaceId: {
type: [String, Number],
required: true
}
}Data
javascript
data() {
return {
notificationSettings: [
{
type: 'asset_shared',
label: 'Asset Shared',
email_enabled: true,
in_app_enabled: true
},
{
type: 'asset_updated',
label: 'Asset Updated',
email_enabled: true,
in_app_enabled: true
},
{
type: 'comment_added',
label: 'Comment Added',
email_enabled: false,
in_app_enabled: true
}
// ... more notification types
]
}
}Methods
javascript
// Load notification settings
async loadSettings() {
const response = await this.$axios.get(
'/digital-assets/notification/user-notification-list',
{ params: { user_id: this.userId, workspace_id: this.workspaceId } }
)
this.notificationSettings = response.data.settings
}
// Update notification setting
async updateSetting(setting) {
try {
await this.$axios.post(
'/digital-assets/notification/update-user-notification',
{
user_id: this.userId,
workspace_id: this.workspaceId,
notification_type: setting.type,
email_enabled: setting.email_enabled,
in_app_enabled: setting.in_app_enabled
}
)
this.$toast.success('Notification settings updated')
} catch (error) {
this.$toast.error('Failed to update notification settings')
}
}
// Save all settings
async saveAllSettings() {
try {
const updates = this.notificationSettings.map(setting => ({
notification_type: setting.type,
email_enabled: setting.email_enabled,
in_app_enabled: setting.in_app_enabled
}))
await this.$axios.post(
'/digital-assets/notification/bulk-update-user-notifications',
{
user_id: this.userId,
workspace_id: this.workspaceId,
settings: updates
}
)
this.$toast.success('All notification settings saved')
} catch (error) {
this.$toast.error('Failed to save notification settings')
}
}Notification Store
File: store/dam.js
Notification state management in Vuex store.
State
javascript
state: {
notifications: {
system: [],
announcements: [],
unreadCount: 0,
lastChecked: null
},
notificationSettings: {
user: {},
organization: {}
},
badgeCount: 0
}Mutations
javascript
mutations: {
SET_NOTIFICATIONS(state, { type, notifications }) {
state.notifications[type] = notifications
state.notifications.unreadCount = this.getters.unreadCount
},
SET_NOTIFICATION_BADGE(state, count) {
state.badgeCount = count
state.notifications.unreadCount = count
},
MARK_NOTIFICATION_READ(state, notificationId) {
const notification = this.getters.getNotificationById(notificationId)
if (notification) {
notification.is_read = true
notification.read_at = new Date().toISOString()
state.notifications.unreadCount--
}
},
MARK_ALL_NOTIFICATIONS_READ(state) {
state.notifications.system.forEach(n => {
n.is_read = true
n.read_at = new Date().toISOString()
})
state.notifications.announcements.forEach(n => {
n.is_read = true
n.read_at = new Date().toISOString()
})
state.notifications.unreadCount = 0
},
SET_NOTIFICATION_SETTINGS(state, { type, settings }) {
state.notificationSettings[type] = settings
}
}Actions
javascript
actions: {
async fetchNotifications({ commit }, { workspaceId, type = 'system' }) {
try {
const response = await this.$axios.get(
`/digital-assets/notification/${type}-notification-list`,
{ params: { workspace_id: workspaceId } }
)
commit('SET_NOTIFICATIONS', {
type,
notifications: response.data.notifications
})
return response.data.notifications
} catch (error) {
console.error('Failed to fetch notifications', error)
throw error
}
},
async markNotificationAsRead({ commit }, notificationId) {
try {
await this.$axios.post(
'/digital-assets/notification/mark-read',
{ notification_id: notificationId }
)
commit('MARK_NOTIFICATION_READ', notificationId)
} catch (error) {
console.error('Failed to mark notification as read', error)
throw error
}
},
async markAllNotificationsAsRead({ commit }, workspaceId) {
try {
await this.$axios.post(
'/digital-assets/notification/mark-all-read',
{ workspace_id: workspaceId }
)
commit('MARK_ALL_NOTIFICATIONS_READ')
} catch (error) {
console.error('Failed to mark all notifications as read', error)
throw error
}
},
async fetchBadgeCount({ commit }, workspaceId) {
try {
const response = await this.$axios.get('/user-badges-count', {
params: { workspace_id: workspaceId }
})
commit('SET_NOTIFICATION_BADGE', response.data.unread_count)
return response.data.unread_count
} catch (error) {
console.error('Failed to fetch badge count', error)
throw error
}
}
}Getters
javascript
getters: {
unreadCount(state) {
const systemUnread = state.notifications.system.filter(n => !n.is_read).length
const announcementsUnread = state.notifications.announcements.filter(n => !n.is_read).length
return systemUnread + announcementsUnread
},
getNotificationById: (state) => (id) => {
return [...state.notifications.system, ...state.notifications.announcements]
.find(n => n.id === id)
},
systemNotifications(state) {
return state.notifications.system
},
announcements(state) {
return state.notifications.announcements
},
badgeCount(state) {
return state.badgeCount
}
}Notification Settings Page
File: pages/_workspace_id/workspace-settings/dam/_instance_id/notifications/index.vue
Route: /:workspace_id/workspace-settings/dam/:instance_id/notifications
Organization notification settings page (admin only).
Features
- Organization-level notification settings
- Email notification preferences
- Notification type management
- Admin-only access
- Settings save/update
Permission Check
javascript
async beforeRouteEnter(to, from, next) {
// Check if user can manage notifications
const canManage = await canManageNotifications()
if (!canManage) {
next({ name: 'forbidden' })
} else {
next()
}
}API Integration
Get System Notifications
Endpoint: GET /digital-assets/notification/system-notification-list
Query Parameters:
workspace_id(required) - Workspace identifier
Response:
json
{
"notifications": [
{
"id": 1,
"workspace_id": 123,
"type": "system",
"title": "System Maintenance",
"message": "Scheduled maintenance on January 20, 2024",
"priority": "high",
"is_read": false,
"created_at": "2024-01-15T10:00:00Z",
"read_at": null,
"action_url": "/workspace/123/settings",
"action_text": "View Settings"
}
]
}Get User Notification Settings
Endpoint: GET /digital-assets/notification/user-notification-list
Query Parameters:
user_id(required) - User identifierworkspace_id(required) - Workspace identifier
Response:
json
{
"settings": [
{
"id": 1,
"user_id": 456,
"workspace_id": 123,
"notification_type": "asset_shared",
"email_enabled": true,
"in_app_enabled": true,
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-20T14:30:00Z"
}
]
}Mark Notification as Read
Endpoint: POST /digital-assets/notification/mark-read
Request Body:
json
{
"notification_id": 1
}Response:
json
{
"success": true,
"message": "Notification marked as read"
}Mark All Notifications as Read
Endpoint: POST /digital-assets/notification/mark-all-read
Request Body:
json
{
"workspace_id": 123
}Response:
json
{
"success": true,
"message": "All notifications marked as read",
"marked_count": 5
}Get Notification Badge Count
Endpoint: GET /user-badges-count
Query Parameters:
workspace_id(required) - Workspace identifier
Response:
json
{
"unread_count": 5,
"system_notifications": 3,
"announcements": 2,
"last_checked": "2024-01-20T14:30:00Z"
}Update User Notification Preferences
Endpoint: POST /digital-assets/notification/update-user-notification
Request Body:
json
{
"user_id": 456,
"workspace_id": 123,
"notification_type": "asset_shared",
"email_enabled": true,
"in_app_enabled": true
}Response:
json
{
"success": true,
"setting": {
"id": 1,
"user_id": 456,
"workspace_id": 123,
"notification_type": "asset_shared",
"email_enabled": true,
"in_app_enabled": true,
"updated_at": "2024-01-20T14:30:00Z"
}
}Workflows
Display Notifications
1. User clicks notification bell
Component: DamNotification.vue
- Badge shows unread count
↓
2. Load notifications
Store Action: fetchNotifications()
API: GET /digital-assets/notification/system-notification-list
API: GET /digital-assets/notification/announcement-list
↓
3. Display notifications
- System notifications tab
- Announcements tab
- Unread notifications highlighted
↓
4. User interacts
- Click notification → Navigate to action URL
- Mark as read → Update notification
- Mark all as read → Update all notificationsMark Notification as Read
1. User clicks "Mark as read" on notification
Component: DamNotification.vue
↓
2. Call API
API: POST /digital-assets/notification/mark-read
Body: { notification_id: 1 }
↓
3. Update notification
Store Mutation: MARK_NOTIFICATION_READ
- Set is_read = true
- Set read_at timestamp
- Decrement unread count
↓
4. Update UI
- Remove unread styling
- Update badge count
- Refresh notification listUpdate Notification Settings
1. User navigates to notification settings
Route: /:workspace_id/workspace-settings/dam/:instance_id/notifications
OR
Route: /profile (user settings)
↓
2. Load current settings
API: GET /digital-assets/notification/user-notification-list
↓
3. User updates preferences
Component: NotificationSetting.vue
- Toggle email notifications
- Toggle in-app notifications
- Update per notification type
↓
4. Save settings
API: POST /digital-assets/notification/update-user-notification
Body: {
user_id, workspace_id, notification_type,
email_enabled, in_app_enabled
}
↓
5. Settings saved
- Update local state
- Show success message
- Refresh settings displayCheck Badge Count
1. Application loads or periodic check
Store Action: fetchBadgeCount()
↓
2. Call API
API: GET /user-badges-count
Query: { workspace_id: 123 }
↓
3. Update badge count
Store Mutation: SET_NOTIFICATION_BADGE
- Update badgeCount state
- Update unreadCount state
↓
4. Display badge
Component: DamNotification.vue
- Show badge with count
- Hide badge if count is 0Component Integration
Using Notifications in Header
vue
<template>
<div class="header">
<!-- Other header content -->
<DamNotification
:workspace-id="workspaceId"
:user-id="userId"
/>
</div>
</template>
<script>
import DamNotification from '~/components/theme/global/DamNotification.vue'
export default {
components: {
DamNotification
},
computed: {
workspaceId() {
return this.$route.params.workspace_id
},
userId() {
return this.$store.state.auth.user.id
}
}
}
</script>Using Notification Settings in Profile
vue
<template>
<div class="profile-page">
<NotificationSetting
:user-id="userId"
:workspace-id="workspaceId"
/>
</div>
</template>
<script>
import NotificationSetting from '~/components/dam/Profile/NotificationSetting.vue'
export default {
components: {
NotificationSetting
},
data() {
return {
userId: this.$store.state.auth.user.id,
workspaceId: this.$route.params.workspace_id
}
}
}
</script>Using Notification Store
vue
<script>
export default {
async mounted() {
// Fetch notifications
await this.$store.dispatch('dam/fetchNotifications', {
workspaceId: this.workspaceId,
type: 'system'
})
// Fetch badge count
await this.$store.dispatch('dam/fetchBadgeCount', this.workspaceId)
},
computed: {
unreadCount() {
return this.$store.getters['dam/unreadCount']
},
systemNotifications() {
return this.$store.getters['dam/systemNotifications']
}
},
methods: {
async markAsRead(notificationId) {
await this.$store.dispatch('dam/markNotificationAsRead', notificationId)
}
}
}
</script>Permission Management
Can Manage Notifications
File: plugins/helper.js
javascript
// Check if user can manage notifications
export function canManageNotifications() {
const user = this.$store.state.auth.user
const permissions = this.$store.state.auth.permissions
// Check for notification management permission
return permissions.includes('can-manage-notifications') ||
user.role === 'admin' ||
user.role === 'workspace_admin'
}Usage
vue
<script>
import { canManageNotifications } from '~/plugins/helper'
export default {
async beforeRouteEnter(to, from, next) {
const canManage = await canManageNotifications.call(this)
if (!canManage) {
next({ name: 'forbidden' })
} else {
next()
}
}
}
</script>Related Documentation
- Management Modules - Module access control
- Role-Based Access Matrix - Permission system
- DAM Store - State management