From da82399d079b3e91dbd557bacaed93037f47f59c Mon Sep 17 00:00:00 2001 From: AmirReza Jamali Date: Tue, 10 Feb 2026 18:19:49 +0330 Subject: [PATCH] feat(tables): integrate EmptyListWrapper component for improved empty state handling - Add EmptyListWrapper to various tables (Admins, Companies, Customers, Feedback, Notification History, Tenders) to provide a consistent empty state message when no data is available. - Update phone number and status display logic in the Profile page to handle null values gracefully. - Introduce worktree setup configuration in worktrees.json for easier project setup. --- .cursor/worktrees.json | 5 + src/app/profile/page.tsx | 4 +- src/components/Tables/admins/index.tsx | 151 +++++++++--------- .../Tables/admins/useAdminsPresenter.ts | 2 +- .../companies/company-categories/index.tsx | 148 +++++++++-------- src/components/Tables/companies/index.tsx | 18 ++- src/components/Tables/customers/index.tsx | 14 ++ .../customers/useCustomerListPresenter.ts | 32 ++-- .../Tables/feedback-table/index.tsx | 19 ++- .../Tables/notification-history/index.tsx | 19 ++- src/components/Tables/tenders/index.tsx | 16 +- .../useCreateNotificationFormPresenter.ts | 2 +- src/hooks/queries/useCustomerQueries.ts | 12 ++ 13 files changed, 240 insertions(+), 202 deletions(-) create mode 100644 .cursor/worktrees.json diff --git a/.cursor/worktrees.json b/.cursor/worktrees.json new file mode 100644 index 0000000..77e9744 --- /dev/null +++ b/.cursor/worktrees.json @@ -0,0 +1,5 @@ +{ + "setup-worktree": [ + "npm install" + ] +} diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index 5f70706..9af0bb2 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -62,7 +62,7 @@ export default function Page() {

Phone

- {formatPhoneNumber(data?.phone)} + {data?.phone ? formatPhoneNumber(data?.phone) : "-"}

@@ -84,7 +84,7 @@ export default function Page() {

Status

- {data?.status} + {data?.status ?? "-"}
diff --git a/src/components/Tables/admins/index.tsx b/src/components/Tables/admins/index.tsx index c90de0e..ccff704 100644 --- a/src/components/Tables/admins/index.tsx +++ b/src/components/Tables/admins/index.tsx @@ -1,6 +1,7 @@ "use client"; import { PencilSquareIcon, TrashIcon, UserSettingIcon } from "@/assets/icons"; +import EmptyListWrapper from "@/components/HOC/EmptyListWrapper"; import ConfirmationModal from "@/components/ui/ConfirmationModal"; import ListHeader from "@/components/ui/ListHeader"; import Modal from "@/components/ui/modal"; @@ -65,85 +66,81 @@ const AdminsTable = () => { {isPending && } - {!allUsers.length ? ( - - -

No user

-
-
- ) : ( - allUsers.map((item, index) => { - return ( - - - {index + 1} - - - {item?.full_name} - - - {item?.email} - + + {allUsers.map((item, index) => ( + + + {index + 1} + + + {item?.full_name} + + + {item?.email} + - - {item?.username} - - - {item?.status} - + + {item?.username} + + + {item?.status} + - -
- - - -
-
-
- ); - }) - )} + +
+ + + +
+
+
+ ))} +
{ } = useForm({ mode: "onChange", defaultValues: { - status: currentUser?.status, + status: currentUser?.status ?? "", }, }); const [params, setParams] = useState>({}); diff --git a/src/components/Tables/companies/company-categories/index.tsx b/src/components/Tables/companies/company-categories/index.tsx index c429c84..ab426af 100644 --- a/src/components/Tables/companies/company-categories/index.tsx +++ b/src/components/Tables/companies/company-categories/index.tsx @@ -1,6 +1,7 @@ "use client"; import { PencilSquareIcon, TrashIcon } from "@/assets/icons"; import { Switch } from "@/components/FormElements/switch"; +import EmptyListWrapper from "@/components/HOC/EmptyListWrapper"; import ConfirmationModal from "@/components/ui/ConfirmationModal"; import IsVisible from "@/components/ui/IsVisible"; import { @@ -44,6 +45,15 @@ const CompanyCategoriesTable = () => { isMutating, } = useCompanyCategoriesPresenter(); + const columns = [ + "Row", + "Title", + "Description", + "Created at", + "Published", + "Actions", + ]; + return (
{ Actions - {isLoading && } + {isLoading && } - - - -
No category found
-
-
-
- - {categories?.map((category, index) => { - return ( - - {index + 1} - {category.name} - {category.description} - - {unixToDate({ unix: category.created_at, hasTime: true })} - - - - - - - { - togglePublished(category.id); - }} - checked={category.published} - onToggle={(e) => { - console.log(e); - }} - /> - - - -
- - -
-
-
- ); - })} -
+ + {(categories ?? []).map((category, index) => ( + + {index + 1} + {category.name} + {category.description} + + {unixToDate({ unix: category.created_at, hasTime: true })} + + + + + + + { + togglePublished(category.id); + }} + checked={category.published} + onToggle={(e) => { + console.log(e); + }} + /> + + + +
+ + +
+
+
+ ))} +
{ setFilterValue, isMutating, } = useCompanyListPresenter(); + const companies = allCompanies.filter( + (c): c is NonNullable => c != null, + ); return (
{ {isPending && } - {allCompanies.map((company, index) => - !company ? ( -
No Data was Found
- ) : ( + + {companies.map((company, index) => ( {
- ), - )} + ))} + { router, columns, allCustomers, + metadata, isModalOpen, assignSelectedCompanies, selectedCompanies, @@ -48,6 +50,7 @@ const CustomersTable = () => { watch, setFilterValue, isMutating, + setParams, } = useCustomerListPresenter(); return (
@@ -170,6 +173,17 @@ const CustomersTable = () => { + { + setParams((currentParams) => ({ + ...currentParams, + offset: e.selected * (metadata?.limit ?? 10), + limit: metadata?.limit ?? 10, + })); + }} + /> setIsFilterModalOpen(false)} diff --git a/src/components/Tables/customers/useCustomerListPresenter.ts b/src/components/Tables/customers/useCustomerListPresenter.ts index b61058b..811ea41 100644 --- a/src/components/Tables/customers/useCustomerListPresenter.ts +++ b/src/components/Tables/customers/useCustomerListPresenter.ts @@ -2,14 +2,15 @@ import { useAssignCompany, - useCustomersInfiniteQuery, + useCustomersQuery, useDeleteCustomerQuery, } from "@/hooks/queries"; import { TAssignCustomerToCompanyCredentials, TCustomer } from "@/lib/api"; +import { apiDefaultParams } from "@/constants/Api_Params"; import { deleteEmptyKeys } from "@/utils/shared"; import { useIsMutating } from "@tanstack/react-query"; -import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { useEffect, useState } from "react"; +import { usePathname, useRouter } from "next/navigation"; +import { useState } from "react"; import { useForm } from "react-hook-form"; const useCustomerListPresenter = () => { @@ -25,8 +26,9 @@ const useCustomerListPresenter = () => { const [isModalOpen, setIsModalOpen] = useState(false); const pathName = usePathname(); - const searchParams = useSearchParams(); - const [params, setParams] = useState>({}); + const [params, setParams] = useState>({ + ...apiDefaultParams, + }); const router = useRouter(); const isMutating = useIsMutating(); @@ -38,18 +40,7 @@ const useCustomerListPresenter = () => { setValue: setFilterValue, } = useForm(); - useEffect(() => { - const newParams: Record = { - sort: "created_at", - }; - for (const [key, value] of searchParams.entries()) { - newParams[key] = value; - } - setParams(newParams); - filterFormReset(newParams); - }, [searchParams, filterFormReset]); - - const { data, isPending } = useCustomersInfiniteQuery(params); + const { data, isPending } = useCustomersQuery(params); const { mutate: deleteCustomer } = useDeleteCustomerQuery(() => setIsModalOpen(false), ); @@ -61,8 +52,9 @@ const useCustomerListPresenter = () => { ); const search = (data: any) => { - const newParams = { ...params, ...data }; + const newParams = { ...params, ...data, offset: 0 }; const cleanedParams = deleteEmptyKeys(newParams); + setParams(cleanedParams); const queryString = new URLSearchParams(cleanedParams).toString(); router.push(`${pathName}?${queryString}`); setIsFilterModalOpen(false); @@ -85,9 +77,10 @@ const useCustomerListPresenter = () => { } }; - const customers = data?.pages.flatMap((page) => page.data.customers) ?? []; + const customers = data?.data?.customers ?? []; return { allCustomers: customers, + metadata: data?.meta, currentCustomer, columns, isPending, @@ -110,6 +103,7 @@ const useCustomerListPresenter = () => { watch, setFilterValue, isMutating, + setParams, }; }; diff --git a/src/components/Tables/feedback-table/index.tsx b/src/components/Tables/feedback-table/index.tsx index 6751676..291c8b0 100644 --- a/src/components/Tables/feedback-table/index.tsx +++ b/src/components/Tables/feedback-table/index.tsx @@ -1,4 +1,5 @@ import { ExclamationIcon } from "@/assets/icons"; +import EmptyListWrapper from "@/components/HOC/EmptyListWrapper"; import { Table, TableBody, @@ -48,14 +49,12 @@ const FeedbackTable = ({ id, paramKey }: IProps) => { {isLoading && } - {!feedback?.length ? ( - - -

No feedback

-
-
- ) : ( - feedback?.map((item, index) => ( + + {(feedback ?? []).map((item, index) => ( { - )) - )} + ))} +
diff --git a/src/components/Tables/notification-history/index.tsx b/src/components/Tables/notification-history/index.tsx index bdf1f1b..a996a4f 100644 --- a/src/components/Tables/notification-history/index.tsx +++ b/src/components/Tables/notification-history/index.tsx @@ -1,5 +1,6 @@ "use client"; import { EyeIcon } from "@/assets/icons"; +import EmptyListWrapper from "@/components/HOC/EmptyListWrapper"; import { ShowcaseSection } from "@/components/Layouts/showcase-section"; import Pagination from "@/components/ui/pagination"; import Status from "@/components/ui/Status"; @@ -60,14 +61,12 @@ const NotificationHistoryTable = () => { {isLoading && } - {!notificationHistory?.length ? ( - - -

No notifications

-
-
- ) : ( - notificationHistory.map((item, index) => ( + + {(notificationHistory ?? []).map((item, index) => ( {index + 1} {item.title} @@ -114,8 +113,8 @@ const NotificationHistoryTable = () => { - )) - )} + ))} +
{ {isPending && } - {allTenders?.data.tenders?.map((item, index) => - !item ? ( -
No tenders were found
- ) : ( + + {(allTenders?.data.tenders ?? []).map((item, index) => ( { - ), - )} + ))} +
{ ], [NotificationTargetGroups.SPECIFIC_USERS]: users?.data.users?.map((user) => ({ - label: user.full_name, + label: user.full_name ?? "", value: user.id, })) ?? null, [NotificationTargetGroups.SPECIFIC_CUSTOMER]: diff --git a/src/hooks/queries/useCustomerQueries.ts b/src/hooks/queries/useCustomerQueries.ts index 921739d..73ea1b1 100644 --- a/src/hooks/queries/useCustomerQueries.ts +++ b/src/hooks/queries/useCustomerQueries.ts @@ -36,6 +36,18 @@ export const useCustomersInfiniteQuery = (params?: Record) => { }, }); }; + +export const useCustomersQuery = (params?: Record) => { + const queryKey = useMemo( + () => [API_ENDPOINTS.CUSTOMERS.READ_ALL, params], + [params], + ); + + return useQuery({ + queryKey, + queryFn: () => customersService.getCustomers(params), + }); +}; export const useGetAllCustomers = () => { const queryKey = useMemo(() => ["GET ALL CUSTOMERS"], []); return useQuery({