diff --git a/src/app/(notifications)/notification-history/[id]/page.tsx b/src/app/(notifications)/notification-history/[id]/page.tsx
index 425234d..3f01c84 100644
--- a/src/app/(notifications)/notification-history/[id]/page.tsx
+++ b/src/app/(notifications)/notification-history/[id]/page.tsx
@@ -7,6 +7,7 @@ import { useGetNotificationDetails } from "@/hooks/queries/useNotificationQuerie
import { BreadcrumbItem } from "@/types/shared";
import { unixToDate } from "@/utils/shared";
import { use } from "react";
+import { capitalize } from "../../../../utils/shared";
interface IProps {
params: Promise<{ id: string }>;
@@ -36,11 +37,6 @@ const NotificationDetailsPage = ({ params }: IProps) => {
-
-
- {data?.data.message}
-
-
{data?.data.event_type}
@@ -52,12 +48,14 @@ const NotificationDetailsPage = ({ params }: IProps) => {
- {data?.data.priority}
+ {capitalize(data?.data.priority ?? "")}
- {data?.data.type}
+
+ {capitalize(data?.data.type ?? "")}
+
@@ -72,6 +70,12 @@ const NotificationDetailsPage = ({ params }: IProps) => {
{data?.data.seen ? "Yes" : "No"}
+
+
+
+ {data?.data.message}
+
+
>
);
diff --git a/src/app/loading.tsx b/src/app/loading.tsx
index 2be5c92..7979c4a 100644
--- a/src/app/loading.tsx
+++ b/src/app/loading.tsx
@@ -3,10 +3,11 @@ import { cn } from "../lib/utils";
interface IProps {
className?: string | null;
}
+
const Loading = ({ className = "w-svw h-svh" }: IProps) => {
return (
);
};
diff --git a/src/components/FormElements/DatePicker/DatePicker.tsx b/src/components/FormElements/DatePicker/DatePicker.tsx
index 6449149..8b32c3a 100644
--- a/src/components/FormElements/DatePicker/DatePicker.tsx
+++ b/src/components/FormElements/DatePicker/DatePicker.tsx
@@ -93,7 +93,7 @@ const DatePicker = ({
onChange(date.isValid ? date.toUnix() : "");
}
}}
- format={"YYYY/MM/DD"}
+ format={`YYYY/MM/DD ${timePicker ? "HH:mm" : ""}`}
containerClassName="w-full"
inputClass={cn(
"w-full rounded-lg border-[1.5px] border-stroke bg-transparent px-5.5 py-3 pl-12.5 text-dark outline-none transition focus:border-primary disabled:cursor-default disabled:bg-gray-2 dark:border-dark-3 dark:bg-dark-2 dark:text-white dark:focus:border-primary dark:disabled:bg-dark",
diff --git a/src/components/Layouts/header/notification/index.tsx b/src/components/Layouts/header/notification/index.tsx
index 3cc5167..0223677 100644
--- a/src/components/Layouts/header/notification/index.tsx
+++ b/src/components/Layouts/header/notification/index.tsx
@@ -5,125 +5,109 @@ import {
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 Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { BellIcon } from "./icons";
-
-const notificationList = [
- {
- image: "/images/user/user-15.png",
- title: "Piter Joined the Team!",
- subTitle: "Congratulate him",
- },
- {
- image: "/images/user/user-03.png",
- title: "New message",
- subTitle: "Devid sent a new message",
- },
- {
- image: "/images/user/user-26.png",
- title: "New Payment received",
- subTitle: "Check your earnings",
- },
- {
- image: "/images/user/user-28.png",
- title: "Jolly completed tasks",
- subTitle: "Assign new task",
- },
- {
- image: "/images/user/user-27.png",
- title: "Roman Joined the Team!",
- subTitle: "Congratulate him",
- },
-];
+import NotificationList from "./notification-list";
export function Notification() {
const [isOpen, setIsOpen] = useState(false);
const [isDotVisible, setIsDotVisible] = useState(true);
+ const [unreadNotificationCount, setUnreadNotificationCount] =
+ useState(0);
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [currentNotification, setCurrentNotification] =
+ useState(null);
+
+ const { mutate } = useMarkNotificationAsSeen(
+ currentNotification?.id ?? "",
+ () => setIsModalOpen(false),
+ );
const isMobile = useIsMobile();
+ const handleNotificationClick = (
+ notification: TNotificationDetailsResponse,
+ ) => {
+ setCurrentNotification(notification);
+ setIsModalOpen(true);
+ setIsOpen(false);
+ };
+
return (
- {
- setIsOpen(open);
-
- if (setIsDotVisible) setIsDotVisible(false);
- }}
- >
-
+ {
+ setIsOpen(open);
+ if (setIsDotVisible) setIsDotVisible(false);
+ }}
>
-
-
-
- {isDotVisible && (
-
-
-
- )}
-
-
-
-
-
-
- Notifications
-
-
- 5 new
-
-
-
-
-
- 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
-
-
-
+
+
+ {isDotVisible && (
+
+
+
+ )}
+
+
+
+
+
+
+ Notifications
+
+
+ {unreadNotificationCount} new
+
+
+
+
+
+ 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
+
+
+
+
+ {
+ setIsModalOpen(false);
+ }}
+ confirmText="Mark as read"
+ onConfirm={() => {
+ mutate();
+ }}
+ >
+
+
{currentNotification?.title}
+
{currentNotification?.message}
+
+
+ >
);
}
diff --git a/src/components/Layouts/header/notification/notification-list.tsx b/src/components/Layouts/header/notification/notification-list.tsx
new file mode 100644
index 0000000..9f3fb8e
--- /dev/null
+++ b/src/components/Layouts/header/notification/notification-list.tsx
@@ -0,0 +1,51 @@
+"use client";
+
+import Loading from "@/app/loading";
+import IsVisible from "@/components/ui/IsVisible";
+import { useGetMyNotifications } from "@/hooks/queries/useNotificationQueries";
+import { TNotificationDetailsResponse } from "@/lib/api/types/NotificationHistory";
+import { truncateString } from "@/utils/shared";
+import { Dispatch, SetStateAction, useEffect } from "react";
+
+interface IProps {
+ setUnreadNotificationCount: Dispatch>;
+ onNotificationClick: (notification: TNotificationDetailsResponse) => void;
+}
+
+const NotificationList = ({
+ setUnreadNotificationCount,
+ onNotificationClick,
+}: IProps) => {
+ const { data, isLoading } = useGetMyNotifications({ seen: false });
+ useEffect(() => {
+ setUnreadNotificationCount(data?.data.length ?? 0);
+ }, [data, isLoading]);
+
+ if (isLoading) return ;
+
+ return (
+
+ );
+};
+
+export default NotificationList;
diff --git a/src/components/forms/notifications/CreateNotification.tsx b/src/components/forms/notifications/CreateNotification.tsx
index fa2c8fe..37c9ff2 100644
--- a/src/components/forms/notifications/CreateNotification.tsx
+++ b/src/components/forms/notifications/CreateNotification.tsx
@@ -51,6 +51,7 @@ const CreateNotificationForm = () => {
name="schedule_at"
label="schedule at"
range={false}
+ timePicker
placeholder="Select schedule date"
/>
@@ -100,7 +101,7 @@ const CreateNotificationForm = () => {
{...register("link", {
pattern: {
value: REGEX.URL,
- message: FormErrorMessages.invalidPattern,
+ message: FormErrorMessages.invalidPattern,
},
})}
name="link"
@@ -152,7 +153,13 @@ const CreateNotificationForm = () => {
void;
onConfirm?: () => void;
children: ReactNode;
+ confirmText?: string;
+ cancelText?: string;
}
const Modal: React.FC = ({
@@ -15,6 +17,8 @@ const Modal: React.FC = ({
onClose,
children,
onConfirm,
+ confirmText = "confirm",
+ cancelText = "close",
}) => {
const [mounted, setMounted] = useState(false);
const isMutating = useIsMutating();
@@ -55,21 +59,21 @@ const Modal: React.FC = ({
diff --git a/src/hooks/queries/useNotificationQueries.ts b/src/hooks/queries/useNotificationQueries.ts
index 23152ac..c0c0896 100644
--- a/src/hooks/queries/useNotificationQueries.ts
+++ b/src/hooks/queries/useNotificationQueries.ts
@@ -16,6 +16,38 @@ export const useGetNotificationHistoryQuery = (
queryFn: () => notificationService.getNotifications(params),
});
};
+export const useGetMyNotifications = (params?: Record) => {
+ const queryKey = useMemo(
+ () => [API_ENDPOINTS.NOTIFICATIONS.MY_NOTIFICATIONS, params],
+ [params],
+ );
+ return useQuery({
+ queryKey,
+ queryFn: () => notificationService.getMyNotifications(params),
+ });
+};
+export const useMarkNotificationAsSeen = (
+ id: string,
+ successCallback?: () => void,
+) => {
+ const queryClient = useQueryClient();
+ const mutationKey = useMemo(
+ () => [API_ENDPOINTS.NOTIFICATIONS.MARK_AS_SEEN(id), id],
+ [id],
+ );
+ return useMutation({
+ mutationKey,
+ mutationFn: () => notificationService.markNotificationAsSeen(id),
+ onSuccess: () => {
+ if (successCallback) {
+ successCallback();
+ }
+ queryClient.invalidateQueries({
+ queryKey: [API_ENDPOINTS.NOTIFICATIONS.MY_NOTIFICATIONS],
+ });
+ },
+ });
+};
export const useGetNotificationDetails = (id: string) => {
const queryKey = useMemo(
() => [API_ENDPOINTS.NOTIFICATIONS.DETAILS(id), "NOTIFICATION DETAILS"],
@@ -40,6 +72,9 @@ export const useCreateNotificationQuery = (successCallback?: () => void) => {
queryClient.invalidateQueries({
queryKey: [API_ENDPOINTS.NOTIFICATIONS.HISTORY],
});
+ queryClient.invalidateQueries({
+ queryKey: [API_ENDPOINTS.NOTIFICATIONS.MY_NOTIFICATIONS],
+ });
if (successCallback) {
successCallback();
}
diff --git a/src/lib/api/endpoints.ts b/src/lib/api/endpoints.ts
index 8a1c352..9d38536 100644
--- a/src/lib/api/endpoints.ts
+++ b/src/lib/api/endpoints.ts
@@ -54,5 +54,7 @@ export const API_ENDPOINTS = {
HISTORY: "notifications",
CREATE: "notifications",
DETAILS: (id: string) => `notifications/view/${id}`,
+ MY_NOTIFICATIONS: "notifications/my",
+ MARK_AS_SEEN: (id: string) => `notifications/mark-seen/${id}`,
},
} as const;
diff --git a/src/lib/api/services/notification-service.ts b/src/lib/api/services/notification-service.ts
index 33518fd..ab452e5 100644
--- a/src/lib/api/services/notification-service.ts
+++ b/src/lib/api/services/notification-service.ts
@@ -8,6 +8,32 @@ import {
import { ApiResponse } from "../types/shared";
export const notificationService = {
+ getMyNotifications: async (
+ params?: Record,
+ ): Promise => {
+ try {
+ return (
+ await api.get(API_ENDPOINTS.NOTIFICATIONS.MY_NOTIFICATIONS, { params })
+ ).data;
+ } catch (error) {
+ console.error(
+ "ERROR Caught in notification service => getMyNotifications",
+ error,
+ );
+ throw error;
+ }
+ },
+ markNotificationAsSeen: async (id: string) => {
+ try {
+ return (await api.get(API_ENDPOINTS.NOTIFICATIONS.MARK_AS_SEEN(id))).data;
+ } catch (error) {
+ console.error(
+ "ERROR Caught in notification service => markNotificationAsRead",
+ error,
+ );
+ throw error;
+ }
+ },
getNotifications: async (
params?: Record,
): Promise => {
diff --git a/src/utils/shared.ts b/src/utils/shared.ts
index b451b25..5b13ddf 100644
--- a/src/utils/shared.ts
+++ b/src/utils/shared.ts
@@ -72,3 +72,13 @@ export const getEnumAsArray = (target: T): ILabelValue[] => {
value,
}));
};
+export const truncateString = (
+ str: string,
+ startLength: number = 10,
+ endLength: number = 10,
+): string => {
+ if (str.length <= startLength + endLength) {
+ return str;
+ }
+ return str.slice(0, startLength) + "..." + str.slice(-endLength);
+};