Customizing Notifications in VC-Shell¶
VC-Shell provides a flexible system for displaying notifications. While the useNotifications
composable handles the logic and data access for notifications, their visual appearance, especially in global components like notification-dropdown.vue
, is managed through a template registration mechanism. This allows developers to define custom Vue components to render different types of notifications.
Overview¶
When a notification is received (e.g., via SignalR or added manually), the system attempts to find a registered template that matches the notification's notifyType
. If a matching template is found, that Vue component is used to render the notification. If no specific template is registered for a given notifyType
, a default template is used.
This approach allows for: - Consistent branding and styling of notifications across the application. - Tailored display of information based on the notification type. - Rich interactive notifications, as the templates are Vue components.
How to Register a Custom Notification Template¶
Notification templates are Vue components that are registered with the VC-Shell framework during application setup. This is typically done in your application's main.ts
or a dedicated module initialization file.
1. Create Your Notification Template Component¶
First, create a Vue component that will render your custom notification. This component will receive the PushNotification
object as a prop and should use the base NotificationTemplate
for consistent styling and structure.
Example: ProductUpdateNotification.vue
(Using NotificationTemplate
as a base)
<template>
<NotificationTemplate
:title="computedTitle"
:notification="props.notification"
:icon="notificationIcon"
:color="notificationColor"
@click="handleNotificationClick"
>
<!-- Custom content for this notification type goes into the default slot -->
<p class="tw-text-sm tw-text-gray-700">{{ notification.message }}</p>
<img
v-if="notification.data?.imageUrl"
:src="notification.data.imageUrl"
alt="Product Image"
class="tw-mt-2 tw-max-w-full tw-h-auto tw-rounded"
/>
<VcButton
v-if="notification.data?.productId"
size="sm"
variant="outline"
class="tw-mt-2"
@click.stop="handleViewDetails"
>
View Product Details
</VcButton>
</NotificationTemplate>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
// Adjust paths as per your project structure
import { NotificationTemplate, type PushNotification, VcButton } from '@vc-shell/framework';
// import { useRouter } from 'vue-router'; // If navigation is needed
interface ProductNotificationData {
imageUrl?: string;
productId?: string;
productName?: string;
// Add other product-specific data fields if necessary
}
interface Props {
notification: PushNotification<ProductNotificationData>;
}
const props = defineProps<Props>();
// const router = useRouter(); // Uncomment if navigation is needed
const computedTitle = computed(() => {
// You can customize the title based on notification data
return props.notification.data?.productName
? `Update for ${props.notification.data.productName}`
: props.notification.title || 'Product Update';
});
const notificationIcon = computed(() => {
// Determine icon based on notification content or type
return 'material-shopping_bag'; // Example icon
});
const notificationColor = computed(() => {
// Determine color based on notification content or type
return 'var(--primary-500)'; // Example color
});
function handleNotificationClick() {
console.log('Notification clicked:', props.notification.id);
// Implement general click action for the notification if needed
// e.g., markAsRead(props.notification) can be called here or in a global handler
}
function handleViewDetails() {
if (props.notification.data?.productId) {
console.log('Navigating to product details:', props.notification.data.productId);
// Example navigation:
// router.push({ name: 'product-details', params: { id: props.notification.data.productId } });
}
}
// It's good practice to also define the notifyType if this component is meant
// to be automatically picked up by its type.
// This is usually done where the component is registered or if it's a self-registering module.
defineOptions({
inheritAttrs: false,
notifyType: "ProductUpdateNotificationEvent", // Example: This specific type will be used for registration
});
</script>
<style scoped>
/* Add any specific styles for your custom content here if needed */
/* The base NotificationTemplate already provides general styling. */
</style>
Key points for your template component:
- Import
NotificationTemplate
: And other necessary components likeVcButton
or types likePushNotification
from@vc-shell/framework
. - Use
NotificationTemplate
in your<template>
: This is the root or main wrapper. - Pass Props to
NotificationTemplate
:notification
: The raw notification object.title
: A computed title for the notification.icon
(optional): Name of the Material Design icon.color
(optional): A CSS color variable or hex code for emphasis (often used for the icon background or a side border, depending onNotificationTemplate
's internal styling).
- Use the Default Slot: Place your custom markup (text, images, buttons specific to this notification type) inside the
<NotificationTemplate>
tags. This content will be rendered in the designated area by the base template. - Handle Custom Actions: Implement methods for any interactive elements within your custom slot (e.g.,
handleViewDetails
). Use@click.stop
on interactive elements if you also have a main@click
onNotificationTemplate
to prevent event bubbling if necessary. defineOptions
: If this component is intended to be automatically associated with anotifyType
for registration, you can includenotifyType
indefineOptions
.- The
PushNotification
type can have a genericdata
property. Type this more specifically (e.g.,PushNotification<ProductNotificationData>
) if your notification type always includes certain data.
2. Register the Template¶
Notification templates are typically registered when a VC-Shell module is initialized using the createAppModule
function. This function accepts an argument for notificationTemplates
, where you provide an object mapping notifyType
strings to your custom template components.
Example: Registering in a module definition file (e.g., src/modules/your-module/index.ts
)
// src/modules/your-module/index.ts
import { createAppModule } from '@vc-shell/framework'; // Adjust path as necessary
// Import your custom notification template components
import ProductUpdateNotification from './components/notifications/ProductUpdateNotification.vue';
import AnotherCustomNotification from './components/notifications/AnotherCustomNotification.vue';
// Import pages, locales, other components for this module
// import MyPages from './pages';
// import myLocales from './locales';
const notificationTemplates = {
// The key here is NOT directly used for lookup;
// the notifyType defined within the component itself is crucial.
// However, it's good practice to use a descriptive key.
ProductUpdate: ProductUpdateNotification, // ProductUpdateNotification.vue should define options.notifyType = "ProductUpdateNotificationEvent"
NewOrder: AnotherCustomNotification, // AnotherCustomNotification.vue should define options.notifyType = "NewOrderReceivedEvent"
};
export default createAppModule(
{ /* your pages */ },
{ /* your locales */ },
notificationTemplates,
{ /* other module components */ }
);
Key points for registration:
createAppModule
: This is the standard way to package and register different parts of a VC-Shell module, including pages, locales, and notification templates.notificationTemplates
Object: You pass an object tocreateAppModule
where keys are descriptive names (for organization) and values are your imported Vue notification template components.notifyType
in Component: Crucially, each of your template components (e.g.,ProductUpdateNotification.vue
) must define itsnotifyType
viadefineOptions({ notifyType: "YourSpecificNotifyType" })
. ThecreateAppModule
function iterates through the provided templates and uses this embeddednotifyType
to populate an internalnotificationTemplatesMap
.- The
notificationTemplatesMap
(usually available viaapp.config.globalProperties.notificationTemplatesMap
) is then used by components likeNotification.vue
(internal toNotificationDropdown
) to find and render the correct template based on thenotifyType
of an incoming push notification.
Where to Register¶
-
Module Initialization File: This is the primary and recommended way. When you define a module (e.g., for Products, Orders, etc.), you include its specific notification templates as part of its definition using
createAppModule
. This keeps module-specific concerns encapsulated.// Example: apps/vendor-portal/src/modules/offers/index.ts import { createAppModule } from '@vc-shell/framework'; import OfferCreatedDomainEvent from './components/notifications/OfferCreatedDomainEvent.vue'; // ... other imports for pages, locales ... const notificationTemplates = { OfferCreated: OfferCreatedDomainEvent, // OfferCreatedDomainEvent.vue defines notifyType: "OfferCreatedDomainEvent" }; export default createAppModule( { /* offer pages */ }, { /* offer locales */ }, notificationTemplates );
-
Application Entry Point (
main.ts
): WhilecreateAppModule
is module-centric, global or truly shared notification templates could potentially be registered by ensuring they are part of anotificationTemplates
object that gets processed by the root application setup if it also uses a mechanism similar tocreateAppModule
for its core features, or by directly manipulatingapp.config.globalProperties.notificationTemplatesMap
during the initial app setup (though this is less common and more direct manipulation should be handled with care).The
framework/index.ts
or similar core framework initialization files are responsible for setting up the initialnotificationTemplatesMap
and potentially registering globally default templates.
Default Notification Template¶
VC-Shell usually provides a default notification template. This template is used if no custom template is registered for a specific notifyType
.
Best Practices¶
- Keep Templates Focused: Notification templates should primarily be concerned with the presentation of notification data. Complex logic related to what happens after a notification is interacted with (e.g., navigating, marking as read) might be handled by events emitted from the template or by the
useNotifications
handlers in other components. - Consistent
notifyType
: Ensure thenotifyType
string used for registration exactly matches thenotifyType
property of the incoming push notifications. - Performance: Keep notification templates lightweight. Avoid overly complex computations or heavy assets directly within the template if possible, especially if many notifications can be displayed at once.
- Modularity: Register templates within the relevant module or application scope to maintain separation of concerns.