Appearance
Announcements
Overview
The Announcements System provides workspace-level announcement management for administrators. It allows creating, editing, and managing announcements that are displayed to users through the notification system. Announcements support rich content, targeting, read/unread tracking, and can be displayed in the notification bell component.
Architecture
System Components
- Announcement Management: Create, edit, delete announcements (admin only)
- Announcement Display: Show announcements in notification bell and dedicated pages
- Announcement Table: List and manage announcements with sorting, filtering, and pagination
- Read/Unread Tracking: Track which users have read announcements
- Permission Management: Access control for announcement management
File Structure
Core Announcement Components
Vue Component Files (.vue)
components/dam/Dialogs/Org-Settings/AnnouncementDialog.vue- Announcement dialog component- Create new announcement
- Edit existing announcement
- View announcement details
- Rich content editor
- Targeting options
- Publish/unpublish toggle
components/dam/table/announcementTable.vue- Announcement table component- Display announcements list
- Sorting functionality
- Filtering options
- Pagination
- Bulk actions
- Edit/delete actions
Announcement Display & Integration
Vue Component Files (.vue)
components/theme/global/DamNotification.vue- Main notification component- Includes announcements tab
- Display announcements in notification bell
- Mark as read functionality
- Announcement actions
components/dam/Collage/AvatarList.vue- Avatar list component- May display announcement publisher avatars
- User avatars for announcement creators
Announcement Management Pages
Vue Page Files (.vue)
pages/_workspace_id/workspace-settings/dam/_instance_id/announcements/list.vue- Announcements list and management page- Admin-only access
- Announcements list display
- Create/edit announcements
- Delete announcements
- Filter and search
- Pagination
Route: /:workspace_id/workspace-settings/dam/:instance_id/announcements/list
Announcement Integration in Navigation
Vue Component Files (.vue)
components/theme/global/LeftMenu.vue- Left menu component- May include announcements access
- Navigation link to announcements
components/theme/settings/AccountSettingsLeftMenu.vue- Account settings menu- May include announcements link
- Navigation to announcements management
Supporting Files
JavaScript Files (.js)
plugins/helper.js- Helper functions plugincanViewAnnouncementspermission checkcanManageAnnouncementspermission check- Announcement utility functions
middleware/can-access-dam-module.js- DAM module access middleware- May check announcement permissions
- Access control for announcement features
Announcement Structure
Announcement Object
javascript
{
id: 1,
workspace_id: 123,
instance_id: 456,
title: "New Feature Release",
content: "We're excited to announce new features...",
content_type: "html", // "text" or "html"
priority: "high", // "low", "medium", "high"
target_audience: "all", // "all", "specific_roles", "specific_users"
target_roles: [], // Array of role IDs if target_audience is "specific_roles"
target_users: [], // Array of user IDs if target_audience is "specific_users"
is_published: true,
publish_at: "2024-01-15T10:00:00Z",
expires_at: null, // Optional expiration date
created_by: 789,
created_at: "2024-01-15T10:00:00Z",
updated_at: "2024-01-20T14:30:00Z",
read_count: 45,
total_users: 100
}Announcement Read Status Object
javascript
{
id: 1,
announcement_id: 1,
user_id: 456,
is_read: true,
read_at: "2024-01-16T09:00:00Z",
created_at: "2024-01-15T10:00:00Z"
}Component Implementation
Announcement Dialog Component
File: components/dam/Dialogs/Org-Settings/AnnouncementDialog.vue
Dialog for creating, editing, and viewing announcements.
Features
- Create new announcement
- Edit existing announcement
- View announcement details
- Rich content editor (HTML/text)
- Title and content fields
- Priority selection
- Target audience selection
- Publish/unpublish toggle
- Publish date/time
- Expiration date (optional)
Props
javascript
{
dialog: {
type: Boolean,
default: false
},
announcement: {
type: Object,
default: null
// Announcement object to edit (null for create)
},
workspaceId: {
type: [String, Number],
required: true
},
instanceId: {
type: [String, Number],
required: true
}
}Data
javascript
data() {
return {
form: {
title: '',
content: '',
content_type: 'text', // 'text' or 'html'
priority: 'medium', // 'low', 'medium', 'high'
target_audience: 'all', // 'all', 'specific_roles', 'specific_users'
target_roles: [],
target_users: [],
is_published: false,
publish_at: null,
expires_at: null
},
contentEditor: null,
loading: false
}
}Methods
javascript
// Load announcement data for editing
loadAnnouncement() {
if (this.announcement) {
this.form = {
title: this.announcement.title,
content: this.announcement.content,
content_type: this.announcement.content_type || 'text',
priority: this.announcement.priority || 'medium',
target_audience: this.announcement.target_audience || 'all',
target_roles: this.announcement.target_roles || [],
target_users: this.announcement.target_users || [],
is_published: this.announcement.is_published || false,
publish_at: this.announcement.publish_at,
expires_at: this.announcement.expires_at
}
}
}
// Validate form
validateForm() {
if (!this.form.title.trim()) {
this.$toast.error('Title is required')
return false
}
if (!this.form.content.trim()) {
this.$toast.error('Content is required')
return false
}
if (this.form.target_audience === 'specific_roles' && this.form.target_roles.length === 0) {
this.$toast.error('Please select at least one role')
return false
}
if (this.form.target_audience === 'specific_users' && this.form.target_users.length === 0) {
this.$toast.error('Please select at least one user')
return false
}
return true
}
// Save announcement
async saveAnnouncement() {
if (!this.validateForm()) {
return
}
this.loading = true
try {
const payload = {
workspace_id: this.workspaceId,
instance_id: this.instanceId,
...this.form
}
if (this.announcement) {
// Update existing
const response = await this.$axios.put(
`/announcement/update`,
{ id: this.announcement.id, ...payload }
)
this.$toast.success('Announcement updated successfully')
this.$emit('saved', response.data.announcement)
} else {
// Create new
const response = await this.$axios.post(
`/announcement/add`,
payload
)
this.$toast.success('Announcement created successfully')
this.$emit('saved', response.data.announcement)
}
this.closeDialog()
} catch (error) {
this.$toast.error('Failed to save announcement')
} finally {
this.loading = false
}
}
// Close dialog
closeDialog() {
this.$emit('close')
this.resetForm()
}
// Reset form
resetForm() {
this.form = {
title: '',
content: '',
content_type: 'text',
priority: 'medium',
target_audience: 'all',
target_roles: [],
target_users: [],
is_published: false,
publish_at: null,
expires_at: null
}
}Usage
vue
<template>
<div>
<v-btn @click="openCreateDialog">Create Announcement</v-btn>
<AnnouncementDialog
:dialog="dialogOpen"
:announcement="selectedAnnouncement"
:workspace-id="workspaceId"
:instance-id="instanceId"
@close="closeDialog"
@saved="handleAnnouncementSaved"
/>
</div>
</template>
<script>
import AnnouncementDialog from '~/components/dam/Dialogs/Org-Settings/AnnouncementDialog.vue'
export default {
components: {
AnnouncementDialog
},
data() {
return {
dialogOpen: false,
selectedAnnouncement: null,
workspaceId: this.$route.params.workspace_id,
instanceId: this.$route.params.instance_id
}
},
methods: {
openCreateDialog() {
this.selectedAnnouncement = null
this.dialogOpen = true
},
editAnnouncement(announcement) {
this.selectedAnnouncement = announcement
this.dialogOpen = true
},
closeDialog() {
this.dialogOpen = false
this.selectedAnnouncement = null
},
handleAnnouncementSaved(announcement) {
// Refresh announcements list
this.loadAnnouncements()
}
}
}
</script>Announcement Table Component
File: components/dam/table/announcementTable.vue
Table component for displaying and managing announcements list.
Features
- Display announcements in table format
- Sorting by column (title, priority, created_at, etc.)
- Filtering options
- Search functionality
- Pagination
- Bulk actions (delete, publish/unpublish)
- Edit/delete actions per row
- Read statistics display
Props
javascript
{
announcements: {
type: Array,
required: true
// Array of announcement objects
},
loading: {
type: Boolean,
default: false
},
total: {
type: Number,
default: 0
},
page: {
type: Number,
default: 1
},
perPage: {
type: Number,
default: 20
}
}Methods
javascript
// Sort announcements
sortBy(column) {
// Toggle sort direction
// Update sort state
// Emit sort event
}
// Filter announcements
applyFilter(filter) {
// Apply filter
// Emit filter event
}
// Search announcements
searchAnnouncements(query) {
// Search in title and content
// Emit search event
}
// Delete announcement
async deleteAnnouncement(announcementId) {
// Confirm deletion
// Call delete API
// Remove from list
// Show success message
}
// Toggle publish status
async togglePublish(announcement) {
// Toggle is_published
// Call update API
// Update local state
}Usage
vue
<template>
<div>
<AnnouncementTable
:announcements="announcements"
:loading="loading"
:total="total"
:page="currentPage"
:per-page="perPage"
@sort="handleSort"
@filter="handleFilter"
@search="handleSearch"
@edit="editAnnouncement"
@delete="deleteAnnouncement"
@page-change="handlePageChange"
/>
</div>
</template>
<script>
import AnnouncementTable from '~/components/dam/table/announcementTable.vue'
export default {
components: {
AnnouncementTable
},
data() {
return {
announcements: [],
loading: false,
total: 0,
currentPage: 1,
perPage: 20
}
},
methods: {
handleSort(sortData) {
// Handle sorting
this.loadAnnouncements(sortData)
},
handleFilter(filter) {
// Handle filtering
this.loadAnnouncements({ filter })
},
handleSearch(query) {
// Handle search
this.loadAnnouncements({ search: query })
},
editAnnouncement(announcement) {
// Open edit dialog
},
deleteAnnouncement(announcementId) {
// Delete announcement
}
}
}
</script>Announcements List Page
File: pages/_workspace_id/workspace-settings/dam/_instance_id/announcements/list.vue
Route: /:workspace_id/workspace-settings/dam/:instance_id/announcements/list
Main announcements management page (admin only).
Features
- Announcements list display
- Create new announcement
- Edit existing announcement
- Delete announcement
- Filter and search
- Sorting
- Pagination
- Bulk actions
Page Structure
vue
<template>
<div class="announcements-page">
<v-card>
<v-card-title>
Announcements
<v-spacer />
<v-btn
v-if="canManageAnnouncements"
color="primary"
@click="openCreateDialog"
>
<AddIcon /> Create Announcement
</v-btn>
</v-card-title>
<v-card-text>
<!-- Search and Filters -->
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="searchQuery"
placeholder="Search announcements..."
prepend-inner-icon="mdi-magnify"
@input="handleSearch"
/>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="selectedPriority"
:items="priorityOptions"
label="Filter by Priority"
clearable
@change="handleFilter"
/>
</v-col>
</v-row>
<!-- Announcements Table -->
<AnnouncementTable
:announcements="announcements"
:loading="loading"
:total="total"
:page="currentPage"
:per-page="perPage"
@sort="handleSort"
@filter="handleFilter"
@search="handleSearch"
@edit="editAnnouncement"
@delete="deleteAnnouncement"
@page-change="handlePageChange"
/>
</v-card-text>
</v-card>
<!-- Create/Edit Dialog -->
<AnnouncementDialog
:dialog="dialogOpen"
:announcement="selectedAnnouncement"
:workspace-id="workspaceId"
:instance-id="instanceId"
@close="closeDialog"
@saved="handleAnnouncementSaved"
/>
</div>
</template>Announcement Display in Notifications
File: components/theme/global/DamNotification.vue
Announcements are displayed in the notification bell component.
Features
- Announcements tab in notification bell
- Display published announcements
- Mark as read functionality
- Filter by read/unread
- Click to view full announcement
Integration
vue
<template>
<v-tabs v-model="activeTab">
<v-tab value="system">System</v-tab>
<v-tab value="announcements">
Announcements
<v-chip
v-if="unreadAnnouncementsCount > 0"
small
color="error"
class="ml-2"
>
{{ unreadAnnouncementsCount }}
</v-chip>
</v-tab>
</v-tabs>
<v-tabs-items v-model="activeTab">
<v-tab-item value="announcements">
<v-list>
<v-list-item
v-for="announcement in announcements"
:key="announcement.id"
:class="{ 'unread': !announcement.is_read }"
@click="viewAnnouncement(announcement)"
>
<v-list-item-content>
<v-list-item-title>{{ announcement.title }}</v-list-item-title>
<v-list-item-subtitle>
{{ formatDate(announcement.created_at) }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-btn
v-if="!announcement.is_read"
icon
small
@click.stop="markAsRead(announcement.id)"
>
<v-icon small>mdi-check</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
</v-list>
</v-tab-item>
</v-tabs-items>
</template>API Integration
Get Announcements List
Endpoint: GET /announcement/list
Query Parameters:
workspace_id(required) - Workspace identifierinstance_id(required) - Instance identifierpage(optional) - Page number (default: 1)per_page(optional) - Items per page (default: 20)sort_by(optional) - Sort column (default: "created_at")sort_order(optional) - Sort order "asc" or "desc" (default: "desc")search(optional) - Search querypriority(optional) - Filter by priorityis_published(optional) - Filter by published status
Response:
json
{
"announcements": [
{
"id": 1,
"workspace_id": 123,
"instance_id": 456,
"title": "New Feature Release",
"content": "We're excited to announce new features...",
"content_type": "html",
"priority": "high",
"target_audience": "all",
"target_roles": [],
"target_users": [],
"is_published": true,
"publish_at": "2024-01-15T10:00:00Z",
"expires_at": null,
"created_by": 789,
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-20T14:30:00Z",
"read_count": 45,
"total_users": 100
}
],
"pagination": {
"current_page": 1,
"per_page": 20,
"total": 15,
"total_pages": 1
}
}Create Announcement
Endpoint: POST /announcement/add
Request Body:
json
{
"workspace_id": 123,
"instance_id": 456,
"title": "New Feature Release",
"content": "We're excited to announce new features...",
"content_type": "html",
"priority": "high",
"target_audience": "all",
"target_roles": [],
"target_users": [],
"is_published": true,
"publish_at": "2024-01-15T10:00:00Z",
"expires_at": null
}Response:
json
{
"success": true,
"announcement": {
"id": 1,
"workspace_id": 123,
"instance_id": 456,
"title": "New Feature Release",
"content": "We're excited to announce new features...",
"content_type": "html",
"priority": "high",
"target_audience": "all",
"is_published": true,
"publish_at": "2024-01-15T10:00:00Z",
"created_by": 789,
"created_at": "2024-01-15T10:00:00Z"
}
}Update Announcement
Endpoint: PUT /announcement/update
Request Body:
json
{
"id": 1,
"title": "Updated Feature Release",
"content": "Updated content...",
"content_type": "html",
"priority": "medium",
"is_published": true
}Response:
json
{
"success": true,
"announcement": {
"id": 1,
"title": "Updated Feature Release",
"content": "Updated content...",
"updated_at": "2024-01-20T14:30:00Z"
}
}Delete Announcement
Endpoint: DELETE /announcement/delete
Query Parameters:
id(required) - Announcement ID
Response:
json
{
"success": true,
"message": "Announcement deleted successfully"
}Get Announcements for Notifications
Endpoint: GET /announcement/notification-list
Query Parameters:
workspace_id(required) - Workspace identifieruser_id(required) - User identifier
Response:
json
{
"announcements": [
{
"id": 1,
"title": "New Feature Release",
"content": "We're excited to announce new features...",
"priority": "high",
"is_read": false,
"created_at": "2024-01-15T10:00:00Z"
}
],
"unread_count": 3
}Mark Announcement as Read/Unread
Endpoint: POST /announcement/read-unread
Request Body:
json
{
"announcement_id": 1,
"user_id": 456,
"is_read": true
}Response:
json
{
"success": true,
"message": "Announcement marked as read"
}Mark All Announcements as Read
Endpoint: POST /announcement/mark-all-read
Request Body:
json
{
"workspace_id": 123,
"user_id": 456
}Response:
json
{
"success": true,
"message": "All announcements marked as read",
"marked_count": 5
}Workflows
Create Announcement
1. Admin navigates to announcements page
Route: /:workspace_id/workspace-settings/dam/:instance_id/announcements/list
↓
2. Click "Create Announcement"
Component: AnnouncementDialog.vue opens
↓
3. Fill announcement form
- Enter title
- Enter content (text or HTML)
- Select priority
- Select target audience
- Set publish date (optional)
- Set expiration date (optional)
↓
4. Validate form
- Check required fields
- Validate target audience selection
↓
5. Save announcement
API: POST /announcement/add
Body: { workspace_id, instance_id, title, content, ... }
↓
6. Announcement created
- Saved to database
- If published, visible to users
- Appears in announcements list
↓
7. Update UI
- Refresh announcements list
- Show success message
- Close dialogDisplay Announcements to Users
1. User clicks notification bell
Component: DamNotification.vue
↓
2. Switch to "Announcements" tab
- Shows announcements tab
- Displays unread count badge
↓
3. Load announcements
API: GET /announcement/notification-list
Query: { workspace_id, user_id }
↓
4. Display announcements
- Show published announcements
- Filter by target audience
- Highlight unread announcements
- Show priority indicators
↓
5. User interacts
- Click announcement → View full content
- Mark as read → Update read status
- Mark all as read → Update all read statusesMark Announcement as Read
1. User views announcement
Component: DamNotification.vue
- Click announcement in list
OR
- Click "Mark as read" button
↓
2. Call API
API: POST /announcement/read-unread
Body: { announcement_id, user_id, is_read: true }
↓
3. Update read status
- Set is_read = true
- Set read_at timestamp
- Decrement unread count
↓
4. Update UI
- Remove unread styling
- Update badge count
- Refresh announcement listEdit Announcement
1. Admin navigates to announcements list
Route: /:workspace_id/workspace-settings/dam/:instance_id/announcements/list
↓
2. Click "Edit" on announcement
Component: AnnouncementDialog.vue opens with announcement data
↓
3. Modify announcement
- Update title, content, priority, etc.
- Change target audience
- Toggle publish status
↓
4. Save changes
API: PUT /announcement/update
Body: { id, title, content, ... }
↓
5. Announcement updated
- Changes saved to database
- If published, updates visible to users
↓
6. Update UI
- Refresh announcements list
- Show success message
- Close dialogDelete Announcement
1. Admin navigates to announcements list
Route: /:workspace_id/workspace-settings/dam/:instance_id/announcements/list
↓
2. Click "Delete" on announcement
- Confirm deletion dialog appears
↓
3. Confirm deletion
- User confirms deletion
↓
4. Call API
API: DELETE /announcement/delete
Query: { id: announcementId }
↓
5. Announcement deleted
- Removed from database
- No longer visible to users
↓
6. Update UI
- Remove from announcements list
- Show success message
- Refresh listComponent Integration
Using Announcements in Notifications
vue
<template>
<DamNotification
:workspace-id="workspaceId"
:user-id="userId"
/>
</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 Announcements List Page
vue
<template>
<div class="announcements-list-page">
<AnnouncementTable
:announcements="announcements"
:loading="loading"
:total="total"
:page="currentPage"
@edit="editAnnouncement"
@delete="deleteAnnouncement"
/>
<AnnouncementDialog
:dialog="dialogOpen"
:announcement="selectedAnnouncement"
:workspace-id="workspaceId"
:instance-id="instanceId"
@close="closeDialog"
@saved="handleSaved"
/>
</div>
</template>
<script>
import AnnouncementTable from '~/components/dam/table/announcementTable.vue'
import AnnouncementDialog from '~/components/dam/Dialogs/Org-Settings/AnnouncementDialog.vue'
export default {
components: {
AnnouncementTable,
AnnouncementDialog
},
data() {
return {
announcements: [],
loading: false,
total: 0,
currentPage: 1,
dialogOpen: false,
selectedAnnouncement: null,
workspaceId: this.$route.params.workspace_id,
instanceId: this.$route.params.instance_id
}
},
async mounted() {
await this.loadAnnouncements()
},
methods: {
async loadAnnouncements() {
this.loading = true
try {
const response = await this.$axios.get('/announcement/list', {
params: {
workspace_id: this.workspaceId,
instance_id: this.instanceId,
page: this.currentPage,
per_page: 20
}
})
this.announcements = response.data.announcements
this.total = response.data.pagination.total
} catch (error) {
this.$toast.error('Failed to load announcements')
} finally {
this.loading = false
}
},
editAnnouncement(announcement) {
this.selectedAnnouncement = announcement
this.dialogOpen = true
},
deleteAnnouncement(announcementId) {
// Confirm and delete
},
closeDialog() {
this.dialogOpen = false
this.selectedAnnouncement = null
},
handleSaved() {
this.loadAnnouncements()
}
}
}
</script>Permission Management
Can View Announcements
File: plugins/helper.js
javascript
// Check if user can view announcements
export function canViewAnnouncements() {
const user = this.$store.state.auth.user
const permissions = this.$store.state.auth.permissions
// All authenticated users can view announcements
return user && user.id
}Can Manage Announcements
File: plugins/helper.js
javascript
// Check if user can manage announcements
export function canManageAnnouncements() {
const user = this.$store.state.auth.user
const permissions = this.$store.state.auth.permissions
// Check for announcement management permission
return permissions.includes('can-manage-announcements') ||
user.role === 'admin' ||
user.role === 'workspace_admin'
}Usage
vue
<script>
import { canManageAnnouncements } from '~/plugins/helper'
export default {
async beforeRouteEnter(to, from, next) {
const canManage = await canManageAnnouncements.call(this)
if (!canManage) {
next({ name: 'forbidden' })
} else {
next()
}
},
computed: {
canManage() {
return canManageAnnouncements.call(this)
}
}
}
</script>Related Documentation
- Notifications System - Notification system integration
- Management Modules - Module access control
- Role-Based Access Matrix - Permission system