feat(notifications): Implement notification details page and card
This commit introduces a dedicated page for viewing the full details of a notification and refactors the related UI components for better modularity and user experience. Key changes include: - Created a new reusable `NotificationDetailsCard` component to display notification details, decoupling the presentation logic from the page. - Refactored the notification details page (`/notification-history/[id]`) to use the new card component, resulting in cleaner code. - Made notification items in the header dropdown clickable, linking each to its respective details page. - Added a dedicated "Mark as Read" button to each notification item in the dropdown for improved usability. - Updated the application's global title metadata for branding consistency. - Added a specific page title for the Tenders page to improve SEO and navigation.
This commit is contained in:
@@ -103,7 +103,7 @@ export function Notification() {
|
||||
mutate();
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-3 max-w-29">
|
||||
<div className="flex flex-col gap-3 max-w-prose">
|
||||
<h2 className="font-lg font-bold">{currentNotification?.title}</h2>
|
||||
<p>{currentNotification?.message}</p>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ const NotificationList = ({
|
||||
setUnreadNotificationCount,
|
||||
onNotificationClick,
|
||||
}: IProps) => {
|
||||
const { data, isLoading } = useGetMyNotifications({ seen: false });
|
||||
const { data, isLoading } = useGetMyNotifications({seen:false});
|
||||
useEffect(() => {
|
||||
setUnreadNotificationCount(data?.data.length ?? 0);
|
||||
}, [data, isLoading]);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use client";
|
||||
import { EyeIcon } from "@/assets/icons";
|
||||
import { CheckIcon, CrossIcon, EyeIcon } from "@/assets/icons";
|
||||
import { ShowcaseSection } from "@/components/Layouts/showcase-section";
|
||||
import Pagination from "@/components/ui/pagination";
|
||||
import Status from "@/components/ui/Status";
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from "@/components/ui/table";
|
||||
import TableSkeleton from "@/components/ui/TableSkeleton";
|
||||
import { _TooltipDefaultParams } from "@/constants/tooltip";
|
||||
import { isoStringToDate } from "@/utils/shared";
|
||||
import { unixToDate } from "@/utils/shared";
|
||||
import Link from "next/link";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import useNotificationHistoryTablePresenter from "./useNotificationHistoryTablePresenter";
|
||||
@@ -59,9 +59,9 @@ const NotificationHistoryTable = () => {
|
||||
<TableCell colSpan={100}>{item.title}</TableCell>
|
||||
<TableCell colSpan={100}>{item.event_type}</TableCell>
|
||||
<TableCell colSpan={100}>
|
||||
{isoStringToDate({
|
||||
{unixToDate({
|
||||
hasTime: true,
|
||||
isoString: item.created_at,
|
||||
unix: item.created_at,
|
||||
})}
|
||||
</TableCell>
|
||||
<TableCell colSpan={100}>{item.message}</TableCell>
|
||||
@@ -72,7 +72,7 @@ const NotificationHistoryTable = () => {
|
||||
{item.type}
|
||||
</TableCell>
|
||||
<TableCell className="capitalize" colSpan={100}>
|
||||
{item.seen ? "Seen" : "Unseen"}
|
||||
{item.seen ? <CheckIcon fill="green"/> : <CrossIcon width={15} fill="red" />}
|
||||
</TableCell>
|
||||
<TableCell colSpan={100}>
|
||||
<Status status={item.status}>{item.status}</Status>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"use client";
|
||||
import { TNotificationDetailsResponse } from "@/lib/api/types/NotificationHistory";
|
||||
import { capitalize, unixToDate } from "@/utils/shared";
|
||||
import { FC } from "react";
|
||||
import IsVisible from "./IsVisible";
|
||||
import Status from "./Status";
|
||||
|
||||
interface IProps {
|
||||
data: TNotificationDetailsResponse;
|
||||
}
|
||||
|
||||
export const NotificationDetailsCard: FC<IProps> = ({ data }) => {
|
||||
return (
|
||||
<div className="rounded-2xl bg-white p-6 shadow-default dark:bg-gray-dark">
|
||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
<DetailItem title="Recipient" value={data.recipient?.full_name} />
|
||||
<DetailItem title="Event Type" value={data.event_type} />
|
||||
<DetailItem title="Link" value={data.link} />
|
||||
<DetailItem title="Priority">
|
||||
<Status status={data.priority ?? ""}>
|
||||
{capitalize(data.priority ?? "")}
|
||||
</Status>
|
||||
</DetailItem>
|
||||
<DetailItem title="Type">
|
||||
<Status status={data.type ?? ""}>
|
||||
{capitalize(data.type ?? "")}
|
||||
</Status>
|
||||
</DetailItem>
|
||||
<DetailItem
|
||||
title="Seen at"
|
||||
value={unixToDate({ unix: data.seen_at ?? 0, hasTime: false })}
|
||||
/>
|
||||
<DetailItem
|
||||
title="Is Scheduled"
|
||||
value={data.is_scheduled ? "Yes" : "No"}
|
||||
/>
|
||||
<DetailItem title="Has seen">
|
||||
<Status status={data.seen ? "success" : "failed"}>
|
||||
{data.seen ? "Yes" : "No"}
|
||||
</Status>
|
||||
</DetailItem>
|
||||
</div>
|
||||
<IsVisible condition={!!data.message}>
|
||||
<hr className="my-5" />
|
||||
<DetailItem title="Message">
|
||||
<p className="text-base leading-relaxed">{data.message}</p>
|
||||
</DetailItem>
|
||||
</IsVisible>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DetailItem: FC<{
|
||||
title: string;
|
||||
value?: string;
|
||||
children?: React.ReactNode;
|
||||
}> = ({ title, value, children }) => {
|
||||
return (
|
||||
<div className="rounded-lg bg-gray-2 p-4 shadow-sm dark:bg-gray-7">
|
||||
<small className="text-sm font-light text-black dark:text-white">
|
||||
{title}
|
||||
</small>
|
||||
{value ? <p className="mt-2 text-base font-normal">{value}</p> : null}
|
||||
{children ? <div className="mt-1">{children}</div> : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user