Skip to content

Commit dadcba4

Browse files
committed
[Completion]: Added Notifiations & set the project to be production ready
1 parent 1abc07e commit dadcba4

File tree

11 files changed

+438
-23
lines changed

11 files changed

+438
-23
lines changed

.env.example

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ AUTH_SECRET="Zhssjsskskskksosososoososos"
33

44
AUTH_GOOGLE_ID=4638hshshssjhshhshshshhshshhshshhs.apps.googleusercontent.com
55
AUTH_GOOGLE_SECRET=GOCSsbsbhbsafsfsf1122457895412
6+
AUTH_URL=http://localhost:3000
67

78
NEXT_PUBLIC_KNOCK_PUBLIC_API_KEY=pk_test_Vsdgsdgdsgdsgsgsgsgdsgdsggd
89
NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID=8sdgsdgsdgdsgdsgdsgsgsgsgc8

app/(portal)/portal/messages/page.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Notifications from "@/components/common/Notifications";
2+
3+
4+
5+
export default function UserMessages() {
6+
return (
7+
<Notifications/>
8+
)
9+
}

app/(user)/user/messages/page.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Notifications from "@/components/common/Notifications";
2+
3+
export default function UserMessages() {
4+
return <Notifications />;
5+
}

app/Providers.tsx

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use client";
2+
3+
import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes";
4+
import { type ThemeProviderProps } from "next-themes/dist/types";
5+
import { Toaster } from "react-hot-toast";
6+
import { KnockProvider, KnockFeedProvider } from "@knocklabs/react";
7+
import "@knocklabs/react/dist/index.css";
8+
9+
type ProviderProps = {
10+
userId: string;
11+
};
12+
13+
14+
const Providers = ({
15+
children,
16+
userId,
17+
...props
18+
}: ThemeProviderProps & ProviderProps) => {
19+
return (
20+
<NextThemesProvider {...props}>
21+
<Toaster />
22+
<KnockProvider
23+
apiKey={process.env.NEXT_PUBLIC_KNOCK_PUBLIC_API_KEY as string}
24+
userId={userId}
25+
>
26+
<KnockFeedProvider
27+
feedId={process.env.NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID as string}
28+
>
29+
{children}
30+
</KnockFeedProvider>
31+
</KnockProvider>
32+
</NextThemesProvider>
33+
);
34+
};
35+
36+
export default Providers;

app/layout.tsx

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
11
import type { Metadata } from "next";
22
import "./globals.css";
33
import { Toaster } from "react-hot-toast";
4-
import ThemeProvider from "./theme-provider";
5-
6-
4+
import Providers from "./Providers";
5+
import { auth } from "@/auth";
76

87
export const metadata: Metadata = {
98
title: "Moto Moto",
109
description: " Driving lessons at your palm",
1110
};
1211

13-
export default function RootLayout({
12+
export default async function RootLayout({
1413
children,
1514
}: Readonly<{
1615
children: React.ReactNode;
1716
}>) {
17+
const session = await auth();
18+
const userId = session?.user?.id as string;
1819
return (
1920
<html lang="en">
20-
<body >
21-
<ThemeProvider
21+
<body>
22+
<Providers
2223
attribute="class"
2324
defaultTheme="system"
2425
enableSystem
2526
disableTransitionOnChange
27+
userId={userId}
2628
>
2729
<Toaster />
2830
{children}
29-
</ThemeProvider>
30-
31+
</Providers>
3132
</body>
3233
</html>
3334
);

app/pay/actions.ts

+49
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import { PaymentMethod } from "@prisma/client";
99

1010
import { redirect } from "next/navigation";
1111

12+
import { Knock } from "@knocklabs/node";
13+
14+
const knock = new Knock(process.env.KNOCK_SECRET_KEY);
15+
1216
//TODO: When paying with a card and calling an API, the paid value should be set to true upon success card payment
1317

1418
type AddBookingArgs = {
@@ -94,5 +98,50 @@ export async function addBookings({ bookings, payMethod }: AddBookingArgs) {
9498
data: modifiedBookings,
9599
});
96100

101+
// Notifications object
102+
const recipients: {
103+
id: string;
104+
name: string;
105+
email: string;
106+
}[] = [];
107+
108+
// Find instructors where instructor.id is found in instructorIds[]
109+
// then find users associtated with each instructor for you to be able to show notification to the user
110+
const instructors = await prisma.instructor.findMany({
111+
where: {
112+
id: { in: Array.from(instructorIds) }, // Convert Set to array for prisma query
113+
},
114+
include: {
115+
user: {
116+
select: {
117+
id: true,
118+
name: true,
119+
email: true,
120+
},
121+
},
122+
},
123+
});
124+
// Loop through each instructor and prepare recipient data
125+
for (const instructor of instructors) {
126+
recipients.push({
127+
id: instructor.user.id,
128+
name: instructor.user.name as string,
129+
email: instructor.user.email as string,
130+
});
131+
}
132+
133+
if (recipients.length > 0) {
134+
await knock.workflows.trigger("booking-placed", {
135+
actor: {
136+
id: userId,
137+
name: session?.user?.name ?? "Anonymous",
138+
email: session?.user?.email,
139+
collection: "users",
140+
},
141+
recipients,
142+
data: {},
143+
});
144+
}
145+
97146
redirect("/user/bookings");
98147
}

app/theme-provider.tsx

-11
This file was deleted.

components/common/Notifications.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"use client";
2+
3+
import { NotificationFeed } from "@knocklabs/react";
4+
5+
export default function Notifications() {
6+
return (
7+
<div className="mx-auto max-w-screen-xl px-4 py-8 lg:py-16">
8+
<NotificationFeed />
9+
</div>
10+
);
11+
}

components/common/NotifyBtn.tsx

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
"use client";
22

3-
import { IoMdNotificationsOutline } from "react-icons/io";
3+
import { useState, useRef } from "react";
4+
import {
5+
NotificationIconButton,
6+
NotificationFeedPopover,
7+
} from "@knocklabs/react";
48

59
export default function NotifyBtn() {
6-
10+
const [isVisible, setIsVisible] = useState(false);
11+
const notifButtonRef = useRef(null);
712

813
return (
914
<div>
10-
<IoMdNotificationsOutline size={24} />
15+
<NotificationIconButton
16+
ref={notifButtonRef}
17+
onClick={(e) => setIsVisible(!isVisible)}
18+
/>
19+
<NotificationFeedPopover
20+
buttonRef={notifButtonRef}
21+
isVisible={isVisible}
22+
onClose={() => setIsVisible(false)}
23+
/>
1124
</div>
1225
);
13-
}
26+
}

0 commit comments

Comments
 (0)