diff --git a/packages/novu/src/commands/init/templates/app-react-email/ts/app/api/events/route.ts b/packages/novu/src/commands/init/templates/app-react-email/ts/app/api/events/route.ts new file mode 100644 index 00000000000..9bc06354d62 --- /dev/null +++ b/packages/novu/src/commands/init/templates/app-react-email/ts/app/api/events/route.ts @@ -0,0 +1,31 @@ +export async function POST(request: Request) { + try { + const body = await request.json(); + + const response = await fetch("https://api.novu.co/v1/telemetry/measure", { + headers: { + Accept: "application/json", + Authorization: `ApiKey ${process.env.NOVU_SECRET_KEY}`, + }, + method: "POST", + body: JSON.stringify({ + event: body.event, + data: body.data, + }), + }); + + if (response.ok) { + return Response.json({ success: true }); + } + + return Response.json({ + connected: false, + error: await response.text(), + }); + } catch (error) { + return Response.json({ + connected: false, + error: error instanceof Error ? error.message : "Unknown error", + }); + } +} diff --git a/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NotificationToast/Notifications.module.css b/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NotificationToast/Notifications.module.css new file mode 100644 index 00000000000..f35c3d40d74 --- /dev/null +++ b/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NotificationToast/Notifications.module.css @@ -0,0 +1,80 @@ +.toast { + position: fixed; + background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%); + border-radius: 16px; + padding: 18px 24px; + box-shadow: + 0 10px 25px rgba(0, 0, 0, 0.1), + 0 6px 12px rgba(0, 0, 0, 0.08), + 0 0 0 1px rgba(255, 255, 255, 0.5) inset; + z-index: 1000; + width: 90%; + max-width: 400px; + right: 24px; + top: 24px; + border: 1px solid rgba(0, 0, 0, 0.06); + animation: slideIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); + backdrop-filter: blur(10px); + transform-origin: top right; +} + +.toastContent { + display: flex; + flex-direction: column; + gap: 10px; + position: relative; + overflow: hidden; + font-weight: 600; + background: linear-gradient(90deg, #1a1a1a 0%, #404040 100%); + -webkit-background-clip: text; + color: transparent; + font-size: 1rem; + letter-spacing: -0.02em; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.toastContent::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient(45deg, + transparent 0%, + rgba(255, 255, 255, 0.1) 50%, + transparent 100%); + animation: shimmer 2s infinite; +} + +@keyframes slideIn { + 0% { + transform: translateY(-120%) scale(0.9); + opacity: 0; + } + + 100% { + transform: translateY(0) scale(1); + opacity: 1; + } +} + +@keyframes shimmer { + 0% { + transform: translateX(-100%) rotate(45deg); + } + + 100% { + transform: translateX(100%) rotate(45deg); + } +} + +@media (prefers-reduced-motion: reduce) { + .toast { + animation: none; + } + + .toastContent::before { + animation: none; + } +} \ No newline at end of file diff --git a/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NotificationToast/Notifications.tsx b/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NotificationToast/Notifications.tsx new file mode 100644 index 00000000000..10dbdc4e37a --- /dev/null +++ b/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NotificationToast/Notifications.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { Novu } from "@novu/js"; +import { useEffect, useState } from "react"; +import styles from "./Notifications.module.css"; // You'll need to create this +import { Inbox } from "@novu/nextjs"; + +const NotificationToast = () => { + const novu = new Novu({ + subscriberId: process.env.NEXT_PUBLIC_NOVU_SUBSCRIBER_ID || "", + applicationIdentifier: + process.env.NEXT_PUBLIC_NOVU_APPLICATION_IDENTIFIER || "", + }); + + const [showToast, setShowToast] = useState(false); + + useEffect(() => { + const listener = ({ result: notification }: { result: any }) => { + console.log("Received notification:", notification); + setShowToast(true); + + setTimeout(() => { + setShowToast(false); + }, 2500); + }; + + console.log("Setting up Novu notification listener"); + novu.on("notifications.notification_received", listener); + + return () => { + novu.off("notifications.notification_received", listener); + }; + }, [novu]); + + if (!showToast) return null; + + return ( +
+
New In-App Notification
+
+ ); +}; + +export default NotificationToast; + +const novuConfig = { + applicationIdentifier: + process.env.NEXT_PUBLIC_NOVU_APPLICATION_IDENTIFIER || "", + subscriberId: process.env.NEXT_PUBLIC_NOVU_SUBSCRIBER_ID || "", + appearance: { + elements: { + bellContainer: { + width: "30px", + height: "30px", + }, + bellIcon: { + width: "30px", + height: "30px", + }, + }, + }, +}; + +export function NovuInbox() { + return ; +} diff --git a/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NovuInbox.tsx b/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NovuInbox.tsx deleted file mode 100644 index 4537eb263cc..00000000000 --- a/packages/novu/src/commands/init/templates/app-react-email/ts/app/components/NovuInbox.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Inbox } from "@novu/nextjs"; - -const novuConfig = { - applicationIdentifier: - process.env.NEXT_PUBLIC_NOVU_APPLICATION_IDENTIFIER || "", - subscriberId: process.env.NEXT_PUBLIC_NOVU_SUBSCRIBER_ID || "", - appearance: { - elements: { - bellContainer: { - width: "30px", - height: "30px", - }, - bellIcon: { - width: "30px", - height: "30px", - }, - }, - }, -}; - -export function NovuInbox() { - return ; -} diff --git a/packages/novu/src/commands/init/templates/app-react-email/ts/app/page.module.css b/packages/novu/src/commands/init/templates/app-react-email/ts/app/page.module.css index d4e9b47de2b..631465cd91f 100644 --- a/packages/novu/src/commands/init/templates/app-react-email/ts/app/page.module.css +++ b/packages/novu/src/commands/init/templates/app-react-email/ts/app/page.module.css @@ -187,6 +187,10 @@ margin-bottom: 1rem; } +.description a { + color: #0081f1; +} + .divider { width: 1px; background: #e2e8f0; @@ -415,4 +419,19 @@ color: #4f46e5; border: 1px solid rgba(224, 231, 255, 0.6); font-weight: 500; +} + +.successMessage { + color: #16a34a; + margin-top: 8px; + margin-left: 5rem; + text-align: center; + font-size: 0.9rem; +} + +.complianceBadges { + display: flex; + gap: 1rem; + align-items: center; + margin-top: 0.5rem; } \ No newline at end of file diff --git a/packages/novu/src/commands/init/templates/app-react-email/ts/app/page.tsx b/packages/novu/src/commands/init/templates/app-react-email/ts/app/page.tsx index 3f3ba6676f8..d93d5718b3c 100644 --- a/packages/novu/src/commands/init/templates/app-react-email/ts/app/page.tsx +++ b/packages/novu/src/commands/init/templates/app-react-email/ts/app/page.tsx @@ -2,11 +2,26 @@ import { useState, useEffect } from "react"; import Image from "next/image"; -import { NovuInbox } from "./components/NovuInbox"; import styles from "./page.module.css"; +import NotificationToast, { + NovuInbox, +} from "./components/NotificationToast/Notifications"; export default function Home() { const [isNovuConnected, setIsNovuConnected] = useState(false); + const [showSuccess, setShowSuccess] = useState(false); + + useEffect(() => { + (async () => { + await fetch("/api/events", { + method: "POST", + body: JSON.stringify({ + event: "Starter Page Visit - [Next.js Starter]", + data: {}, + }), + }); + })(); + }, []); useEffect(() => { const checkNovuConnection = async () => { @@ -42,6 +57,16 @@ export default function Home() { const data = await response.json(); console.log("Notification triggered:", data); + setShowSuccess(true); + setTimeout(() => setShowSuccess(false), 3000); // Hide after 3 seconds + + await fetch("/api/events", { + method: "POST", + body: JSON.stringify({ + event: "Notification Triggered - [Next.js Starter]", + data: {}, + }), + }); } catch (error) { console.error("Error triggering notification:", error); } @@ -49,6 +74,7 @@ export default function Home() { return (
+
{/* Header */} @@ -59,23 +85,22 @@ export default function Home() {
- {/* Content */}
{/* Info Section */}
+ {/* Create a workflow */}
Create a workflow

- A workflow acts as the blueprint for the notifications that - will be sent. This is where all the different channels, - filters, rules and actions are tied together under a single - entity. + In Novu, all notifications are sent via a workflow. Each + workflow acts as a container for the logic and templates + that are associated with a kind of notification in your + system.

-
- + {/* Add Inbox to your app */}
- Add Inbox to your app + Add In-App notifications

@@ -148,8 +174,23 @@ export default function Home() {

                     {``}
                   
+
+

+ Check out the{" "} + + Inbox Playground + + . You can customize the Inbox component to match your + application's design. +

+
Learn more about Inbox @@ -166,7 +207,7 @@ export default function Home() {
- + {/* Digest multiple notifications */}
Digest multiple notifications @@ -186,6 +227,7 @@ export default function Home() {
Learn more about Digest @@ -202,7 +244,7 @@ export default function Home() {
- + {/* Schedule / Delay notifications */}
Schedule / Delay notifications @@ -236,6 +278,7 @@ export default function Home() { Learn more about Delay @@ -252,15 +295,72 @@ export default function Home() {
+ {/* Preferences */} +
+ + Preferences + +
+

+ Novu provides a way to store subscriber preferences. This + allows subscribers, your users, to specify and manage their + preferences and customize their notifications experience. +

+ +

Levels of preferences:

+
    +
  • +
    - Workflow channel + preferences +
  • +
  • +
    - Subscriber channel + preferences per workflow +
  • +
  • +
    - Subscriber global + preferences +
  • +
+ + + Learn more about Preferences + + + + +
+
{isNovuConnected ? ( - + <> + + {showSuccess && ( +

+ ✓ Notification triggered successfully! +

+ )} + ) : (
@@ -348,6 +448,22 @@ export default function Home() { Contact +
  • + + Roadmap + +
  • +
  • + + Changelog + +