Skip to content

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

  1. Notification Display: Main notification bell component with system and announcements tabs
  2. Notification Settings: User and organization-level notification preferences
  3. Email Notifications: Email notification alerts and preferences
  4. Notification Store: State management for notifications
  5. Notification Badges: Unread notification counts
  6. 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 plugin

    • canManageNotifications permission 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 identifier
  • workspace_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 notifications

Mark 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 list

Update 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 display

Check 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 0

Component 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>