diff --git a/src/components/Tables/companies/index.tsx b/src/components/Tables/companies/index.tsx
index f42c1ce..e2261a7 100644
--- a/src/components/Tables/companies/index.tsx
+++ b/src/components/Tables/companies/index.tsx
@@ -1,6 +1,7 @@
"use client";
import { ExclamationIcon, PencilSquareIcon, TrashIcon } from "@/assets/icons";
import ConfirmationModal from "@/components/ui/ConfirmationModal";
+import { _TooltipDefaultParams } from "@/constants/tooltip";
import {
Table,
TableBody,
@@ -12,6 +13,7 @@ import {
import TableSkeleton from "@/components/ui/TableSkeleton";
import { formatPhoneNumber } from "@/utils/shared";
import Link from "next/link";
+import { Tooltip } from "react-tooltip";
import { useCompanyListPresenter } from "./useCompanyListPresenter";
interface IProps {}
@@ -88,30 +90,42 @@ const CompaniesTable = ({}: IProps) => {
diff --git a/src/components/Tables/customers/index.tsx b/src/components/Tables/customers/index.tsx
index 0d0d4da..3797bc1 100644
--- a/src/components/Tables/customers/index.tsx
+++ b/src/components/Tables/customers/index.tsx
@@ -9,6 +9,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
+import { _TooltipDefaultParams } from "@/constants/tooltip";
import Link from "next/link";
import useCustomerListPresenter from "./useCustomerListPresenter";
@@ -18,6 +19,7 @@ import Status from "@/components/ui/Status";
import TableSkeleton from "@/components/ui/TableSkeleton";
import { unixToDate } from "@/utils/shared";
import { toast } from "react-toastify";
+import { Tooltip } from "react-tooltip";
import AssignToCompanyModalContent from "./AssignToCompanyModalContent";
const CustomersTable = () => {
@@ -65,7 +67,7 @@ const CustomersTable = () => {
{customer.full_name}
{customer.email}
- {unixToDate(customer.created_at)}
+ {unixToDate({ unix: customer.created_at, hasTime: true })}
@@ -89,25 +91,36 @@ const CustomersTable = () => {
diff --git a/src/components/Tables/feedback-table/index.tsx b/src/components/Tables/feedback-table/index.tsx
index 3dbd299..6751676 100644
--- a/src/components/Tables/feedback-table/index.tsx
+++ b/src/components/Tables/feedback-table/index.tsx
@@ -67,11 +67,11 @@ const FeedbackTable = ({ id, paramKey }: IProps) => {
{item.feedback_type}
- {unixToDate(item.updated_at)}
+ {unixToDate({ unix: item.updated_at, hasTime: true })}
- {unixToDate(item.created_at)}
+ {unixToDate({ unix: item.created_at, hasTime: true })}
{
+ const {
+ columns,
+ isLoading,
+ metadata,
+ notificationHistory,
+ pathName,
+ router,
+ setParams,
+ } = useNotificationHistoryTablePresenter();
+
+ return (
+
+
+
+
+ {columns.map((column) => (
+
+ {column}
+
+ ))}
+
+
+ {isLoading && }
+
+ {!notificationHistory?.length ? (
+
+
+ No notifications
+
+
+ ) : (
+ notificationHistory.map((item, index) => (
+
+ {index + 1}
+ {item.title}
+ {item.event_type}
+
+ {isoStringToDate({
+ hasTime: true,
+ isoString: item.created_at,
+ })}
+
+ {item.message}
+
+ {item.priority}
+
+
+ {item.type}
+
+
+ {item.seen ? "Seen" : "Unseen"}
+
+
+ {item.status}
+
+
+
+
+
+ ))
+ )}
+
+
+ {
+ setParams((currentParams) => ({
+ ...currentParams,
+ offset: e.selected * (metadata?.limit ?? 10),
+ limit: metadata?.limit ?? 1000,
+ }));
+ }}
+ />
+
+ );
+};
+
+export default NotificationHistoryTable;
diff --git a/src/components/Tables/notification-history/useNotificationHistoryTablePresenter.ts b/src/components/Tables/notification-history/useNotificationHistoryTablePresenter.ts
new file mode 100644
index 0000000..f24e7f3
--- /dev/null
+++ b/src/components/Tables/notification-history/useNotificationHistoryTablePresenter.ts
@@ -0,0 +1,37 @@
+"use client";
+import { apiDefaultParams } from "@/constants/Api_Params";
+import { useGetNotificationHistoryQuery } from "@/hooks/queries/useNotificationQueries";
+import { usePathname, useRouter } from "next/navigation";
+import { useState } from "react";
+
+const useNotificationHistoryTablePresenter = () => {
+ const [params, setParams] = useState>({
+ ...apiDefaultParams,
+ });
+ const { data, isLoading } = useGetNotificationHistoryQuery(params);
+ const columns: string[] = [
+ "row",
+ "title",
+ "channel",
+ "created at",
+ "message",
+ "priority",
+ "type",
+ "seen",
+ "status",
+ "actions",
+ ];
+ const pathName = usePathname();
+ const router = useRouter();
+ return {
+ notificationHistory: data?.data.notifications,
+ metadata: data?.meta,
+ isLoading,
+ columns,
+ router,
+ pathName,
+ setParams,
+ };
+};
+
+export default useNotificationHistoryTablePresenter;
diff --git a/src/components/Tables/tenders/index.tsx b/src/components/Tables/tenders/index.tsx
index 547219a..a89295b 100644
--- a/src/components/Tables/tenders/index.tsx
+++ b/src/components/Tables/tenders/index.tsx
@@ -9,11 +9,13 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
+import { Tooltip } from "react-tooltip";
import useTenderListPresenter from "./useTenderListPresenter";
import { ExclamationIcon, EyeIcon } from "@/assets/icons";
import Pagination from "@/components/ui/pagination";
import TableSkeleton from "@/components/ui/TableSkeleton";
+import { _TooltipDefaultParams } from "@/constants/tooltip";
import { cn } from "@/lib/utils";
import Link from "next/link";
const TendersTable = () => {
@@ -70,18 +72,30 @@ const TendersTable = () => {
colSpan={100}
>
diff --git a/src/components/ui/Status.tsx b/src/components/ui/Status.tsx
index 4684e37..a8d4274 100644
--- a/src/components/ui/Status.tsx
+++ b/src/components/ui/Status.tsx
@@ -22,6 +22,7 @@ const Status = ({ status, children }: IProps) => {
"success",
"verified",
"published",
+ "sent"
];
const blues = ["awarded", "processing", "in-review", "scheduled"];
const yellows = ["pending", "warning", "on-hold", "delayed"];
diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx
index dcebaa8..8d39467 100644
--- a/src/components/ui/table.tsx
+++ b/src/components/ui/table.tsx
@@ -70,7 +70,7 @@ export function TableHead({
return (
({
+ id,
+ delayShow: 500,
+ delayHide: 500,
+ variant,
+ style: { backgroundColor: variant === "error" ? "#F23030" : "", ...styles },
+});
diff --git a/src/css/style.css b/src/css/style.css
index a79fe56..073e170 100644
--- a/src/css/style.css
+++ b/src/css/style.css
@@ -49,6 +49,9 @@
.tableCheckbox:checked ~ div span {
@apply opacity-100;
}
+.react-tooltip {
+ @apply !rounded-full !transition-opacity !duration-500;
+}
.tableCheckbox:checked ~ div {
@apply border-primary bg-primary;
@@ -502,17 +505,17 @@ input[type="search"]::-webkit-search-cancel-button {
}
.Toastify__toast--success {
- background: linear-gradient(to bottom right, #82E6AC, #1A8245) !important;
+ background: linear-gradient(to bottom right, #82e6ac, #1a8245) !important;
}
.Toastify__toast--error {
- background: linear-gradient(to bottom right, #F89090, #E10E0E) !important;
+ background: linear-gradient(to bottom right, #f89090, #e10e0e) !important;
}
.Toastify__toast--warning {
- background: linear-gradient(to bottom right, #F59E0B, #D97706) !important;
+ background: linear-gradient(to bottom right, #f59e0b, #d97706) !important;
}
.Toastify__toast--info {
- background: linear-gradient(to bottom right, #8099EC, #1C3FB7) !important;
+ background: linear-gradient(to bottom right, #8099ec, #1c3fb7) !important;
}
diff --git a/src/hooks/queries/useNotificationQueries.ts b/src/hooks/queries/useNotificationQueries.ts
new file mode 100644
index 0000000..9d5646c
--- /dev/null
+++ b/src/hooks/queries/useNotificationQueries.ts
@@ -0,0 +1,27 @@
+import { API_ENDPOINTS } from "@/lib/api";
+import { notificationService } from "@/lib/api/services/notification-service";
+import { useQuery } from "@tanstack/react-query";
+import { useMemo } from "react";
+
+export const useGetNotificationHistoryQuery = (
+ params?: Record,
+) => {
+ const queryKey = useMemo(
+ () => [API_ENDPOINTS.NOTIFICATIONS.HISTORY, "READ NOTIFICATIONS HISTORY"],
+ [],
+ );
+ return useQuery({
+ queryKey,
+ queryFn: () => notificationService.getNotifications(params),
+ });
+};
+export const useGetNotificationDetails = (id: string) => {
+ const queryKey = useMemo(
+ () => [API_ENDPOINTS.NOTIFICATIONS.DETAILS(id), "NOTIFICATION DETAILS"],
+ [id],
+ );
+ return useQuery({
+ queryKey,
+ queryFn: () => notificationService.getNotificationDetails(id),
+ });
+};
diff --git a/src/lib/api/endpoints.ts b/src/lib/api/endpoints.ts
index 587cb86..29c7d8b 100644
--- a/src/lib/api/endpoints.ts
+++ b/src/lib/api/endpoints.ts
@@ -50,4 +50,8 @@ export const API_ENDPOINTS = {
DETAILS: (id: string) => `feedback/${id}`,
DELETE: (id: string) => `feedback/${id}`,
},
+ NOTIFICATIONS: {
+ HISTORY: "notifications",
+ DETAILS: (id: string) => `notifications/view/${id}`,
+ },
} as const;
diff --git a/src/lib/api/services/notification-service.ts b/src/lib/api/services/notification-service.ts
new file mode 100644
index 0000000..2d888bf
--- /dev/null
+++ b/src/lib/api/services/notification-service.ts
@@ -0,0 +1,37 @@
+import api from "../axios";
+import { API_ENDPOINTS } from "../endpoints";
+import {
+ TNotificationDetailsResponse,
+ TNotificationHistoryResponse,
+} from "../types/NotificationHistory";
+import { ApiResponse } from "../types/shared";
+
+export const notificationService = {
+ getNotifications: async (
+ params?: Record,
+ ): Promise => {
+ try {
+ return (await api.get(API_ENDPOINTS.NOTIFICATIONS.HISTORY, { params }))
+ .data;
+ } catch (error) {
+ console.error(
+ "ERROR Caught in notification service => getNotifications",
+ error,
+ );
+ throw error;
+ }
+ },
+ getNotificationDetails: async (
+ id: string,
+ ): Promise> => {
+ try {
+ return (await api.get(API_ENDPOINTS.NOTIFICATIONS.DETAILS(id))).data;
+ } catch (error) {
+ console.error(
+ "ERROR Caught in notification service => getNotificationDetails",
+ error,
+ );
+ throw error;
+ }
+ },
+};
diff --git a/src/lib/api/types/Customers.ts b/src/lib/api/types/Customers.ts
index f13f7b4..8213774 100644
--- a/src/lib/api/types/Customers.ts
+++ b/src/lib/api/types/Customers.ts
@@ -31,9 +31,6 @@ const customersSchema = z.object({
username: z.string(),
});
-export const customersListResponse = z.object({
- customers: z.array(customersSchema),
-});
const createCustomerCredentials = z.object({
address: z
.object({
@@ -99,6 +96,9 @@ const createCustomerCredentials = z.object({
"Username must contain only alphanumeric characters",
),
});
+export const customersListResponse = z.object({
+ customers: z.array(customersSchema),
+});
const assignCustomerToCompanySchema = z.object({
company_ids: z.array(z.string()),
});
diff --git a/src/lib/api/types/NotificationHistory.ts b/src/lib/api/types/NotificationHistory.ts
new file mode 100644
index 0000000..dc738d6
--- /dev/null
+++ b/src/lib/api/types/NotificationHistory.ts
@@ -0,0 +1,43 @@
+import { z } from "zod";
+import { createApiResponseSchema } from "./Factory";
+
+export const notificationSchema = z.object({
+ created_at: z.string(),
+ event_type: z.string(),
+ id: z.string(),
+ image: z.string(),
+ is_scheduled: z.boolean(),
+ link: z.string(),
+ message: z.string(),
+ metadata: z.object({
+ additionalProp1: z.string(),
+ additionalProp2: z.string(),
+ additionalProp3: z.string(),
+ }),
+ method: z.object({
+ additionalProp1: z.string(),
+ additionalProp2: z.string(),
+ additionalProp3: z.string(),
+ }),
+ priority: z.string(),
+ schedule_at: z.number(),
+ scheduled_at: z.number(),
+ seen: z.boolean(),
+ seen_at: z.number(),
+ status: z.string(),
+ title: z.string(),
+ type: z.string(),
+ updated_at: z.string(),
+ user_id: z.string(),
+});
+
+const notificationHistoryResponseSchema = createApiResponseSchema(
+ z.object({
+ notifications: z.array(notificationSchema),
+ }),
+);
+export type TNotificationHistory = z.infer;
+export type TNotificationDetailsResponse = z.infer;
+export type TNotificationHistoryResponse = z.infer<
+ typeof notificationHistoryResponseSchema
+>;
diff --git a/src/utils/shared.ts b/src/utils/shared.ts
index 2585746..6a67c54 100644
--- a/src/utils/shared.ts
+++ b/src/utils/shared.ts
@@ -1,11 +1,29 @@
import * as moment from "moment";
-export const unixToDate = (unix: number) => {
- return moment.unix(unix).format("YYYY/MM/DD HH:MM");
+export const unixToDate = ({
+ hasTime = false,
+ unix,
+}: {
+ unix: number;
+ hasTime?: boolean;
+}) => {
+ return moment.unix(unix).format(`YYYY/MM/DD ${hasTime && "HH:MM"}`);
};
export const msToDate = (ms: number) => {
return moment.default(ms).format("YYYY/MM/DD");
};
+export const isoStringToDate = ({
+ hasTime = false,
+ isoString,
+}: {
+ isoString: string;
+ hasTime: boolean;
+}): string => {
+ return moment
+ .default(isoString)
+ .format(`YYYY/MM/DD ${hasTime ? "HH:MM" : ""}`);
+};
+
export const getStatusColor = (status: T) => {
switch (status) {
case "active":
|