021ee7d11e
This commit replaces the static notification dropdown in the header with a fully functional component that fetches live data from the API. Key features include: - Fetches and displays the latest notifications. - Shows a badge with the count of unseen notifications. - Opens a modal with notification details upon clicking an item. - Marks notifications as seen when they are viewed. Additionally, this commit includes several related improvements: - refactor: Replaced the global loading component with a custom Tailwind CSS spinner for a consistent look and feel. - feat: Enhanced the DatePicker component to optionally include a time picker. - style: Improved the layout of the notification details page by moving the message to a dedicated section and capitalizing the 'priority' and 'type' fields for better readability.
114 lines
4.1 KiB
TypeScript
114 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
Dropdown,
|
|
DropdownContent,
|
|
DropdownTrigger,
|
|
} from "@/components/ui/dropdown";
|
|
import Modal from "@/components/ui/modal";
|
|
import { useMarkNotificationAsSeen } from "@/hooks/queries/useNotificationQueries";
|
|
import { useIsMobile } from "@/hooks/use-mobile";
|
|
import { TNotificationDetailsResponse } from "@/lib/api/types/NotificationHistory"; // Import the type
|
|
import { cn } from "@/lib/utils";
|
|
import Link from "next/link";
|
|
import { useState } from "react";
|
|
import { BellIcon } from "./icons";
|
|
import NotificationList from "./notification-list";
|
|
|
|
export function Notification() {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [isDotVisible, setIsDotVisible] = useState(true);
|
|
const [unreadNotificationCount, setUnreadNotificationCount] =
|
|
useState<number>(0);
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [currentNotification, setCurrentNotification] =
|
|
useState<TNotificationDetailsResponse | null>(null);
|
|
|
|
const { mutate } = useMarkNotificationAsSeen(
|
|
currentNotification?.id ?? "",
|
|
() => setIsModalOpen(false),
|
|
);
|
|
const isMobile = useIsMobile();
|
|
|
|
const handleNotificationClick = (
|
|
notification: TNotificationDetailsResponse,
|
|
) => {
|
|
setCurrentNotification(notification);
|
|
setIsModalOpen(true);
|
|
setIsOpen(false);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Dropdown
|
|
isOpen={isOpen}
|
|
setIsOpen={(open) => {
|
|
setIsOpen(open);
|
|
if (setIsDotVisible) setIsDotVisible(false);
|
|
}}
|
|
>
|
|
<DropdownTrigger
|
|
className="grid size-12 place-items-center rounded-full border bg-gray-2 text-dark outline-none hover:text-primary focus-visible:border-primary focus-visible:text-primary dark:border-dark-4 dark:bg-dark-3 dark:text-white dark:focus-visible:border-primary"
|
|
aria-label="View Notifications"
|
|
>
|
|
<span className="relative">
|
|
<BellIcon />
|
|
{isDotVisible && (
|
|
<span
|
|
className={cn(
|
|
"bg-red-light absolute right-0 top-0 z-1 size-2 rounded-full ring-2 ring-gray-2 dark:ring-dark-3",
|
|
)}
|
|
>
|
|
<span className="bg-red-light absolute inset-0 -z-1 animate-ping rounded-full opacity-75" />
|
|
</span>
|
|
)}
|
|
</span>
|
|
</DropdownTrigger>
|
|
|
|
<DropdownContent
|
|
align={isMobile ? "end" : "center"}
|
|
className="border border-stroke bg-white px-3.5 py-3 shadow-md dark:border-dark-3 dark:bg-gray-dark min-[350px]:min-w-[20rem]"
|
|
>
|
|
<div className="mb-1 flex items-center justify-between px-2 py-1.5">
|
|
<span className="text-lg font-medium text-dark dark:text-white">
|
|
Notifications
|
|
</span>
|
|
<span className="rounded-md bg-primary px-[9px] py-0.5 text-xs font-medium text-white">
|
|
{unreadNotificationCount} new
|
|
</span>
|
|
</div>
|
|
|
|
<NotificationList
|
|
setUnreadNotificationCount={setUnreadNotificationCount}
|
|
onNotificationClick={handleNotificationClick}
|
|
/>
|
|
|
|
<Link
|
|
href="/notification-history"
|
|
onClick={() => setIsOpen(false)}
|
|
className="block rounded-lg border border-primary p-2 text-center text-sm font-medium tracking-wide text-primary outline-none transition-colors hover:bg-blue-light-5 focus:bg-blue-light-5 focus:text-primary focus-visible:border-primary dark:border-dark-3 dark:text-dark-6 dark:hover:border-dark-5 dark:hover:bg-dark-3 dark:hover:text-dark-7 dark:focus-visible:border-dark-5 dark:focus-visible:bg-dark-3 dark:focus-visible:text-dark-7"
|
|
>
|
|
See all notifications
|
|
</Link>
|
|
</DropdownContent>
|
|
</Dropdown>
|
|
|
|
<Modal
|
|
isOpen={isModalOpen}
|
|
onClose={() => {
|
|
setIsModalOpen(false);
|
|
}}
|
|
confirmText="Mark as read"
|
|
onConfirm={() => {
|
|
mutate();
|
|
}}
|
|
>
|
|
<div className="flex flex-col gap-3 max-w-29">
|
|
<h2 className="font-lg font-bold">{currentNotification?.title}</h2>
|
|
<p>{currentNotification?.message}</p>
|
|
</div>
|
|
</Modal>
|
|
</>
|
|
);
|
|
}
|