feat: add notification page

This commit is contained in:
Sosokker 2024-11-10 21:18:30 +07:00
parent a756e22cc8
commit 87e7947fcd
3 changed files with 143 additions and 34 deletions

View File

@ -1,46 +1,114 @@
"use client"; "use client";
import { useState } from "react";
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { BellIcon } from "lucide-react"; import { BellIcon } from "lucide-react";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { getNotificationByUserId } from "@/lib/data/norificationQuery";
import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation";
import useSession from "@/lib/supabase/useSession";
import { LegacyLoader } from "@/components/loading/LegacyLoader";
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(date);
};
export default function Notification() { export default function Notification() {
const sampleNotifications = [ const supabase = createSupabaseClient();
{ id: 1, message: "New message from John Doe", time: "5 minutes ago" }, const router = useRouter();
{ id: 2, message: "Your order has been shipped", time: "2 hours ago" }, const { session, loading } = useSession();
{ id: 3, message: "Meeting reminder: Team sync at 3 PM", time: "1 day ago" },
]; const [showUnreadOnly, setShowUnreadOnly] = useState(false);
const {
data: notifications,
error,
isLoading,
refetch,
} = useQuery(getNotificationByUserId(supabase, session?.user.id), { enabled: !!session });
if (loading) {
return <LegacyLoader />;
}
if (!session) {
return (
<div className="container max-w-screen-xl my-5">
<p className="text-red-600">Error fetching data. Please try again.</p>={" "}
<Button className="mt-4" onClick={() => router.refresh()}>
Refresh
</Button>
</div>
);
}
const filteredNotifications = showUnreadOnly
? notifications?.filter((notification) => !notification.is_read)
: notifications;
const markAsRead = async (id: number) => {
const { error } = await supabase.from("notification").update({ is_read: true }).eq("id", id);
if (!error) {
refetch();
}
};
if (isLoading) return <LegacyLoader />;
if (error) return <p>Error loading notifications: {error.message}</p>;
return ( return (
<div> <div className="container max-w-screen-xl my-4">
<div className="ml-24 md:ml-56 mt-16 "> <h1 className="text-3xl font-bold mb-6 border-b pb-2">Notifications</h1>
<h1 className="font-bold text-2xl md:text-3xl h-0">Notifications</h1>
<div className="w-full mt-20"> <div className="mb-4 flex justify-end">
{/* Cards */} <label className="text-sm mr-2 font-medium">Show Unread Only</label>
<Card className="border-slate-800 w-3/4 p-6"> <input
<CardContent> type="checkbox"
<Card> checked={showUnreadOnly}
<CardContent> onChange={() => setShowUnreadOnly((prev) => !prev)}
{sampleNotifications.map((notification) => ( className="cursor-pointer"
<div />
key={notification.id}
className="flex items-center justify-between p-4 border-b border-gray-200"
>
<div className="flex items-center">
<BellIcon className="w-5 h-5 text-blue-500 mr-3" />
<div>
<p className="text-sm font-medium">{notification.message}</p>
<p className="text-xs text-gray-500">{notification.time}</p>
</div>
</div>
<button className="text-sm text-blue-500 hover:text-blue-600">Mark as read</button>
</div>
))}
</CardContent>
</Card>
</CardContent>
</Card>
</div>
</div> </div>
<Card className="shadow-lg rounded-lg border border-gray-200">
<CardContent className="p-4 space-y-4">
{filteredNotifications?.map((notification) => (
<div
key={notification.id}
className={`flex items-center justify-between p-4 rounded-lg shadow-sm hover:shadow-md transition-shadow duration-150 border ${
notification.is_read ? "bg-gray-100 text-gray-400" : "bg-white text-gray-800"
}`}
>
<div className="flex items-center">
<BellIcon className={`w-5 h-5 mr-3 ${notification.is_read ? "text-gray-400" : "text-blue-500"}`} />
<div>
<p className="text-sm font-medium">{notification.message}</p>
<p className="text-xs text-gray-500">{formatDate(notification.created_at)}</p>
</div>
</div>
{!notification.is_read && (
<button
onClick={() => markAsRead(notification.id)}
className="text-xs text-blue-500 hover:text-blue-600 transition duration-150"
>
Mark as read
</button>
)}
</div>
))}
</CardContent>
</Card>
</div> </div>
); );
} }

View File

@ -0,0 +1,41 @@
import { SupabaseClient } from "@supabase/supabase-js";
interface NotificationData {
count: number;
status: number;
statusText: string;
}
export async function getUnreadNotificationCountByUserId(client: SupabaseClient, userId: string) {
const { data, error } = await client
.from("notification")
.select("*", { count: "exact", head: true })
.eq("receiver_id", userId)
.eq("is_read", false);
if (error) {
return { data: null, error: error };
}
if (data === null) {
return { data: null, error: error };
} else {
const notiData = data as unknown as NotificationData;
return { data: notiData, error: error };
}
}
export function getNotificationByUserId(client: SupabaseClient, userId: string | undefined) {
return client
.from("notification")
.select(
`
id,
receiver_id,
message,
created_at,
is_read
`
)
.eq("receiver_id", userId);
}

View File