{"_id":"@danielbiegler/vendure-plugin-channel-notifications","name":"@danielbiegler/vendure-plugin-channel-notifications","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@danielbiegler/vendure-plugin-channel-notifications","description":"Foundation for building notification inboxes and or changelogs for your users. Features channel aware, translatable and customizable notifications with read-receipts per user.","version":"0.1.0","license":"MIT","scripts":{"dev":"ts-node dev-server/index.ts","c":"ts-node generate-types.ts","codegen":"ts-node generate-types.ts","build":"rimraf dist && tsc -p ./tsconfig.build.json","e2e":"cross-env PACKAGE=channel-notifications vitest -c ../../utils/e2e/vitest.config.mts"},"author":{"name":"Daniel Biegler","url":"https://www.danielbiegler.de"},"repository":{"type":"git","url":"git+https://github.com/DanielBiegler/bieglers-vendure-plugins.git"},"keywords":["vendure","plugin","vendure-plugin","ecommerce","headless","graphql","typescript","notification","notifications","inbox"],"private":false,"publishConfig":{"access":"public"},"main":"./dist/index.js","types":"./dist/index.d.ts","_id":"@danielbiegler/vendure-plugin-channel-notifications@0.1.0","gitHead":"1c929a235413c2283cf024d40d4c1b14310ccc52","bugs":{"url":"https://github.com/DanielBiegler/bieglers-vendure-plugins/issues"},"homepage":"https://github.com/DanielBiegler/bieglers-vendure-plugins#readme","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-NpgohlUDCdS7yBTGYHfJZnYQIwL0pdPca7iPIhkeSr/4g5m3+VGp8AkoESbmSxQsS40TOwqrG10hz9qHBYRl2Q==","shasum":"4b02b77e55537ff03abfc5ba8be40203e476469b","tarball":"https://registry.npmjs.org/@danielbiegler/vendure-plugin-channel-notifications/-/vendure-plugin-channel-notifications-0.1.0.tgz","fileCount":24,"unpackedSize":323524,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBbp9P+Eh7ljvAG4JaputNRz4a3H62LMNJTNCNihz4QGAiAhOX/zciIcqiOfoqnzZ2NnHTMUucFA5ZdG/sWksaVhFQ=="}]},"_npmUser":{"name":"danielbiegler","email":"danielbiegler.de@gmail.com"},"directories":{},"maintainers":[{"name":"danielbiegler","email":"danielbiegler.de@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vendure-plugin-channel-notifications_0.1.0_1757275296347_0.11393585724476396"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-07T20:01:36.264Z","0.1.0":"2025-09-07T20:01:36.542Z","modified":"2025-09-07T20:01:36.810Z"},"maintainers":[{"name":"danielbiegler","email":"danielbiegler.de@gmail.com"}],"description":"Foundation for building notification inboxes and or changelogs for your users. Features channel aware, translatable and customizable notifications with read-receipts per user.","homepage":"https://github.com/DanielBiegler/bieglers-vendure-plugins#readme","keywords":["vendure","plugin","vendure-plugin","ecommerce","headless","graphql","typescript","notification","notifications","inbox"],"repository":{"type":"git","url":"git+https://github.com/DanielBiegler/bieglers-vendure-plugins.git"},"author":{"name":"Daniel Biegler","url":"https://www.danielbiegler.de"},"bugs":{"url":"https://github.com/DanielBiegler/bieglers-vendure-plugins/issues"},"license":"MIT","readme":"![Banner Image](https://raw.githubusercontent.com/DanielBiegler/bieglers-vendure-plugins/master/packages/channel-notifications/assets/thumbnail_16x9.jpeg)\n\n# Vendure Plugin: Channel Notifications\n\nFoundation for building notification inboxes and or changelogs for your users. Features channel aware, translatable and customizable notifications with read-receipts per user.\n\n<a href=\"https://www.npmjs.com/package/@danielbiegler/vendure-plugin-channel-notifications\" target=\"_blank\">\n  <img src=\"https://badge.fury.io/js/@danielbiegler%2Fvendure-plugin-channel-notifications.svg\" alt=\"npm version badge\" height=\"18\">\n</a>\n\n## Features\n\n- Notification entity with read-receipts\n- Title and content are [translatable][translatable]\n- Notification-/ and Read-Receipt-Entities are extendable by you via [Custom Fields][customfields] to fit your specific business needs\n- Each [Channel][channels] can have their own inbox, because notifications implement [`ChannelAware`][channelaware]\n- Publishes [events][events] on the [EventBus][eventbus]\n- Granular CRUD permissions\n- Suite of end-to-end tests ensuring correctness\n\n### End-To-End Tests\n\n```\n ✓ channel-notifications/e2e/plugin.e2e-spec.ts (12 tests)\n\n Test Files  1 passed (1)\n      Tests  12 passed (12)\n```\n\n## How To: Usage\n\n> [!TIP]\n> This initial How-To Guide shows just the general usage to give you an overview of the plugin, for more details on how to customize notifications to fit your needs, check [\"Practical Guides\"](#practical-guides-and-resources) below.\n\nThe plugin [extends][extendapi] the admin API with queries and mutations:\n\n```graphql\nextend type Query {\n  \"Get a single notification\"\n  channelNotification(id: ID!): ChannelNotification\n  \"List all notifications for the active user, by default orders by dateTime descending\"\n  channelNotificationList(options: ChannelNotificationListOptions): ChannelNotificationList!\n}\n\nextend type Mutation {\n  CreateChannelNotification(input: CreateChannelNotificationInput!): ChannelNotification!\n  UpdateChannelNotification(input: UpdateChannelNotificationInput!): ChannelNotification!\n  DeleteChannelNotification(input: DeleteChannelNotificationInput!): DeletionResponse!\n  MarkChannelNotificationAsRead(input: MarkChannelNotificationAsReadInput!): Success!\n}\n```\n\nSee [api-extensions.ts](https://github.com/DanielBiegler/bieglers-vendure-plugins/blob/master/packages/channel-notifications/src/api/api-extensions.ts) for a complete overview of the graphql extensions and types.\n\n### 1. Add the plugin to your Vendure Config\n\nYou can find the package over on [npm](https://www.npmjs.com/package/@danielbiegler/vendure-plugin-channel-notifications) and install it via:\n\n```bash\nnpm i @danielbiegler/vendure-plugin-channel-notifications\n```\n\nAdd it to your [Vendure Config][configuration]:\n\n```ts\nimport { ChannelNotificationsPlugin } from \"@danielbiegler/vendure-plugin-channel-notifications\";\nexport const config: VendureConfig = {\n  // ...\n  plugins: [\n    ChannelNotificationsPlugin.init({}),\n  ],\n}\n```\n\nPlease refer to the specific [docs](https://github.com/DanielBiegler/bieglers-vendure-plugins/blob/master/packages/channel-notifications/src/types.ts) for how and what you can customize.\n\n### 2. Generate a database migration\n\nThis plugin adds new entities, namely:\n\n- `ChannelNotification`\n- `ChannelNotificationTranslation`\n- `ChannelNotificationReadReceipt`\n\nwhich requires you to generate a database migration. See Vendure's [migration documentation][migrations] for further guidance.\n\n### 3. Create Roles\n\nYou'll probably want to enable some users to create notifications while others are only given [read permissions][roles]. This plugin adds [custom permissions][custompermissions] which you can assign in Vendures settings.\n\n### 4. Include Channel-Token\n\nNotifications are [Channel-Aware][channelaware], meaning each channel has their own separate notifications. Given a multi-vendor setup where each vendor is their own Channel, each Vendor can be notified separately, simply by supplying the Channel-Token in the request header.\n\nA short example using ApolloClient in React:\n\n```ts\nconst { loading, error, data } = useQuery(GET_NOTIFICATION_LIST, {\n    context: {\n        headers: {\n            'vendure-token': 'my-example-channel-token',\n        },\n    },\n});\n```\n\nFor more details on how Channels work, see Vendures [Channel Documentation][channels].\n\n### 5. Create a notification\n\n```graphql\nmutation {\n  CreateChannelNotification(input: {\n    dateTime: \"2025-09-04T12:00:00Z\"\n    translations: [\n      {\n        languageCode: en,\n        title: \"My first notification\",\n        content: \"Hello world!\"\n      },\n      {\n        languageCode: de,\n        title: \"Meine erste Benachrichtigung\",\n        content: \"Hallo Welt!\"\n      }\n    ]\n  }) {\n    id\n  }\n}\n```\n\n### 6. Consume notifications\n\n1. List paginated notifications\n\n```graphql\nquery {\n  channelNotificationList(options: { take: 5 }) {\n    totalItems\n    items {\n      id\n      dateTime\n      title\n      content\n      readAt\n    }\n  }\n}\n```\n\n2. Mark them as read\n\n```graphql\nmutation {\n  MarkChannelNotificationAsRead(input: { id: \"1\" }) {\n    success\n  }\n}\n```\n\n## Practical Guides and Resources\n\n### Guides\n\nIt's important to note that this plugin aims to be **a foundation** for you to build upon. The default notification entity only holds the bare minimum of information and you are supposed to extend it via [custom fields][customfields] to fit your specific business need. Here are some examples:\n\n#### Example #1: Adding an Avatar and click action\n\nThink about how notifications on social media sites work: They often feature an image and you can click on them to get to the relevant event. You can extend the notification with an asset and text input like so:\n\n```ts\nconst vendureConfig = {\n  // ...\n  plugins: [\n    ChannelNotificationsPlugin.init({}),\n  ],\n  customFields: {\n    ChannelNotification: [\n      { name: \"asset\", type: \"relation\", entity: Asset, },\n      { name: \"urlAction\", type: \"string\", },\n    ],\n  },\n};\n```\n\nNow let's say someone reviewed your product and you'd like to notify the admins working that channel. Everytime a new review-event [gets published][eventbus] we can create notifications:\n\n```ts\nasync onApplicationBootstrap() {\n  this.eventBus.ofType(NewReviewEvent).subscribe(async event => {\n    // This is just an example, you should handle errors in prod\n    await this.channelNotificationService.create(event.ctx, {\n      dateTime: event.input.date,\n      customFields: {\n        assetId: event.input.product.featuredAssetId,\n        urlAction: `https://YOURBACKEND/dashboard/review/${event.input.review.id}`,\n      },\n      translations: [\n        {\n          languageCode: LanguageCode.en,\n          title: `A new review has been submitted for **\"${event.input.product.name}\"**`,\n          content: `${event.input.user.firstName}: \"${event.input.review.content.slice(0, 32)}...\"`,\n        },\n        // ...\n      ]\n    })\n  });\n}\n```\n\n#### Example #2: Adding a Notification Category\n\nDepending on your platform, a single inbox without the ability to filter notifications could overwhelm users, so you might want to add a notification-\"category\" to be able to group them:\n\n```ts\nconst vendureConfig = {\n  // ...\n  plugins: [\n    ChannelNotificationsPlugin.init({}),\n  ],\n  customFields: {\n    ChannelNotification: [\n      {\n        name: \"category\",\n        type: \"string\",\n        defaultValue: \"general\",\n        nullable: false,\n        validate: value => {\n          // This is just a simple example,\n          // in a real scenario you'd hopefully do this properly\n          if ([\"general\", \"review\", \"order\", /* ... */].includes(value)) return;\n\n          return [\n            { languageCode: LanguageCode.en, value: 'Invalid category' },\n            // ...\n          ];\n        }\n      },\n    ],\n  },\n};\n```\n\nSee about validation and other common props at [Custom Fields][customfields]\n\n#### Example #3: Customizing Read-Receipts\n\nLet's say you'd like your users to snooze a notification so that it pops up later again.\n\n```ts\nconst vendureConfig = {\n  // ...\n  plugins: [\n    ChannelNotificationsPlugin.init({}),\n  ],\n  customFields: {\n    ChannelNotificationReadReceipt: [\n      { name: \"renotifyAt\", type: \"datetime\", },\n    ],\n  },\n};\n```\n\nThen in your notification inbox add a button for snoozing and query like so:\n\n```ts\nconst dateAfter15Minutes = new Date(Date.now() + (1000 * 60 * 15));\nconst responseMark = await adminClient.query(MarkAsReadDocument, {\n  input: {\n    id: notification.id,\n    readReceiptCustomFields: { renotifyAt: dateAfter15Minutes }\n  }\n});\n```\n\nNow the notification will be marked as read and contain info for your backend to re-notify the user. In this example scenario, imagine having a [scheduled task][scheduledtasks] that regularly checks and updates read-receipts once enough time has passed.\n\n> [!IMPORTANT]\n> Users with read permissions can see their read-receipt, so keep that in mind when attaching data that's only intended for Superadmins for example! Vendures [custom fields][customfields] do allow restricting permissions on them via `requiresPermission`.\n\n### Resources\n\n- Free [Notification Inbox UI Component](https://flowbite.com/docs/components/dropdowns/#notification-bell) using TailwindCSS \n\n---\n\n#### Credits\n\n- [Banner Photos](https://github.com/DanielBiegler/bieglers-vendure-plugins/blob/master/packages/channel-notifications/assets/channel-notifications.psd) created and edited by [Daniel Biegler](https://www.danielbiegler.de/)\n\n<!-- Link references -->\n\n[customfields]: https://docs.vendure.io/guides/developer-guide/custom-fields/\n[channelaware]: https://docs.vendure.io/guides/developer-guide/channel-aware/\n[channels]: https://docs.vendure.io/guides/core-concepts/channels/\n[migrations]: https://docs.vendure.io/guides/developer-guide/migrations/\n[configuration]: https://docs.vendure.io/guides/developer-guide/configuration/\n[plugins]: https://docs.vendure.io/guides/developer-guide/plugins/\n[custompermissions]: https://docs.vendure.io/guides/developer-guide/custom-permissions/\n[translatable]: https://docs.vendure.io/guides/developer-guide/translatable/\n[events]: https://docs.vendure.io/guides/developer-guide/events/\n[eventbus]: https://docs.vendure.io/reference/typescript-api/events/event-bus/\n[roles]: https://docs.vendure.io/guides/core-concepts/auth/#roles--permissions\n[extendapi]: https://docs.vendure.io/guides/developer-guide/extend-graphql-api/\n[jobqueue]: https://docs.vendure.io/guides/developer-guide/worker-job-queue/\n[entity]: https://docs.vendure.io/guides/developer-guide/database-entity/\n[scheduledtasks]: https://docs.vendure.io/guides/developer-guide/scheduled-tasks/\n","readmeFilename":"README.md","_rev":"1-047638461400bc267dea9193e311e37c"}