diff --git a/shell/components/HomePageDynamicContent.vue b/shell/components/HomePageDynamicContent.vue new file mode 100644 index 0000000000..e9eff8a054 --- /dev/null +++ b/shell/components/HomePageDynamicContent.vue @@ -0,0 +1,124 @@ + + + + diff --git a/shell/components/nav/NotificationCenter/index.vue b/shell/components/nav/NotificationCenter/index.vue index d36fc37556..8c3a80f228 100644 --- a/shell/components/nav/NotificationCenter/index.vue +++ b/shell/components/nav/NotificationCenter/index.vue @@ -11,9 +11,11 @@ import { RcDropdownSeparator, RcDropdownTrigger } from '@components/RcDropdown'; +import { NotificationLevel, Notification as NotificationType } from '@shell/types/notifications'; const store = useStore(); -const allNotifications = computed(() => store.getters['notifications/all']); +// We don't want any hidden notifications showing in the notification center (these are shown elsewhere, e.g. home page dynamic content announcements) +const allNotifications = computed(() => store.getters['notifications/all'].filter((n: NotificationType) => n.level !== NotificationLevel.Hidden)); const unreadLevelClass = computed(() => { return store.getters['notifications/unreadCount'] === 0 ? '' : 'unread'; }); diff --git a/shell/pages/home.vue b/shell/pages/home.vue index 920e929e88..395b6e8378 100644 --- a/shell/pages/home.vue +++ b/shell/pages/home.vue @@ -7,6 +7,7 @@ import PaginatedResourceTable from '@shell/components/PaginatedResourceTable.vue import { BadgeState } from '@components/BadgeState'; import CommunityLinks from '@shell/components/CommunityLinks.vue'; import SingleClusterInfo from '@shell/components/SingleClusterInfo.vue'; +import HomePageDynamicContent from '@shell/components/HomePageDynamicContent.vue'; import { mapGetters, mapState } from 'vuex'; import { MANAGEMENT, CAPI, COUNT } from '@shell/config/types'; import { NAME as MANAGER } from '@shell/config/product/manager'; @@ -47,6 +48,7 @@ export default defineComponent({ SingleClusterInfo, TabTitle, ResourceTable, + HomePageDynamicContent, }, mixins: [PageHeaderActions, Preset], @@ -610,6 +612,7 @@ export default defineComponent({ pref-key="welcomeBanner" data-testid="home-banner-graphic" /> +
diff --git a/shell/store/notifications.ts b/shell/store/notifications.ts index ac29c2d3ed..b0fb196f87 100644 --- a/shell/store/notifications.ts +++ b/shell/store/notifications.ts @@ -66,7 +66,8 @@ async function saveEncryptedNotification(getters: any, notification: Notificatio primaryAction: notification.primaryAction, secondaryAction: notification.secondaryAction, preference: notification.preference, - handlerName: notification.handlerName + handlerName: notification.handlerName, + data: notification.data, }; const localStorageKey = getters['localStorageKey']; diff --git a/shell/types/notifications/index.ts b/shell/types/notifications/index.ts index 257114f89f..29db0ffd45 100644 --- a/shell/types/notifications/index.ts +++ b/shell/types/notifications/index.ts @@ -14,6 +14,7 @@ export enum NotificationLevel { Success, // eslint-disable-line no-unused-vars Warning, // eslint-disable-line no-unused-vars Error, // eslint-disable-line no-unused-vars + Hidden, // eslint-disable-line no-unused-vars } /** @@ -52,6 +53,8 @@ export type EncryptedNotification = { // Handler to be associated with this notification that can invoke additional behaviour when the notification changes // This is the name of the handler (the handlers are added as extensions). Notifications are persisted in the store, so can't use functions. handlerName?: string; + // Additional data to be stored with the notification (optional) + data?: any; }; /** diff --git a/shell/utils/dynamic-content/announcement.ts b/shell/utils/dynamic-content/announcement.ts index 51e1b993d6..0198429af1 100644 --- a/shell/utils/dynamic-content/announcement.ts +++ b/shell/utils/dynamic-content/announcement.ts @@ -15,10 +15,15 @@ import { DynamicContentAnnouncementHandlerName } from './notification-handler'; // Prefixes used in the notifications IDs created here export const ANNOUNCEMENT_PREFIX = 'announcement-'; +const TARGET_NOTIFICATION_CENTER = 'notification'; +const TARGET_HOME_PAGE = 'homepage'; +const ALLOWED_TARGETS = [TARGET_NOTIFICATION_CENTER, TARGET_HOME_PAGE]; + const ALLOWED_NOTIFICATIONS: Record = { announcement: NotificationLevel.Announcement, info: NotificationLevel.Info, warning: NotificationLevel.Warning, + homepage: NotificationLevel.Hidden, }; /** @@ -49,6 +54,7 @@ export async function processAnnouncements(context: Context, announcements: Anno // Check type const targetSplit = announcement.target.split('/'); + const target = targetSplit[0]; if (targetSplit[0] === 'notification') { // Show a notification @@ -85,6 +91,7 @@ export async function processAnnouncements(context: Context, announcements: Anno title: announcement.title, message: announcement.message, handlerName: DynamicContentAnnouncementHandlerName, + data, }; if (announcement.cta?.primary) { @@ -101,7 +108,7 @@ export async function processAnnouncements(context: Context, announcements: Anno }; } - logger.info(`Adding announcement with ID ${ id } (title: ${ announcement.title })`); + logger.info(`Adding announcement with ID ${ id } (title: ${ announcement.title }, target: ${ announcement.target })`); await dispatch('notifications/add', notification); } diff --git a/shell/utils/dynamic-content/types.d.ts b/shell/utils/dynamic-content/types.d.ts index 4ae4471e55..5f806019b8 100644 --- a/shell/utils/dynamic-content/types.d.ts +++ b/shell/utils/dynamic-content/types.d.ts @@ -108,12 +108,18 @@ export type Announcement = { target: string; // Where the announcement should be shown version?: string; // Version or semver expression for when to show this announcement audience?: 'admin' | 'all'; // Audience - show for just Admins or for all users + icon?: string; cta?: { primary: CallToAction, // Must have a primary call to action, if we have a cta field secondary?: CallToAction, } }; +export type AnnouncementNotificationData = { + icon?: string; + location: string; +}; + /** * Main type for the metadata that is retrieved from the dynamic content endpoint */