diff --git a/src/app/portal/accounts/[accountId]/edit/page.tsx b/src/app/portal/accounts/[accountId]/edit/page.tsx
deleted file mode 100644
index 0750d32..0000000
--- a/src/app/portal/accounts/[accountId]/edit/page.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import AccountForm from "@/components/accounts/account-form";
-import { SimpleContentView } from "@/components/admin-panel/simple-content-view";
-import { findAccountById } from "@/lib/actions/accounts.action";
-import { deobfuscateToNumber, obfuscate } from "@/lib/endecode";
-
-export default async function Page({
- params,
-}: {
- params: { accountId: string | "new" };
-}) {
- const account =
- params.accountId !== "new"
- ? await findAccountById(deobfuscateToNumber(params.accountId))
- : undefined;
-
- const breadcrumbItems = [
- { title: "Dashboard", link: "/portal" },
- { title: "Accounts", link: "/portal/accounts" },
- ...(account
- ? [
- {
- title: `${account.name}`,
- link: `/portal/accounts/${obfuscate(account.id)}`,
- },
- { title: "Edit", link: "#" },
- ]
- : [{ title: "Add", link: "#" }]),
- ];
-
- return (
-
-
-
- );
-}
diff --git a/src/app/portal/accounts/[accountId]/page.tsx b/src/app/portal/accounts/[accountId]/page.tsx
deleted file mode 100644
index 563fa4a..0000000
--- a/src/app/portal/accounts/[accountId]/page.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { notFound } from "next/navigation";
-
-import { AccountView } from "@/components/accounts/account-view";
-import { SimpleContentView } from "@/components/admin-panel/simple-content-view";
-import { findAccountById } from "@/lib/actions/accounts.action";
-import { deobfuscateToNumber } from "@/lib/endecode";
-
-export default async function Page({
- params,
-}: {
- params: { accountId: string };
-}) {
- const account = await findAccountById(deobfuscateToNumber(params.accountId));
- if (!account) {
- notFound();
- }
-
- const breadcrumbItems = [
- { title: "Dashboard", link: "/portal" },
- { title: "Accounts", link: "/portal/accounts" },
- { title: account.name, link: "#" },
- ];
-
- return (
-
-
-
- );
-}
diff --git a/src/app/portal/accounts/layout.tsx b/src/app/portal/accounts/layout.tsx
deleted file mode 100644
index bf0e947..0000000
--- a/src/app/portal/accounts/layout.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from "react";
-
-import { ResourceProvider } from "@/providers/resource-provider";
-
-export default function TeamsLayout({
- children,
-}: {
- children: React.ReactNode;
-}) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/portal/accounts/page.tsx b/src/app/portal/accounts/page.tsx
deleted file mode 100644
index 4ff3a85..0000000
--- a/src/app/portal/accounts/page.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Plus } from "lucide-react";
-import Link from "next/link";
-import React from "react";
-
-import { AccountsTable } from "@/components/accounts/account-table";
-import { SimpleContentView } from "@/components/admin-panel/simple-content-view";
-import { Heading } from "@/components/heading";
-import { buttonVariants } from "@/components/ui/button";
-import { Separator } from "@/components/ui/separator";
-import { searchAccounts } from "@/lib/actions/accounts.action";
-import { cn } from "@/lib/utils";
-import { accountSearchParamsSchema } from "@/types/accounts";
-
-const breadcrumbItems = [
- { title: "Dashboard", link: "/portal" },
- { title: "Accounts", link: "/portal/accounts" },
-];
-
-type paramsProps = {
- searchParams: {
- [key: string]: string | string[] | undefined;
- };
-};
-
-const Page = ({ searchParams }: paramsProps) => {
- const search = accountSearchParamsSchema.parse(searchParams);
- const accountPromise = searchAccounts(search);
-
- return (
-
-
-
-
-
- );
-};
-
-export default Page;
diff --git a/src/app/portal/contacts/[contactId]/edit/page.tsx b/src/app/portal/contacts/[contactId]/edit/page.tsx
deleted file mode 100644
index c48fe95..0000000
--- a/src/app/portal/contacts/[contactId]/edit/page.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { SimpleContentView } from "@/components/admin-panel/simple-content-view";
-import ContactForm from "@/components/contacts/contact-form";
-import { findContactById } from "@/lib/actions/contacts.action";
-import { deobfuscateToNumber } from "@/lib/endecode";
-
-const breadcrumbItems = [
- { title: "Dashboard", link: "/portal" },
- { title: "Contacts", link: "/portal/contacts" },
- { title: "Create", link: "/portal/contacts/new" },
-];
-
-export default async function Page({
- params,
-}: {
- params: { contactId: string | "new" };
-}) {
- const contact =
- params.contactId !== "new"
- ? await findContactById(deobfuscateToNumber(params.contactId))
- : undefined;
-
- return (
-
-
-
- );
-}
diff --git a/src/app/portal/contacts/[contactId]/page.tsx b/src/app/portal/contacts/[contactId]/page.tsx
deleted file mode 100644
index d45e658..0000000
--- a/src/app/portal/contacts/[contactId]/page.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { notFound } from "next/navigation";
-
-import { ContentLayout } from "@/components/admin-panel/content-layout";
-import { Breadcrumbs } from "@/components/breadcrumbs";
-import { ContactView } from "@/components/contacts/contact-view";
-import { findContactById } from "@/lib/actions/contacts.action";
-import { deobfuscateToNumber } from "@/lib/endecode";
-
-const Page = async ({ params }: { params: { contactId: string } }) => {
- const contact = await findContactById(deobfuscateToNumber(params.contactId));
- if (!contact) {
- notFound();
- }
-
- const breadcrumbItems = [
- { title: "Dashboard", link: "/portal" },
- { title: "Contacts", link: "/portal/contacts" },
- { title: `${contact.firstName} ${contact.lastName}`, link: "#" },
- ];
-
- return (
-
-
-
-
- );
-};
-
-export default Page;
diff --git a/src/app/portal/contacts/layout.tsx b/src/app/portal/contacts/layout.tsx
deleted file mode 100644
index 3735cb1..0000000
--- a/src/app/portal/contacts/layout.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from "react";
-
-import { ResourceProvider } from "@/providers/resource-provider";
-
-export default function TeamsLayout({
- children,
-}: {
- children: React.ReactNode;
-}) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/portal/contacts/page.tsx b/src/app/portal/contacts/page.tsx
deleted file mode 100644
index d2b4b67..0000000
--- a/src/app/portal/contacts/page.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { SimpleContentView } from "@/components/admin-panel/simple-content-view";
-
-const breadcrumbItems = [
- { title: "Dashboard", link: "/portal" },
- { title: "Contacts", link: "/portal/contacts" },
-];
-
-const ContactPage = () => {
- return (
-
- Contacts
-
- );
-};
-
-export default ContactPage;
diff --git a/src/app/portal/files/layout.tsx b/src/app/portal/files/layout.tsx
deleted file mode 100644
index 7c2865e..0000000
--- a/src/app/portal/files/layout.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from "react";
-
-import { ResourceProvider } from "@/providers/resource-provider";
-
-export default function TeamsLayout({
- children,
-}: {
- children: React.ReactNode;
-}) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/portal/files/page.tsx b/src/app/portal/files/page.tsx
deleted file mode 100644
index 131dbdc..0000000
--- a/src/app/portal/files/page.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Button } from "@/components/ui/button";
-
-const FilesPage = () => {
- return (
- <>
-
-
Inventory
-
-
-
-
- You have no files
-
-
Start uploading files
-
-
-
- >
- );
-};
-export default FilesPage;
diff --git a/src/app/portal/settings/accounts/industries/page.tsx b/src/app/portal/settings/accounts/industries/page.tsx
deleted file mode 100644
index ac407b0..0000000
--- a/src/app/portal/settings/accounts/industries/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-const AccountIndustriesSettingPage = () => {
- return Industries setting
;
-};
-
-export default AccountIndustriesSettingPage;
diff --git a/src/app/portal/settings/accounts/types/page.tsx b/src/app/portal/settings/accounts/types/page.tsx
deleted file mode 100644
index 047bd3a..0000000
--- a/src/app/portal/settings/accounts/types/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-const AccountTypesSettingPage = () => {
- return Account Types
;
-};
-
-export default AccountTypesSettingPage;
diff --git a/src/app/portal/settings/authorities/page.tsx b/src/app/portal/settings/authorities/page.tsx
index 0a9b3e5..d8e55a7 100644
--- a/src/app/portal/settings/authorities/page.tsx
+++ b/src/app/portal/settings/authorities/page.tsx
@@ -2,15 +2,14 @@
import { Plus } from "lucide-react";
import { useRouter } from "next/navigation";
-import React, { useState } from "react";
+import React from "react";
import { SimpleContentView } from "@/components/admin-panel/simple-content-view";
-import { AuthoritiesTable } from "@/components/authorities/authority-table";
+import { AuthoritiesView } from "@/components/authorities/authority-list";
import { Heading } from "@/components/heading";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { usePagePermission } from "@/hooks/use-page-permission";
-import { getAuthorities } from "@/lib/actions/authorities.action";
import { PermissionUtils } from "@/types/resources";
const breadcrumbItems = [
@@ -22,7 +21,6 @@ const breadcrumbItems = [
const AuthoritiesPage = () => {
const router = useRouter();
const permissionLevel = usePagePermission();
- const [authorityPromise, setAuthorityPromise] = useState(getAuthorities);
return (
@@ -38,10 +36,7 @@ const AuthoritiesPage = () => {
)}
-
+
);
};
diff --git a/src/components/accounts/account-delete-dialog.tsx b/src/components/accounts/account-delete-dialog.tsx
deleted file mode 100644
index dfdd46f..0000000
--- a/src/components/accounts/account-delete-dialog.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-"use client";
-
-import {
- EntitiesDeleteDialog,
- RowEntitiesDeleteDialogProps,
-} from "@/components/shared/table-entity-delete-dialog";
-import { AccountDTO } from "@/types/accounts";
-
-export function AccountDeleteDialog({
- ...props
-}: RowEntitiesDeleteDialogProps) {
- return EntitiesDeleteDialog({
- ...props,
- });
-}
diff --git a/src/components/accounts/account-form.tsx b/src/components/accounts/account-form.tsx
deleted file mode 100644
index 70194eb..0000000
--- a/src/components/accounts/account-form.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-"use client";
-
-import { zodResolver } from "@hookform/resolvers/zod";
-import { useRouter } from "next/navigation";
-import React from "react";
-import { useForm } from "react-hook-form";
-
-import AccountIndustriesSelect from "@/components/accounts/account-industries-select";
-import AccountStatusSelect from "@/components/accounts/account-status-select";
-import AccountTypesSelect from "@/components/accounts/account-types-select";
-import { Heading } from "@/components/heading";
-import { Button } from "@/components/ui/button";
-import {
- ExtInputField,
- ExtTextAreaField,
- FormProps,
- SubmitButton,
-} from "@/components/ui/ext-form";
-import { Form } from "@/components/ui/form";
-import { Separator } from "@/components/ui/separator";
-import { saveOrUpdateAccount } from "@/lib/actions/accounts.action";
-import { validateForm } from "@/lib/validator";
-import { AccountDTO, AccountDTOSchema } from "@/types/accounts";
-
-export const AccountForm = ({ initialData }: FormProps) => {
- const router = useRouter();
-
- const form = useForm({
- resolver: zodResolver(AccountDTOSchema),
- defaultValues: initialData,
- });
-
- async function onSubmit(account: AccountDTO) {
- if (validateForm(account, AccountDTOSchema, form)) {
- await saveOrUpdateAccount(isEdit, account);
- }
- }
-
- const isEdit = !!initialData;
- const title = isEdit ? `Edit account ${initialData?.name}` : "Create account";
- const description = isEdit ? "Edit account" : "Add a new account";
- const submitText = isEdit ? "Save changes" : "Create";
- const submitTextWhileLoading = isEdit ? "Saving changes ..." : "Creating ...";
-
- return (
-
- );
-};
-
-export default AccountForm;
diff --git a/src/components/accounts/account-id-select.tsx b/src/components/accounts/account-id-select.tsx
deleted file mode 100644
index 7da56d6..0000000
--- a/src/components/accounts/account-id-select.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-"use client";
-
-import React from "react";
-
-import IdInputSelect from "@/components/shared/id-input-select";
-
-type AccountFieldSelectProps = {
- accountName: string | null;
-};
-
-const AccountSelectField: React.FC = ({
- accountName,
-}) => {
- const handleButtonClick = () => {
- alert("Button clicked");
- };
-
- return (
-
- );
-};
-
-export default AccountSelectField;
diff --git a/src/components/accounts/account-industries-select.tsx b/src/components/accounts/account-industries-select.tsx
deleted file mode 100644
index 5260395..0000000
--- a/src/components/accounts/account-industries-select.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from "react";
-
-import ValuesQuerySelect from "@/components/shared/values-query-select";
-import { findAccountIndustries } from "@/lib/actions/accounts.action";
-import { EntityValueDefinition } from "@/types/commons";
-import { FormFieldProps } from "@/types/ui-components";
-
-const AccountIndustriesSelect = ({ form, required }: FormFieldProps) => {
- return (
-
- form={form}
- queryName="accountIndustries"
- fieldName="industry"
- fieldLabel="Industry"
- fetchDataFn={findAccountIndustries}
- valueKey="value"
- renderTooltip={(entityValueDef: EntityValueDefinition) =>
- `${entityValueDef.description}`
- }
- renderOption={(entityValueDef: EntityValueDefinition) =>
- `${entityValueDef.value}`
- }
- required={required}
- placeholder="Select industry"
- noDataMessage="No industry found"
- searchPlaceholder="Search industry..."
- />
- );
-};
-
-export default AccountIndustriesSelect;
diff --git a/src/components/accounts/account-status-select.tsx b/src/components/accounts/account-status-select.tsx
deleted file mode 100644
index 62f7b8b..0000000
--- a/src/components/accounts/account-status-select.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from "react";
-
-import ValuesQuerySelect from "@/components/shared/values-query-select";
-import { findAccountStatuses } from "@/lib/actions/accounts.action";
-import { EntityValueDefinition } from "@/types/commons";
-import { FormFieldProps } from "@/types/ui-components";
-
-const AccountStatusSelect = ({ form, required }: FormFieldProps) => {
- return (
-
- form={form}
- queryName="accountStatuses"
- fieldName="status"
- fieldLabel="Status"
- fetchDataFn={findAccountStatuses}
- valueKey="value"
- renderTooltip={(entityValueDef: EntityValueDefinition) =>
- `${entityValueDef.description}`
- }
- renderOption={(entityValueDef: EntityValueDefinition) =>
- `${entityValueDef.value}`
- }
- required={required}
- placeholder="Select status"
- noDataMessage="No status found"
- searchPlaceholder="Search status..."
- />
- );
-};
-
-export default AccountStatusSelect;
diff --git a/src/components/accounts/account-table-cell-action.tsx b/src/components/accounts/account-table-cell-action.tsx
deleted file mode 100644
index bbd6aa3..0000000
--- a/src/components/accounts/account-table-cell-action.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-"use client";
-// import { AlertModal } from '@/components/modal/alert-modal';
-import { Row } from "@tanstack/react-table";
-import { Edit, MoreHorizontal, Trash } from "lucide-react";
-import { useRouter } from "next/navigation";
-
-import { Button } from "@/components/ui/button";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import { obfuscate } from "@/lib/endecode";
-import { AccountDTO } from "@/types/accounts";
-
-export function AccountTableRowActions({ row }: { row: Row }) {
- const router = useRouter();
-
- function showDeleteConfirmDialog() {}
-
- return (
- <>
-
-
-
-
-
- Actions
-
-
- router.push(
- `/portal/accounts/${obfuscate(row.original.id!)}/edit`,
- )
- }
- >
- Update
-
-
- Delete
-
-
-
- >
- );
-}
diff --git a/src/components/accounts/account-table-columns.tsx b/src/components/accounts/account-table-columns.tsx
deleted file mode 100644
index 77b9788..0000000
--- a/src/components/accounts/account-table-columns.tsx
+++ /dev/null
@@ -1,133 +0,0 @@
-"use client";
-
-import { ColumnDef } from "@tanstack/react-table";
-import Link from "next/link";
-
-import { AccountTableRowActions } from "@/components/accounts/account-table-cell-action";
-import { Button } from "@/components/ui/button";
-import { Checkbox } from "@/components/ui/checkbox";
-import { DataTableColumnHeader } from "@/components/ui/table/data-table-column-header";
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from "@/components/ui/tooltip";
-import { formatDateTime, formatDateTimeDistanceToNow } from "@/lib/datetime";
-import { obfuscate } from "@/lib/endecode";
-import { AccountDTO } from "@/types/accounts";
-
-export const accounts_columns_def: ColumnDef[] = [
- {
- id: "select",
- header: ({ table }) => (
-
- table.toggleAllPageRowsSelected(!!value)
- }
- aria-label="Select all"
- className="translate-y-0.5"
- />
- ),
- cell: ({ row }) => (
- row.toggleSelected(!!value)}
- aria-label="Select row"
- className="translate-y-0.5"
- />
- ),
- enableSorting: false,
- enableHiding: false,
- },
- {
- accessorKey: "name",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => (
-
-
-
- ),
- },
- {
- accessorKey: "type",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => (
- {row.getValue("type")}
- ),
- },
- {
- accessorKey: "industry",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => (
- {row.getValue("industry")}
- ),
- },
- {
- accessorKey: "status",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => (
- {row.getValue("status")}
- ),
- },
- {
- accessorKey: "parentAccountName",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => (
-
- {row.original.parentAccountId ? (
-
- ) : (
-
- )}
-
- ),
- },
- {
- accessorKey: "createdAt",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => {
- const field = new Date(row.getValue("createdAt"));
- return (
-
-
-
- {formatDateTimeDistanceToNow(field)}
-
-
- {formatDateTime(field)}
-
-
-
- );
- },
- },
- {
- id: "actions",
- cell: ({ row }) => ,
- size: 40,
- },
-];
diff --git a/src/components/accounts/account-table-toolbar-actions.tsx b/src/components/accounts/account-table-toolbar-actions.tsx
deleted file mode 100644
index d52c3de..0000000
--- a/src/components/accounts/account-table-toolbar-actions.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-"use client";
-
-import { type Table } from "@tanstack/react-table";
-import { Download } from "lucide-react";
-
-import { AccountDeleteDialog } from "@/components/accounts/account-delete-dialog";
-import { Button } from "@/components/ui/button";
-import { deleteAccounts } from "@/lib/actions/accounts.action";
-import { exportTableToCSV } from "@/lib/export";
-import { AccountDTO } from "@/types/accounts";
-
-interface AccountTableToolbarActionsProps {
- table: Table;
-}
-
-export function AccountsTableToolbarActions({
- table,
-}: AccountTableToolbarActionsProps) {
- return (
-
- {table.getFilteredSelectedRowModel().rows.length > 0 ? (
-
row.original)}
- onSuccess={() => table.toggleAllRowsSelected(false)}
- entityName="Account"
- deleteEntitiesFn={deleteAccounts}
- />
- ) : null}
-
-
- );
-}
diff --git a/src/components/accounts/account-table.tsx b/src/components/accounts/account-table.tsx
deleted file mode 100644
index 2b28936..0000000
--- a/src/components/accounts/account-table.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-"use client";
-
-import React from "react";
-
-import { accounts_columns_def } from "@/components/accounts/account-table-columns";
-import { AccountsTableToolbarActions } from "@/components/accounts/account-table-toolbar-actions";
-import { DataTable } from "@/components/ui/table/data-table";
-import { DataTableToolbar } from "@/components/ui/table/data-table-toolbar";
-import { useDataTable } from "@/hooks/use-data-table";
-import { useFetchData } from "@/hooks/use-fetch-data-values";
-import {
- findAccountIndustries,
- findAccountStatuses,
- findAccountTypes,
- searchAccounts,
-} from "@/lib/actions/accounts.action";
-import { AccountDTO } from "@/types/accounts";
-import { DataTableFilterField } from "@/types/table";
-
-interface AccountsTableProps {
- accountsPromise: ReturnType;
- enableAdvancedFilter: boolean;
-}
-
-export function AccountsTable({
- accountsPromise,
- enableAdvancedFilter = false,
-}: AccountsTableProps) {
- const { content: data, totalPages: pageCount } = React.use(accountsPromise);
- const columns = React.useMemo(() => accounts_columns_def, []);
-
- const accountStatuses = useFetchData(findAccountStatuses);
- const accountTypes = useFetchData(findAccountTypes);
- const accountIndustries = useFetchData(findAccountIndustries);
-
- /**
- * This component can render either a faceted filter or a search filter based on the `options` prop.
- *
- * @prop options - An array of objects, each representing a filter option. If provided, a faceted filter is rendered. If not, a search filter is rendered.
- *
- * Each `option` object has the following properties:
- * @prop {string} label - The label for the filter option.
- * @prop {string} value - The value for the filter option.
- * @prop {React.ReactNode} [icon] - An optional icon to display next to the label.
- * @prop {boolean} [withCount] - An optional boolean to display the count of the filter option.
- */
- const filterFields: DataTableFilterField[] = [
- {
- id: "name",
- label: "Name",
- placeholder: "Filter names...",
- },
- // {
- // id: "type",
- // label: "Type",
- // options: accountTypes.map((type) => ({
- // label: type,
- // value: type,
- // icon: undefined,
- // withCount: true,
- // })),
- // },
- // {
- // id: "industry",
- // label: "Industry",
- // options: accountIndustries.map((industry) => ({
- // label: industry,
- // value: industry,
- // icon: undefined,
- // withCount: true,
- // })),
- // },
- // {
- // id: "status",
- // label: "Status",
- // options: accountStatuses.map((status) => ({
- // label: status,
- // value: status,
- // icon: undefined,
- // withCount: true,
- // })),
- // },
- ];
-
- const { table } = useDataTable({
- data,
- columns,
- pageCount,
- filterFields,
- enableAdvancedFilter: enableAdvancedFilter,
- initialState: {
- sorting: [{ id: "createdAt", desc: true }],
- columnPinning: { right: ["actions"] },
- },
- getRowId: (originalRow, index) => `${originalRow.id}-${index}`,
- shallow: false,
- clearOnDefault: true,
- });
-
- return (
-
-
-
-
-
- );
-}
diff --git a/src/components/accounts/account-types-select.tsx b/src/components/accounts/account-types-select.tsx
deleted file mode 100644
index 0d183b2..0000000
--- a/src/components/accounts/account-types-select.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from "react";
-
-import ValuesQuerySelect from "@/components/shared/values-query-select";
-import { findAccountTypes } from "@/lib/actions/accounts.action";
-import { EntityValueDefinition } from "@/types/commons";
-import { FormFieldProps } from "@/types/ui-components";
-
-const AccountTypesSelect = ({ form, required }: FormFieldProps) => {
- return (
-
- form={form}
- queryName="accountTypes"
- fieldName="type"
- fieldLabel="Type"
- fetchDataFn={findAccountTypes}
- valueKey="value"
- renderTooltip={(entityValueDef: EntityValueDefinition) =>
- `${entityValueDef.description}`
- }
- renderOption={(entityValueDef: EntityValueDefinition) =>
- `${entityValueDef.value}`
- }
- required={required}
- placeholder="Select type"
- noDataMessage="No type found"
- searchPlaceholder="Search type..."
- />
- );
-};
-
-export default AccountTypesSelect;
diff --git a/src/components/accounts/account-view.tsx b/src/components/accounts/account-view.tsx
deleted file mode 100644
index fb0800a..0000000
--- a/src/components/accounts/account-view.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-"use client";
-
-import { ChevronLeft, ChevronRight, Edit, Plus } from "lucide-react";
-import Link from "next/link";
-import { usePathname, useRouter } from "next/navigation";
-import React, { useEffect, useState } from "react";
-
-import { ContactsTable } from "@/components/contacts/contact-table";
-import { Badge } from "@/components/ui/badge";
-import { usePagePermission } from "@/hooks/use-page-permission";
-import {
- findNextAccount,
- findPreviousAccount,
-} from "@/lib/actions/accounts.action";
-import { searchContacts } from "@/lib/actions/contacts.action";
-import { obfuscate } from "@/lib/endecode";
-import { navigateToRecord } from "@/lib/navigation-record";
-import { cn } from "@/lib/utils";
-import { AccountDTO } from "@/types/accounts";
-import { PageableResult } from "@/types/commons";
-import { ConTactDTO } from "@/types/contacts";
-import { PermissionUtils } from "@/types/resources";
-
-import { Button, buttonVariants } from "../ui/button";
-import { Card, CardContent } from "../ui/card";
-import { ViewProps } from "../ui/ext-form";
-
-export const AccountView: React.FC> = ({
- entity,
-}: ViewProps) => {
- const permissionLevel = usePagePermission();
- const router = useRouter();
- const pathname = usePathname();
- const [account, setAccount] = useState(entity);
- const [contactPromise, setContactPromise] = useState<
- Promise>
- >(
- searchContacts({
- filters: [{ field: "account.id", operator: "eq", value: account.id! }],
- }),
- );
-
- useEffect(() => {
- if (account && pathname !== `/portal/accounts/${obfuscate(account.id)}`) {
- router.replace(`/portal/accounts/${obfuscate(account.id)}`);
- }
- }, [account, router]);
-
- const navigateToPreviousRecord = async () => {
- const previousAccount = await navigateToRecord(
- findPreviousAccount,
- "You reach the first record",
- account.id!,
- );
- setAccount(previousAccount);
- };
-
- const navigateToNextRecord = async () => {
- const nextAccount = await navigateToRecord(
- findNextAccount,
- "You reach the last record",
- account.id!,
- );
- setAccount(nextAccount);
- };
-
- return (
-
-
-
-
{account.name}
- {PermissionUtils.canWrite(permissionLevel) && (
-
- )}
-
-
-
-
-
-
-
- Type:{" "}
-
- {account.type}
-
-
-
- Industry:{" "}
-
- {account.industry}
-
-
-
- Address Line: {account.addressLine1}
- {account.city ? `, ${account.city}` : ""}
- {account.state ? `, ${account.state}` : ""}
- {account.postalCode ? `, ${account.postalCode}` : ""}
- {account.country ? `, ${account.country}` : ""}
-
-
Phone number: {account.phoneNumber}
-
- Status:{" "}
-
- {account.status}
-
-
-
- Parent Account:{" "}
- {account.parentAccountId ? (
-
- ) : (
- ""
- )}
-
-
-
-
-
-
-
-
- );
-};
diff --git a/src/components/authorities/authority-list.tsx b/src/components/authorities/authority-list.tsx
new file mode 100644
index 0000000..fa1b793
--- /dev/null
+++ b/src/components/authorities/authority-list.tsx
@@ -0,0 +1,165 @@
+"use client";
+
+import Link from "next/link";
+import React, { useEffect, useState } from "react";
+
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge"; // Add a badge for visual distinction
+import {
+ Dialog,
+ DialogContent,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
+import { getAuthorities } from "@/lib/actions/authorities.action";
+import { obfuscate } from "@/lib/endecode";
+import { AuthorityDTO } from "@/types/authorities";
+import PaginationExt from "@/components/shared/pagination-ext";
+import { PermissionUtils } from "@/types/resources";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { Ellipsis, Trash, Shield } from "lucide-react";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+import { usePagePermission } from "@/hooks/use-page-permission";
+
+export function AuthoritiesView() {
+ const [authorities, setAuthorities] = useState>([]);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [totalPages, setTotalPages] = useState(0);
+ const [totalElements, setTotalElements] = useState(0);
+ const [selectedAuthority, setSelectedAuthority] =
+ useState(null);
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
+
+ const permissionLevel = usePagePermission();
+
+ useEffect(() => {
+ const fetchAuthorities = async () => {
+ getAuthorities().then((pageableResult) => {
+ setAuthorities(pageableResult.content);
+ setTotalPages(pageableResult.totalPages);
+ setTotalElements(pageableResult.totalElements);
+ });
+ };
+
+ fetchAuthorities();
+ }, []);
+
+ function handleDeleteClick(authority: AuthorityDTO) {
+ setSelectedAuthority(authority);
+ setIsDialogOpen(true);
+ }
+
+ function confirmDeleteAuthority() {
+ if (selectedAuthority) {
+ console.log(`Delete ${JSON.stringify(selectedAuthority)}`);
+ // Add actual delete logic here (e.g., API call)
+ }
+ setIsDialogOpen(false);
+ setSelectedAuthority(null);
+ }
+
+ return (
+
+
+ {authorities?.map((authority) => (
+
+
+
+
+ {authority.systemRole && (
+
+
+ System Role
+
+ )}
+
+
{authority.description}
+
+ {PermissionUtils.canAccess(permissionLevel) &&
+ !authority.systemRole && (
+
+
+
+
+
+
+
+
+ handleDeleteClick(authority)}
+ >
+ Remove authority
+
+
+
+
+ This action will delete the role and unassign all
+ users from this role.
+
+
+
+
+
+
+ )}
+
+ ))}
+
setCurrentPage(page)}
+ />
+
+
+ {/* Confirmation Dialog */}
+
+
+ );
+}
diff --git a/src/components/authorities/authority-table-cell-action.tsx b/src/components/authorities/authority-table-cell-action.tsx
deleted file mode 100644
index bb75c31..0000000
--- a/src/components/authorities/authority-table-cell-action.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-"use client";
-// import { AlertModal } from '@/components/modal/alert-modal';
-import { Row } from "@tanstack/react-table";
-import { Edit, MoreHorizontal, Trash } from "lucide-react";
-import { useRouter } from "next/navigation";
-
-import { Button } from "@/components/ui/button";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import { obfuscate } from "@/lib/endecode";
-import { AuthorityDTO } from "@/types/authorities";
-
-export function AuthorityTableRowActions({ row }: { row: Row }) {
- const router = useRouter();
-
- const authority = row.original;
- return (
- <>
-
-
-
-
-
- Actions
-
-
- router.push(
- `/portal/settings/authorities/${obfuscate(row.original.name!)}/edit`,
- )
- : undefined
- }
- >
- Update
-
- console.log("Open menu") : undefined
- }
- >
- Delete
-
-
-
- >
- );
-}
diff --git a/src/components/authorities/authority-table-columns.tsx b/src/components/authorities/authority-table-columns.tsx
deleted file mode 100644
index 358b673..0000000
--- a/src/components/authorities/authority-table-columns.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-"use client";
-
-import { ColumnDef } from "@tanstack/react-table";
-import Link from "next/link";
-
-import { AuthorityTableRowActions } from "@/components/authorities/authority-table-cell-action";
-import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
-import { Checkbox } from "@/components/ui/checkbox";
-import { DataTableColumnHeader } from "@/components/ui/table/data-table-column-header";
-import { obfuscate } from "@/lib/endecode";
-import { AuthorityDTO } from "@/types/authorities";
-
-export const authorities_columns_def: ColumnDef[] = [
- {
- id: "select",
- header: ({ table }) => (
-
- table.toggleAllPageRowsSelected(!!value)
- }
- aria-label="Select all"
- className="translate-y-0.5"
- />
- ),
- cell: ({ row }) => (
- row.toggleSelected(!!value)}
- aria-label="Select row"
- className="translate-y-0.5"
- />
- ),
- enableSorting: false,
- enableHiding: false,
- },
- {
- accessorKey: "descriptiveName",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => {
- const isSystemLabel = row.original.systemRole;
-
- return (
-
- {isSystemLabel && System}
-
-
- );
- },
- },
- {
- accessorKey: "description",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => {row.getValue("description")}
,
- enableSorting: false,
- },
- {
- id: "actions",
- cell: ({ row }) => ,
- },
-];
diff --git a/src/components/authorities/authority-table-toolbar-actions.tsx b/src/components/authorities/authority-table-toolbar-actions.tsx
deleted file mode 100644
index 00c0d94..0000000
--- a/src/components/authorities/authority-table-toolbar-actions.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-"use client";
-
-import { type Table } from "@tanstack/react-table";
-import { Download } from "lucide-react";
-
-import { Button } from "@/components/ui/button";
-import { exportTableToCSV } from "@/lib/export";
-import { AuthorityDTO } from "@/types/authorities";
-
-interface AuthorityTableToolbarActionsProps {
- table: Table;
-}
-
-export function AuthoritiesTableToolbarActions({
- table,
-}: AuthorityTableToolbarActionsProps) {
- return (
-
-
-
- );
-}
diff --git a/src/components/authorities/authority-table.tsx b/src/components/authorities/authority-table.tsx
deleted file mode 100644
index 4b2bf4a..0000000
--- a/src/components/authorities/authority-table.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-"use client";
-
-import React, { useEffect, useState } from "react";
-
-import { authorities_columns_def } from "@/components/authorities/authority-table-columns";
-import { AuthoritiesTableToolbarActions } from "@/components/authorities/authority-table-toolbar-actions";
-import { DataTable } from "@/components/ui/table/data-table";
-import { DataTableToolbar } from "@/components/ui/table/data-table-toolbar";
-import { useDataTable } from "@/hooks/use-data-table";
-import { getAuthorities } from "@/lib/actions/authorities.action";
-import { AuthorityDTO } from "@/types/authorities";
-import { DataTableFilterField } from "@/types/table";
-
-interface AuthorityTableProps {
- authoritiesPromise: ReturnType;
- enableAdvancedFilter: boolean;
-}
-
-export function AuthoritiesTable({
- authoritiesPromise,
- enableAdvancedFilter = false,
-}: AuthorityTableProps) {
- const [data, setData] = useState>([]);
- const [pageCount, setPageCount] = useState(0);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- const fetchAuthorities = async () => {
- try {
- setLoading(true);
- const pageResult = await authoritiesPromise;
- setData(pageResult?.content);
- setPageCount(pageResult?.totalPages);
- } finally {
- setLoading(false);
- }
- };
-
- fetchAuthorities();
- }, [authoritiesPromise]);
-
- // Memoize the columns so they don't re-render on every render
- const columns = React.useMemo(() => authorities_columns_def, []);
-
- /**
- * This component can render either a faceted filter or a search filter based on the `options` prop.
- *
- * @prop options - An array of objects, each representing a filter option. If provided, a faceted filter is rendered. If not, a search filter is rendered.
- *
- * Each `option` object has the following properties:
- * @prop {string} label - The label for the filter option.
- * @prop {string} value - The value for the filter option.
- * @prop {React.ReactNode} [icon] - An optional icon to display next to the label.
- * @prop {boolean} [withCount] - An optional boolean to display the count of the filter option.
- */
- const filterFields: DataTableFilterField[] = [
- {
- label: "Name",
- id: "name",
- placeholder: "Filter names...",
- },
- ];
-
- const { table } = useDataTable({
- data,
- columns,
- pageCount,
- filterFields,
- enableAdvancedFilter: enableAdvancedFilter,
- initialState: {
- sorting: [{ id: "name", desc: true }],
- columnPinning: { right: ["actions"] },
- },
- getRowId: (originalRow, index) => `${originalRow.name}-${index}`,
- shallow: false,
- clearOnDefault: true,
- });
-
- return (
-
-
-
-
-
- );
-}
diff --git a/src/components/contacts/contact-form.tsx b/src/components/contacts/contact-form.tsx
deleted file mode 100644
index 29b070c..0000000
--- a/src/components/contacts/contact-form.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-"use client";
-
-import { zodResolver } from "@hookform/resolvers/zod";
-import { useRouter, useSearchParams } from "next/navigation";
-import React from "react";
-import { useForm } from "react-hook-form";
-
-import AccountSelectField from "@/components/accounts/account-id-select";
-import ContactStatusSelect from "@/components/contacts/contact-status-select";
-import { Heading } from "@/components/heading";
-import {
- ExtInputField,
- ExtTextAreaField,
- FormProps,
- SubmitButton,
-} from "@/components/ui/ext-form";
-import { Form } from "@/components/ui/form";
-import { Separator } from "@/components/ui/separator";
-import { saveOrUpdateContact } from "@/lib/actions/contacts.action";
-import { deobfuscateToNumber } from "@/lib/endecode";
-import { validateForm } from "@/lib/validator";
-import { ConTactDTO, ContactDTOSchema } from "@/types/contacts";
-
-import { Button } from "../ui/button";
-
-export const ContactForm = ({ initialData }: FormProps) => {
- const router = useRouter();
-
- let accountId: number | null = null;
- let accountName: string | null = null;
- const searchParams = useSearchParams();
- const accountIdBase64 = searchParams.get("accountId");
-
- if (accountIdBase64 !== null) {
- accountId = deobfuscateToNumber(accountIdBase64);
- accountName = searchParams.get("accountName");
- }
-
- const form = useForm({
- resolver: zodResolver(ContactDTOSchema),
- defaultValues: {
- ...initialData,
- accountId: accountId,
- accountName: accountName,
- },
- });
-
- async function onSubmit(contact: ConTactDTO) {
- // contact.accountId = 1;
- if (validateForm(contact, ContactDTOSchema, form)) {
- await saveOrUpdateContact(isEdit, contact);
- }
- }
-
- const isEdit = !!initialData;
- const title = isEdit
- ? `Edit contact ${initialData?.firstName} ${initialData?.lastName}`
- : "Create contact";
- const description = isEdit ? "Edit contact" : "Add a new contact";
- const submitText = isEdit ? "Save changes" : "Create";
- const submitTextWhileLoading = isEdit ? "Saving changes ..." : "Creating ...";
-
- return (
-
- );
-};
-
-export default ContactForm;
diff --git a/src/components/contacts/contact-status-select.tsx b/src/components/contacts/contact-status-select.tsx
deleted file mode 100644
index 7dec566..0000000
--- a/src/components/contacts/contact-status-select.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from "react";
-
-import ValuesQuerySelect from "@/components/shared/values-query-select";
-import { findContactStatuses } from "@/lib/actions/contacts.action";
-import { EntityValueDefinition } from "@/types/commons";
-import { FormFieldProps } from "@/types/ui-components";
-
-const ContactStatusSelect = ({ form, required }: FormFieldProps) => {
- return (
-
- form={form}
- queryName="contactStatuses"
- fieldName="status"
- fieldLabel="Status"
- fetchDataFn={findContactStatuses}
- valueKey="value"
- renderTooltip={(entityValueDef: EntityValueDefinition) =>
- `${entityValueDef.description}`
- }
- renderOption={(entityValueDef: EntityValueDefinition) =>
- `${entityValueDef.value}`
- }
- required={required}
- placeholder="Select status"
- noDataMessage="No status found"
- searchPlaceholder="Search status..."
- />
- );
-};
-
-export default ContactStatusSelect;
diff --git a/src/components/contacts/contact-table-cell-action.tsx b/src/components/contacts/contact-table-cell-action.tsx
deleted file mode 100644
index 9fd3ff5..0000000
--- a/src/components/contacts/contact-table-cell-action.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-"use client";
-// import { AlertModal } from '@/components/modal/alert-modal';
-import { Row } from "@tanstack/react-table";
-import { Edit, MoreHorizontal } from "lucide-react";
-import { useRouter } from "next/navigation";
-
-import { Button } from "@/components/ui/button";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import { obfuscate } from "@/lib/endecode";
-import { ConTactDTO } from "@/types/contacts";
-
-export function DataTableRowActions({ row }: { row: Row }) {
- const router = useRouter();
-
- return (
- <>
-
-
-
-
-
- Actions
-
-
- router.push(`/portal/contacts/${obfuscate(row.original.id)}/edit`)
- }
- >
- Update
-
- {/* setOpen(true)}>*/}
- {/* Delete*/}
- {/**/}
-
-
- >
- );
-}
diff --git a/src/components/contacts/contact-table-columns.tsx b/src/components/contacts/contact-table-columns.tsx
deleted file mode 100644
index c43e0bb..0000000
--- a/src/components/contacts/contact-table-columns.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-"use client";
-
-import { ColumnDef } from "@tanstack/react-table";
-import Link from "next/link";
-
-import { DataTableRowActions } from "@/components/contacts/contact-table-cell-action";
-import { Button } from "@/components/ui/button";
-import { Checkbox } from "@/components/ui/checkbox";
-import { DataTableColumnHeader } from "@/components/ui/table/data-table-column-header";
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from "@/components/ui/tooltip";
-import { formatDateTime, formatDateTimeDistanceToNow } from "@/lib/datetime";
-import { obfuscate } from "@/lib/endecode";
-import { ConTactDTO } from "@/types/contacts";
-
-export const contacts_columns_def: ColumnDef[] = [
- {
- id: "select",
- header: ({ table }) => (
-
- table.toggleAllPageRowsSelected(!!value)
- }
- aria-label="Select all"
- className="translate-y-[2px]"
- />
- ),
- cell: ({ row }) => (
- row.toggleSelected(!!value)}
- aria-label="Select row"
- className="translate-y-[2px]"
- />
- ),
- enableSorting: false,
- enableHiding: false,
- },
- {
- accessorFn: (row) => `${row.firstName} ${row.lastName}`,
- accessorKey: "name",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => (
-
-
-
- ),
- },
- {
- accessorKey: "email",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => {
- return {row.getValue("email")}
;
- },
- filterFn: (row, id, value) => {
- return value.includes(row.getValue(id));
- },
- },
- {
- accessorKey: "status",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => {row.getValue("status")}
,
- },
- {
- accessorKey: "createdAt",
- header: ({ column }) => (
-
- ),
- cell: ({ row }) => {
- const field = new Date(row.getValue("createdAt"));
- return (
-
-
-
- {formatDateTimeDistanceToNow(field)}
-
-
- {formatDateTime(field)}
-
-
-
- );
- },
- },
- {
- id: "actions",
- cell: ({ row }) => ,
- },
-];
diff --git a/src/components/contacts/contact-table-toolbar-actions.tsx b/src/components/contacts/contact-table-toolbar-actions.tsx
deleted file mode 100644
index ee9f42d..0000000
--- a/src/components/contacts/contact-table-toolbar-actions.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-"use client";
-
-import { type Table } from "@tanstack/react-table";
-import { Download } from "lucide-react";
-
-import { Button } from "@/components/ui/button";
-import { exportTableToCSV } from "@/lib/export";
-import { ConTactDTO } from "@/types/contacts";
-
-interface ContactsTableToolbarActionsProps {
- table: Table;
-}
-
-export function ContactsTableToolbarActions({
- table,
-}: ContactsTableToolbarActionsProps) {
- return (
-
-
-
- );
-}
diff --git a/src/components/contacts/contact-table.tsx b/src/components/contacts/contact-table.tsx
deleted file mode 100644
index 0bf05eb..0000000
--- a/src/components/contacts/contact-table.tsx
+++ /dev/null
@@ -1,111 +0,0 @@
-"use client";
-
-import React, { useEffect, useState } from "react";
-
-import { contacts_columns_def } from "@/components/contacts/contact-table-columns";
-import { ContactsTableToolbarActions } from "@/components/contacts/contact-table-toolbar-actions";
-import { Skeleton } from "@/components/ui/skeleton";
-import { DataTable } from "@/components/ui/table/data-table";
-import { DataTableToolbar } from "@/components/ui/table/data-table-toolbar";
-import { useDataTable } from "@/hooks/use-data-table";
-import { useFetchData } from "@/hooks/use-fetch-data-values";
-import {
- findContactStatuses,
- searchContacts,
-} from "@/lib/actions/contacts.action";
-import { ConTactDTO } from "@/types/contacts";
-import { DataTableFilterField } from "@/types/table";
-
-interface ContactsTableProps {
- contactPromise: ReturnType;
- enableAdvancedFilter: boolean;
-}
-
-export function ContactsTable({
- contactPromise,
- enableAdvancedFilter = false,
-}: ContactsTableProps) {
- const [data, setData] = useState>([]);
- const [pageCount, setPageCount] = useState(0);
- const [loading, setLoading] = useState(true);
-
- // Fetch data using useEffect
- useEffect(() => {
- // Define async function to handle the promise
- const fetchContacts = async () => {
- try {
- setLoading(true);
- const pageResult = await contactPromise;
- setData(pageResult?.content);
- setPageCount(pageResult?.totalPages);
- } finally {
- setLoading(false);
- }
- };
-
- fetchContacts(); // Call the async function
- }, []); // Empty dependency array ensures it runs only once when component mounts
-
- // Memoize the columns so they don't re-render on every render
- const columns = React.useMemo(() => contacts_columns_def, []);
-
- const contactStatuses = useFetchData(findContactStatuses);
-
- /**
- * This component can render either a faceted filter or a search filter based on the `options` prop.
- *
- * @prop options - An array of objects, each representing a filter option. If provided, a faceted filter is rendered. If not, a search filter is rendered.
- *
- * Each `option` object has the following properties:
- * @prop {string} label - The label for the filter option.
- * @prop {string} value - The value for the filter option.
- * @prop {React.ReactNode} [icon] - An optional icon to display next to the label.
- * @prop {boolean} [withCount] - An optional boolean to display the count of the filter option.
- */
- const filterFields: DataTableFilterField[] = [
- {
- label: "First Name",
- id: "firstName",
- placeholder: "Filter names...",
- },
- // {
- // label: "Status",
- // id: "status",
- // options: contactStatuses.map((status) => ({
- // label: status,
- // value: status,
- // icon: undefined,
- // withCount: true,
- // })),
- // },
- ];
-
- const { table } = useDataTable({
- data,
- columns,
- pageCount,
- filterFields,
- enableAdvancedFilter: enableAdvancedFilter,
- initialState: {
- sorting: [{ id: "createdAt", desc: true }],
- columnPinning: { right: ["actions"] },
- },
- getRowId: (originalRow, index) => `${originalRow.id}-${index}`,
- shallow: false,
- clearOnDefault: true,
- });
-
- if (loading)
- return (
-
-
-
- );
- return (
-
-
-
-
-
- );
-}
diff --git a/src/components/contacts/contact-view.tsx b/src/components/contacts/contact-view.tsx
deleted file mode 100644
index 3a0a9af..0000000
--- a/src/components/contacts/contact-view.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-"use client";
-
-import { ChevronLeft, ChevronRight, Edit } from "lucide-react";
-import { usePathname, useRouter } from "next/navigation";
-import React, { useState } from "react";
-
-import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
-import { Card, CardContent } from "@/components/ui/card";
-import { ViewProps } from "@/components/ui/ext-form";
-import {
- findNextContact,
- findPreviousContact,
-} from "@/lib/actions/contacts.action";
-import { obfuscate } from "@/lib/endecode";
-import { navigateToRecord } from "@/lib/navigation-record";
-import { ConTactDTO } from "@/types/contacts";
-
-export const ContactView: React.FC> = ({
- entity,
-}: ViewProps) => {
- const router = useRouter();
- const pathname = usePathname();
-
- const [contact, setContact] = useState(entity);
-
- const navigateToPreviousRecord = async () => {
- const previousContact = await navigateToRecord(
- findPreviousContact,
- "You reach the first record",
- contact.id!,
- );
- setContact(previousContact);
- };
-
- const navigateToNextRecord = async () => {
- const nextContact = await navigateToRecord(
- findNextContact,
- "You reach the last record",
- contact.id!,
- );
- setContact(nextContact);
- };
-
- return (
-
-
-
-
- {contact.firstName} {contact.lastName}
-
-
-
-
-
-
-
-
- Account: {contact.accountName}
-
-
Email: {contact.email}
-
Address Line: {contact.address}
-
City: {contact.city}
-
State: {contact.state}
-
Country: {contact.country}
-
- Status: {contact.status}
-
-
-
-
-
- );
-};
diff --git a/src/components/dashboard/team-unresolved-tickets-priority-distribution.tsx b/src/components/dashboard/team-unresolved-tickets-priority-distribution.tsx
index d7330ec..c4b3959 100644
--- a/src/components/dashboard/team-unresolved-tickets-priority-distribution.tsx
+++ b/src/components/dashboard/team-unresolved-tickets-priority-distribution.tsx
@@ -67,13 +67,13 @@ const TeamUnresolvedTicketsPriorityDistributionChart = () => {
Unresolved Tickets by Team
-
+
{loading ? (
-
+
) : chartData.length === 0 ? (
-
+
No data available to display.
) : (
diff --git a/src/components/shared/table-entity-delete-dialog.tsx b/src/components/shared/table-entity-delete-dialog.tsx
deleted file mode 100644
index f6afd13..0000000
--- a/src/components/shared/table-entity-delete-dialog.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
-"use client";
-
-import { TrashIcon } from "@radix-ui/react-icons";
-import { type Row } from "@tanstack/react-table";
-import * as React from "react";
-import { toast } from "sonner";
-
-import { Icons } from "@/components/icons";
-import { Button } from "@/components/ui/button";
-import {
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "@/components/ui/dialog";
-import {
- Drawer,
- DrawerClose,
- DrawerContent,
- DrawerDescription,
- DrawerFooter,
- DrawerHeader,
- DrawerTitle,
- DrawerTrigger,
-} from "@/components/ui/drawer";
-import { useMediaQuery } from "@/hooks/use-media-query";
-
-export interface RowEntitiesDeleteDialogProps<
- TEntity extends Record
,
-> extends React.ComponentPropsWithoutRef {
- entities: Row["original"][];
- showTrigger?: boolean;
- onSuccess?: () => void;
- deleteEntitiesFn: (ids: number[]) => Promise; // Pass a function to delete entities
- entityName: string; // Pass the entity name for the UI text
-}
-
-export function EntitiesDeleteDialog>({
- entities,
- showTrigger = true,
- onSuccess,
- deleteEntitiesFn,
- entityName,
- ...props
-}: RowEntitiesDeleteDialogProps) {
- const [isDeletePending, startDeleteTransition] = React.useTransition();
- const isDesktop = useMediaQuery("(min-width: 640px)");
-
- function onDelete() {
- startDeleteTransition(async () => {
- // Perform a runtime check to ensure each entity has an "id" field
- const ids = entities.map((entity) => {
- if ("id" in entity && typeof entity.id === "number") {
- return entity.id; // Return the ID if it exists and is a string
- } else {
- throw new Error(`Entity does not have a valid "id" field`);
- }
- });
-
- try {
- await deleteEntitiesFn(ids);
- } catch (error) {
- toast.error(
- `Cannot delete ${entityName}${entities.length > 1 ? "s" : ""}`,
- );
- return;
- }
-
- props.onOpenChange?.(false);
- toast.success(
- `${entityName}${entities.length > 1 ? "s are" : " is"} deleted`,
- );
- onSuccess?.();
- });
- }
-
- if (isDesktop) {
- return (
-
- );
- }
-
- return (
-
- {showTrigger ? (
-
-
-
- ) : null}
-
-
- Are you absolutely sure?
-
- This action cannot be undone. This will permanently delete your{" "}
- {entities.length}
- {entities.length === 1 ? ` ${entityName}` : ` ${entityName}s`} from
- our servers.
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/components/teams/team-dashboard-kpis.tsx b/src/components/teams/team-dashboard-kpis.tsx
index 9c49bf5..ce2ed04 100644
--- a/src/components/teams/team-dashboard-kpis.tsx
+++ b/src/components/teams/team-dashboard-kpis.tsx
@@ -45,7 +45,7 @@ const TeamDashboardTopSection = ({ teamId }: { teamId: number }) => {
{
title: "Total Tickets",
description: "All tickets received",
- value: totalTickets,
+ value: totalTickets ?? 0,
color: "text-gray-700 dark:text-gray-300",
link: "#",
tooltip: "View all team tickets.",
@@ -53,7 +53,7 @@ const TeamDashboardTopSection = ({ teamId }: { teamId: number }) => {
{
title: "Pending Tickets",
description: "Tickets yet to be addressed",
- value: pendingTickets,
+ value: pendingTickets ?? 0,
color: "text-yellow-500",
link: "#",
tooltip: "View tickets that are still pending.",
@@ -61,7 +61,7 @@ const TeamDashboardTopSection = ({ teamId }: { teamId: number }) => {
{
title: "Completed Tickets",
description: "Successfully resolved tickets",
- value: completedTickets,
+ value: completedTickets ?? 0,
color: "text-green-500",
link: "#",
tooltip: "View tickets that have been resolved.",
@@ -69,7 +69,7 @@ const TeamDashboardTopSection = ({ teamId }: { teamId: number }) => {
{
title: "Overdue Tickets",
description: "Tickets past their deadline",
- value: overDueTickets,
+ value: overDueTickets ?? 0,
color: "text-red-500",
link: "#",
tooltip: "View overdue tickets that need attention.",
diff --git a/src/components/ui/table/data-table-advanced-toolbar.tsx b/src/components/ui/table/data-table-advanced-toolbar.tsx
deleted file mode 100644
index 9368247..0000000
--- a/src/components/ui/table/data-table-advanced-toolbar.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-"use client";
-
-import { type Table } from "@tanstack/react-table";
-import * as React from "react";
-
-import { DataTableFilterList } from "@/components/ui/table/data-table-filter-list";
-import { DataTableSortList } from "@/components/ui/table/data-table-sort-list";
-import { DataTableViewOptions } from "@/components/ui/table/data-table-view-options";
-import { cn } from "@/lib/utils";
-import { DataTableAdvancedFilterField } from "@/types/table";
-
-interface DataTableAdvancedToolbarProps
- extends React.HTMLAttributes {
- /**
- * The table instance returned from useDataTable hook with pagination, sorting, filtering, etc.
- * @type Table
- */
- table: Table;
-
- /**
- * An array of filter field configurations for the data table.
- * @type DataTableAdvancedFilterField[]
- * @example
- * const filterFields = [
- * {
- * id: 'name',
- * label: 'Name',
- * type: 'text',
- * placeholder: 'Filter by name...'
- * },
- * {
- * id: 'status',
- * label: 'Status',
- * type: 'select',
- * options: [
- * { label: 'Active', value: 'active', count: 10 },
- * { label: 'Inactive', value: 'inactive', count: 5 }
- * ]
- * }
- * ]
- */
- filterFields: DataTableAdvancedFilterField[];
-
- /**
- * Debounce time (ms) for filter updates to enhance performance during rapid input.
- * @default 300
- */
- debounceMs?: number;
-
- /**
- * Shallow mode keeps query states client-side, avoiding server calls.
- * Setting to `false` triggers a network request with the updated querystring.
- * @default true
- */
- shallow?: boolean;
-}
-
-export function DataTableAdvancedToolbar({
- table,
- filterFields = [],
- debounceMs = 300,
- shallow = true,
- children,
- className,
- ...props
-}: DataTableAdvancedToolbarProps) {
- return (
-
-
-
-
-
-
- {children}
-
-
-
- );
-}
diff --git a/src/components/ui/table/data-table-column-header.tsx b/src/components/ui/table/data-table-column-header.tsx
deleted file mode 100644
index cae219e..0000000
--- a/src/components/ui/table/data-table-column-header.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-"use client";
-
-import { SelectIcon } from "@radix-ui/react-select";
-import { type Column } from "@tanstack/react-table";
-import { ArrowDown, ArrowUp, ChevronsUpDown, EyeOff } from "lucide-react";
-
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
-} from "@/components/ui/select";
-import { cn } from "@/lib/utils";
-
-interface DataTableColumnHeaderProps
- extends React.HTMLAttributes {
- column: Column;
- title: string;
-}
-
-export function DataTableColumnHeader({
- column,
- title,
- className,
-}: DataTableColumnHeaderProps) {
- if (!column.getCanSort() && !column.getCanHide()) {
- return {title}
;
- }
-
- const ascValue = `${column.id}-asc`;
- const descValue = `${column.id}-desc`;
- const hideValue = `${column.id}-hide`;
-
- return (
-
-
-
- );
-}
diff --git a/src/components/ui/table/data-table-faceted-filter.tsx b/src/components/ui/table/data-table-faceted-filter.tsx
deleted file mode 100644
index 0d88c0b..0000000
--- a/src/components/ui/table/data-table-faceted-filter.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-import type { Column } from "@tanstack/react-table";
-import { Check, PlusCircle } from "lucide-react";
-
-import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
- CommandSeparator,
-} from "@/components/ui/command";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { Separator } from "@/components/ui/separator";
-import { cn } from "@/lib/utils";
-import { Option } from "@/types/table";
-
-interface DataTableFacetedFilterProps {
- column?: Column;
- title?: string;
- options: Option[];
-}
-
-export function DataTableFacetedFilter({
- column,
- title,
- options,
-}: DataTableFacetedFilterProps) {
- const unknownValue = column?.getFilterValue();
- const selectedValues = new Set(
- Array.isArray(unknownValue) ? unknownValue : [],
- );
-
- return (
-
-
-
-
-
-
-
-
- No results found.
-
- {options.map((option) => {
- const isSelected = selectedValues.has(option.value);
-
- return (
- {
- if (isSelected) {
- selectedValues.delete(option.value);
- } else {
- selectedValues.add(option.value);
- }
- const filterValues = Array.from(selectedValues);
- column?.setFilterValue(
- filterValues.length ? filterValues : undefined,
- );
- }}
- >
-
-
-
- {option.icon && (
-
- )}
- {option.label}
- {option.count && (
-
- {option.count}
-
- )}
-
- );
- })}
-
- {selectedValues.size > 0 && (
- <>
-
-
- column?.setFilterValue(undefined)}
- className="justify-center text-center"
- >
- Clear filters
-
-
- >
- )}
-
-
-
-
- );
-}
diff --git a/src/components/ui/table/data-table-filter-list.tsx b/src/components/ui/table/data-table-filter-list.tsx
deleted file mode 100644
index 6df83da..0000000
--- a/src/components/ui/table/data-table-filter-list.tsx
+++ /dev/null
@@ -1,767 +0,0 @@
-"use client";
-
-import { type Table } from "@tanstack/react-table";
-import {
- CalendarIcon,
- Check,
- ChevronsUpDown,
- GripVertical,
- ListFilter,
- Trash2,
-} from "lucide-react";
-import { customAlphabet } from "nanoid";
-import { parseAsStringEnum, useQueryState } from "nuqs";
-import * as React from "react";
-
-import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
-import { Calendar } from "@/components/ui/calendar";
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from "@/components/ui/command";
-import {
- FacetedFilter,
- FacetedFilterContent,
- FacetedFilterEmpty,
- FacetedFilterGroup,
- FacetedFilterInput,
- FacetedFilterItem,
- FacetedFilterList,
- FacetedFilterTrigger,
-} from "@/components/ui/faceted-filter";
-import { Input } from "@/components/ui/input";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
-import {
- Sortable,
- SortableDragHandle,
- SortableItem,
-} from "@/components/ui/sortable";
-import { dataTableConfig } from "@/config/data-table";
-import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
-import { getDefaultFilterOperator, getFilterOperators } from "@/lib/data-table";
-import { getFiltersStateParser } from "@/lib/parsers";
-import { cn, formatDate } from "@/lib/utils";
-import {
- DataTableAdvancedFilterField,
- Filter,
- FilterOperator,
- JoinOperator,
- StringKeyOf,
-} from "@/types/table";
-
-interface DataTableFilterListProps {
- table: Table;
- filterFields: DataTableAdvancedFilterField[];
- debounceMs: number;
- shallow?: boolean;
-}
-
-export function DataTableFilterList({
- table,
- filterFields,
- debounceMs,
- shallow,
-}: DataTableFilterListProps) {
- const id = React.useId();
- const [filters, setFilters] = useQueryState(
- "filters",
- getFiltersStateParser(table.getRowModel().rows[0]?.original)
- .withDefault([])
- .withOptions({
- clearOnDefault: true,
- shallow,
- }),
- );
-
- const [joinOperator, setJoinOperator] = useQueryState(
- "joinOperator",
- parseAsStringEnum(["and", "or"]).withDefault("and").withOptions({
- clearOnDefault: true,
- shallow,
- }),
- );
-
- const debouncedSetFilters = useDebouncedCallback(setFilters, debounceMs);
-
- function addFilter() {
- const filterField = filterFields[0];
-
- if (!filterField) return;
-
- void setFilters([
- ...filters,
- {
- id: filterField.id,
- value: "",
- type: filterField.type,
- operator: getDefaultFilterOperator(filterField.type),
- rowId: customAlphabet(
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
- 6,
- )(),
- },
- ]);
- }
-
- function updateFilter({
- rowId,
- field,
- debounced = false,
- }: {
- rowId: string;
- field: Partial>;
- debounced?: boolean;
- }) {
- const updateFunction = debounced ? debouncedSetFilters : setFilters;
- updateFunction((prevFilters) => {
- const updatedFilters = prevFilters.map((filter) => {
- if (filter.rowId === rowId) {
- const updatedFilter = { ...filter, ...field };
- if ("type" in field) {
- updatedFilter.value = "";
- }
- if (field.operator === "isEmpty" || field.operator === "isNotEmpty") {
- updatedFilter.value = "";
- }
- return updatedFilter;
- }
- return filter;
- });
-
- return updatedFilters;
- });
- }
-
- function removeFilter(rowId: string) {
- const updatedFilters = filters.filter((filter) => filter.rowId !== rowId);
- void setFilters(updatedFilters);
- }
-
- function moveFilter(activeIndex: number, overIndex: number) {
- void setFilters((prevFilters) => {
- const newFilters = [...prevFilters];
- const [removed] = newFilters.splice(activeIndex, 1);
- if (!removed) return prevFilters;
- newFilters.splice(overIndex, 0, removed);
- return newFilters;
- });
- }
-
- function renderFilterInput({
- filter,
- inputId,
- }: {
- filter: Filter;
- inputId: string;
- }) {
- const filterField = filterFields.find((f) => f.id === filter.id);
-
- if (!filterField) return null;
-
- if (filter.operator === "isEmpty" || filter.operator === "isNotEmpty") {
- return (
-
- );
- }
-
- switch (filter.type) {
- case "text":
- case "number":
- return (
-
- updateFilter({
- rowId: filter.rowId,
- field: { value: event.target.value },
- debounced: true,
- })
- }
- />
- );
- case "select":
- return (
-
-
-
-
-
-
-
- No options found.
-
- {filterField?.options?.map((option) => (
- {
- updateFilter({ rowId: filter.rowId, field: { value } });
- setTimeout(() => {
- document.getElementById(inputId)?.click();
- }, 0);
- }}
- >
- {option.icon && (
-
- )}
- {option.label}
- {option.count && (
-
- {option.count}
-
- )}
-
- ))}
-
-
-
-
- );
- case "multi-select":
- const selectedValues = new Set(
- Array.isArray(filter.value) ? filter.value : [],
- );
-
- return (
-
-
-
-
-
-
-
- No options found.
-
- {filterField?.options?.map((option) => (
- {
- const currentValue = Array.isArray(filter.value)
- ? filter.value
- : [];
- const newValue = currentValue.includes(value)
- ? currentValue.filter((v) => v !== value)
- : [...currentValue, value];
- updateFilter({
- rowId: filter.rowId,
- field: { value: newValue },
- });
- }}
- >
- {option.icon && (
-
- )}
- {option.label}
- {option.count && (
-
- {option.count}
-
- )}
-
- ))}
-
-
-
-
- );
- case "date":
- const dateValue = Array.isArray(filter.value)
- ? filter.value.filter(Boolean)
- : [filter.value, filter.value].filter(Boolean);
-
- const displayValue =
- filter.operator === "isBetween" && dateValue.length === 2
- ? `${formatDate(dateValue[0] ?? new Date())} - ${formatDate(
- dateValue[1] ?? new Date(),
- )}`
- : dateValue[0]
- ? formatDate(dateValue[0])
- : "Pick a date";
-
- return (
-
-
-
-
-
- {filter.operator === "isBetween" ? (
- {
- updateFilter({
- rowId: filter.rowId,
- field: {
- value: date
- ? [
- date.from?.toISOString() ?? "",
- date.to?.toISOString() ?? "",
- ]
- : [],
- },
- });
- }}
- initialFocus
- numberOfMonths={1}
- />
- ) : (
- {
- updateFilter({
- rowId: filter.rowId,
- field: { value: date?.toISOString() ?? "" },
- });
-
- setTimeout(() => {
- document.getElementById(inputId)?.click();
- }, 0);
- }}
- initialFocus
- />
- )}
-
-
- );
- case "boolean": {
- if (Array.isArray(filter.value)) return null;
-
- return (
-
- );
- }
- default:
- return null;
- }
- }
-
- return (
- ({ id: item.rowId }))}
- onMove={({ activeIndex, overIndex }) =>
- moveFilter(activeIndex, overIndex)
- }
- overlay={
-
- }
- >
-
-
-
-
- 0 ? "gap-3.5" : "gap-2",
- )}
- >
- {filters.length > 0 ? (
- Filters
- ) : (
-
-
No filters applied
-
- Add filters to refine your results.
-
-
- )}
-
- {filters.map((filter, index) => {
- const filterId = `${id}-filter-${filter.rowId}`;
- const joinOperatorListboxId = `${filterId}-join-operator-listbox`;
- const fieldListboxId = `${filterId}-field-listbox`;
- const fieldTriggerId = `${filterId}-field-trigger`;
- const operatorListboxId = `${filterId}-operator-listbox`;
- const inputId = `${filterId}-input`;
-
- return (
-
-
-
- {index === 0 ? (
-
- Where
-
- ) : index === 1 ? (
-
- ) : (
-
- {joinOperator}
-
- )}
-
-
-
-
-
-
- document.getElementById(fieldTriggerId)?.focus()
- }
- >
-
-
-
- No fields found.
-
- {filterFields.map((field) => (
- {
- const column = filterFields.find(
- (col) => col.id === value,
- );
- if (column) {
- updateFilter({
- rowId: filter.rowId,
- field: {
- id: value as StringKeyOf,
- type: column.type,
- operator: getDefaultFilterOperator(
- column.type,
- ),
- },
- });
- }
- }}
- >
-
- {field.label}
-
-
-
- ))}
-
-
-
-
-
-
-
- {renderFilterInput({ filter, inputId })}
-
-
-
-
-
-
-
- );
- })}
-
-
-
- {filters.length > 0 ? (
-
- ) : null}
-
-
-
-
- );
-}
diff --git a/src/components/ui/table/data-table-pagination.tsx b/src/components/ui/table/data-table-pagination.tsx
deleted file mode 100644
index b28f1da..0000000
--- a/src/components/ui/table/data-table-pagination.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { type Table } from "@tanstack/react-table";
-import {
- ChevronLeft,
- ChevronRight,
- ChevronsLeft,
- ChevronsRight,
-} from "lucide-react";
-
-import { Button } from "@/components/ui/button";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
-
-interface DataTablePaginationProps {
- table: Table;
- pageSizeOptions?: number[];
-}
-
-export function DataTablePagination({
- table,
- pageSizeOptions = [10, 20, 30, 40, 50],
-}: DataTablePaginationProps) {
- return (
-
-
- {table.getFilteredSelectedRowModel().rows.length} of{" "}
- {table.getFilteredRowModel().rows.length} row(s) selected.
-
-
-
-
Rows per page
-
-
-
- Page {table.getState().pagination.pageIndex + 1} of{" "}
- {table.getPageCount()}
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/components/ui/table/data-table-skeleton.tsx b/src/components/ui/table/data-table-skeleton.tsx
deleted file mode 100644
index 73be90d..0000000
--- a/src/components/ui/table/data-table-skeleton.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-import { Skeleton } from "@/components/ui/skeleton";
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "@/components/ui/table";
-import { cn } from "@/lib/utils";
-
-interface DataTableSkeletonProps extends React.HTMLAttributes {
- /**
- * The number of columns in the table.
- * @type number
- */
- columnCount: number;
-
- /**
- * The number of rows in the table.
- * @default 10
- * @type number | undefined
- */
- rowCount?: number;
-
- /**
- * The number of searchable columns in the table.
- * @default 0
- * @type number | undefined
- */
- searchableColumnCount?: number;
-
- /**
- * The number of filterable columns in the table.
- * @default 0
- * @type number | undefined
- */
- filterableColumnCount?: number;
-
- /**
- * Flag to show the table view options.
- * @default undefined
- * @type boolean | undefined
- */
- showViewOptions?: boolean;
-
- /**
- * The width of each cell in the table.
- * The length of the array should be equal to the columnCount.
- * Any valid CSS width value is accepted.
- * @default ["auto"]
- * @type string[] | undefined
- */
- cellWidths?: string[];
-
- /**
- * Flag to show the pagination bar.
- * @default true
- * @type boolean | undefined
- */
- withPagination?: boolean;
-
- /**
- * Flag to prevent the table cells from shrinking.
- * @default false
- * @type boolean | undefined
- */
- shrinkZero?: boolean;
-}
-
-export function DataTableSkeleton(props: DataTableSkeletonProps) {
- const {
- columnCount,
- rowCount = 10,
- searchableColumnCount = 0,
- filterableColumnCount = 0,
- showViewOptions = true,
- cellWidths = ["auto"],
- withPagination = true,
- shrinkZero = false,
- className,
- ...skeletonProps
- } = props;
-
- return (
-
-
-
- {searchableColumnCount > 0
- ? Array.from({ length: searchableColumnCount }).map((_, i) => (
-
- ))
- : null}
- {filterableColumnCount > 0
- ? Array.from({ length: filterableColumnCount }).map((_, i) => (
-
- ))
- : null}
-
- {showViewOptions ? (
-
- ) : null}
-
-
-
-
- {Array.from({ length: 1 }).map((_, i) => (
-
- {Array.from({ length: columnCount }).map((_, j) => (
-
-
-
- ))}
-
- ))}
-
-
- {Array.from({ length: rowCount }).map((_, i) => (
-
- {Array.from({ length: columnCount }).map((_, j) => (
-
-
-
- ))}
-
- ))}
-
-
-
- {withPagination ? (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ) : null}
-
- );
-}
diff --git a/src/components/ui/table/data-table-sort-list.tsx b/src/components/ui/table/data-table-sort-list.tsx
deleted file mode 100644
index 6cb4cce..0000000
--- a/src/components/ui/table/data-table-sort-list.tsx
+++ /dev/null
@@ -1,348 +0,0 @@
-"use client";
-
-import type { SortDirection, Table } from "@tanstack/react-table";
-import {
- ArrowDownUp,
- Check,
- ChevronsUpDown,
- GripVertical,
- Trash2,
-} from "lucide-react";
-import { useQueryState } from "nuqs";
-import * as React from "react";
-
-import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from "@/components/ui/command";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
-import {
- Sortable,
- SortableDragHandle,
- SortableItem,
-} from "@/components/ui/sortable";
-import { dataTableConfig } from "@/config/data-table";
-import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
-import { getSortingStateParser } from "@/lib/parsers";
-import { cn, toSentenceCase } from "@/lib/utils";
-import {
- ExtendedColumnSort,
- ExtendedSortingState,
- StringKeyOf,
-} from "@/types/table";
-
-interface DataTableSortListProps {
- table: Table;
- debounceMs: number;
- shallow?: boolean;
-}
-
-export function DataTableSortList({
- table,
- debounceMs,
- shallow,
-}: DataTableSortListProps) {
- const id = React.useId();
-
- const initialSorting = (table.initialState.sorting ??
- []) as ExtendedSortingState;
-
- const [sorting, setSorting] = useQueryState(
- "sort",
- getSortingStateParser(table.getRowModel().rows[0]?.original)
- .withDefault(initialSorting)
- .withOptions({
- clearOnDefault: true,
- shallow,
- }),
- );
-
- const uniqueSorting = sorting.filter(
- (sort, index, self) => index === self.findIndex((t) => t.id === sort.id),
- );
-
- const debouncedSetSorting = useDebouncedCallback(setSorting, debounceMs);
-
- const sortableColumns = table
- .getAllColumns()
- .filter((column) => column.getCanSort())
- .map((column) => ({
- id: column.id,
- label: toSentenceCase(column.id),
- selected: sorting?.some((s) => s.id === column.id),
- }));
-
- function addSort() {
- const firstAvailableColumn = sortableColumns.find(
- (column) => !sorting.some((s) => s.id === column.id),
- );
- if (!firstAvailableColumn) return;
-
- void setSorting([
- ...sorting,
- {
- id: firstAvailableColumn.id as StringKeyOf,
- desc: false,
- },
- ]);
- }
-
- function updateSort({
- field,
- index,
- debounced = false,
- }: {
- field: Partial>;
- index: number;
- debounced?: boolean;
- }) {
- const updateFunction = debounced ? debouncedSetSorting : setSorting;
-
- updateFunction((prevSorting) => {
- if (!prevSorting) return prevSorting;
-
- const updatedSorting = prevSorting.map((sort, i) =>
- i === index ? { ...sort, ...field } : sort,
- );
- return updatedSorting;
- });
- }
-
- function removeSort(id: string) {
- void setSorting((prevSorting) =>
- prevSorting.filter((item) => item.id !== id),
- );
- }
-
- return (
-
-
-
-
-
-
- }
- >
-
-
-
-
- 0 ? "gap-3.5" : "gap-2",
- )}
- >
- {uniqueSorting.length > 0 ? (
- Sort by
- ) : (
-
-
No sorting applied
-
- Add sorting to organize your results.
-
-
- )}
-
-
- {uniqueSorting.map((sort, index) => {
- const sortId = `${id}-sort-${sort.id}`;
- const fieldListboxId = `${sortId}-field-listbox`;
- const fieldTriggerId = `${sortId}-field-trigger`;
- const directionListboxId = `${sortId}-direction-listbox`;
-
- return (
-
-
-
-
-
-
-
- document.getElementById(fieldTriggerId)?.focus()
- }
- >
-
-
-
- No fields found.
-
- {sortableColumns
- .filter(
- (column) =>
- !column.selected || column.id === sort.id,
- )
- .map((column) => (
-
- updateSort({
- field: {
- id: value as StringKeyOf,
- },
- index,
- })
- }
- >
-
- {column.label}
-
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
- );
- })}
-
-
-
-
- {sorting.length > 0 ? (
-
- ) : null}
-
-
-
-
- );
-}
diff --git a/src/components/ui/table/data-table-toolbar.tsx b/src/components/ui/table/data-table-toolbar.tsx
deleted file mode 100644
index 031daa6..0000000
--- a/src/components/ui/table/data-table-toolbar.tsx
+++ /dev/null
@@ -1,119 +0,0 @@
-"use client";
-
-import type { Table } from "@tanstack/react-table";
-import { X } from "lucide-react";
-import * as React from "react";
-
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { DataTableFacetedFilter } from "@/components/ui/table/data-table-faceted-filter";
-import { DataTableViewOptions } from "@/components/ui/table/data-table-view-options";
-import { cn } from "@/lib/utils";
-import { DataTableFilterField } from "@/types/table";
-
-interface DataTableToolbarProps
- extends React.HTMLAttributes {
- table: Table;
- /**
- * An array of filter field configurations for the data table.
- * When options are provided, a faceted filter is rendered.
- * Otherwise, a search filter is rendered.
- *
- * @example
- * const filterFields = [
- * {
- * id: 'name',
- * label: 'Name',
- * placeholder: 'Filter by name...'
- * },
- * {
- * id: 'status',
- * label: 'Status',
- * options: [
- * { label: 'Active', value: 'active', icon: ActiveIcon, count: 10 },
- * { label: 'Inactive', value: 'inactive', icon: InactiveIcon, count: 5 }
- * ]
- * }
- * ]
- */
- filterFields?: DataTableFilterField[];
-}
-
-export function DataTableToolbar({
- table,
- filterFields = [],
- children,
- className,
- ...props
-}: DataTableToolbarProps) {
- const isFiltered = table.getState().columnFilters.length > 0;
-
- // Memoize computation of searchableColumns and filterableColumns
- const { searchableColumns, filterableColumns } = React.useMemo(() => {
- return {
- searchableColumns: filterFields.filter((field) => !field.options),
- filterableColumns: filterFields.filter((field) => field.options),
- };
- }, [filterFields]);
-
- return (
-
-
- {searchableColumns.length > 0 &&
- searchableColumns.map(
- (column) =>
- table.getColumn(column.id ? String(column.id) : "") && (
-
- table
- .getColumn(String(column.id))
- ?.setFilterValue(event.target.value)
- }
- className="h-8 w-40 lg:w-64"
- />
- ),
- )}
- {filterableColumns.length > 0 &&
- filterableColumns.map(
- (column) =>
- table.getColumn(column.id ? String(column.id) : "") && (
-
- ),
- )}
- {isFiltered && (
-
- )}
-
-
- {children}
-
-
-
- );
-}
diff --git a/src/components/ui/table/data-table-view-options.tsx b/src/components/ui/table/data-table-view-options.tsx
deleted file mode 100644
index 98a85a8..0000000
--- a/src/components/ui/table/data-table-view-options.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-"use client";
-
-import type { Table } from "@tanstack/react-table";
-import { Check, ChevronsUpDown, Settings2 } from "lucide-react";
-import * as React from "react";
-
-import { Button } from "@/components/ui/button";
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from "@/components/ui/command";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { cn, toSentenceCase } from "@/lib/utils";
-
-interface DataTableViewOptionsProps {
- table: Table;
-}
-
-export function DataTableViewOptions({
- table,
-}: DataTableViewOptionsProps) {
- const triggerRef = React.useRef(null);
-
- return (
-
-
-
-
- triggerRef.current?.focus()}
- >
-
-
-
- No columns found.
-
- {table
- .getAllColumns()
- .filter(
- (column) =>
- typeof column.accessorFn !== "undefined" &&
- column.getCanHide(),
- )
- .map((column) => {
- return (
-
- column.toggleVisibility(!column.getIsVisible())
- }
- >
-
- {toSentenceCase(column.id)}
-
-
-
- );
- })}
-
-
-
-
-
- );
-}
diff --git a/src/components/ui/table/data-table.tsx b/src/components/ui/table/data-table.tsx
deleted file mode 100644
index 1a92d94..0000000
--- a/src/components/ui/table/data-table.tsx
+++ /dev/null
@@ -1,112 +0,0 @@
-import { flexRender, type Table as TanstackTable } from "@tanstack/react-table";
-import * as React from "react";
-
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "@/components/ui/table";
-import { DataTablePagination } from "@/components/ui/table/data-table-pagination";
-import { getCommonPinningStyles } from "@/lib/data-table";
-import { cn } from "@/lib/utils";
-
-interface DataTableProps extends React.HTMLAttributes {
- /**
- * The table instance returned from useDataTable hook with pagination, sorting, filtering, etc.
- * @type TanstackTable
- */
- table: TanstackTable;
-
- /**
- * The floating bar to render at the bottom of the table on row selection.
- * @default null
- * @type React.ReactNode | null
- * @example floatingBar={}
- */
- floatingBar?: React.ReactNode | null;
-}
-
-export function DataTable({
- table,
- floatingBar = null,
- children,
- className,
- ...props
-}: DataTableProps) {
- return (
-
- {children}
-
-
-
- {table.getHeaderGroups().map((headerGroup) => (
-
- {headerGroup.headers.map((header) => {
- return (
-
- {header.isPlaceholder
- ? null
- : flexRender(
- header.column.columnDef.header,
- header.getContext(),
- )}
-
- );
- })}
-
- ))}
-
-
- {table.getRowModel().rows?.length ? (
- table.getRowModel().rows.map((row) => (
-
- {row.getVisibleCells().map((cell) => (
-
- {flexRender(
- cell.column.columnDef.cell,
- cell.getContext(),
- )}
-
- ))}
-
- ))
- ) : (
-
-
- No results.
-
-
- )}
-
-
-
-
-
- {table.getFilteredSelectedRowModel().rows.length > 0 && floatingBar}
-
-
- );
-}
diff --git a/src/config/data-table.ts b/src/config/data-table.ts
deleted file mode 100644
index 50c3423..0000000
--- a/src/config/data-table.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import { Pickaxe, SquareSquare } from "lucide-react";
-
-export type DataTableConfig = typeof dataTableConfig;
-
-export const dataTableConfig = {
- featureFlags: [
- {
- label: "Advanced table",
- value: "advancedTable" as const,
- icon: Pickaxe,
- tooltipTitle: "Toggle advanced table",
- tooltipDescription: "A filter and sort builder to filter and sort rows.",
- },
- {
- label: "Floating bar",
- value: "floatingBar" as const,
- icon: SquareSquare,
- tooltipTitle: "Toggle floating bar",
- tooltipDescription: "A floating bar that sticks to the top of the table.",
- },
- ],
- textOperators: [
- { label: "Contains", value: "iLike" as const },
- { label: "Does not contain", value: "notILike" as const },
- { label: "Is", value: "eq" as const },
- { label: "Is not", value: "ne" as const },
- { label: "Is empty", value: "isEmpty" as const },
- { label: "Is not empty", value: "isNotEmpty" as const },
- ],
- numericOperators: [
- { label: "Is", value: "eq" as const },
- { label: "Is not", value: "ne" as const },
- { label: "Is less than", value: "lt" as const },
- { label: "Is less than or equal to", value: "lte" as const },
- { label: "Is greater than", value: "gt" as const },
- { label: "Is greater than or equal to", value: "gte" as const },
- { label: "Is empty", value: "isEmpty" as const },
- { label: "Is not empty", value: "isNotEmpty" as const },
- ],
- dateOperators: [
- { label: "Is", value: "eq" as const },
- { label: "Is not", value: "ne" as const },
- { label: "Is before", value: "lt" as const },
- { label: "Is after", value: "gt" as const },
- { label: "Is on or before", value: "lte" as const },
- { label: "Is on or after", value: "gte" as const },
- { label: "Is between", value: "isBetween" as const },
- { label: "Is relative to today", value: "isRelativeToToday" as const },
- { label: "Is empty", value: "isEmpty" as const },
- { label: "Is not empty", value: "isNotEmpty" as const },
- ],
- selectOperators: [
- { label: "Is", value: "eq" as const },
- { label: "Is not", value: "ne" as const },
- { label: "Is empty", value: "isEmpty" as const },
- { label: "Is not empty", value: "isNotEmpty" as const },
- ],
- booleanOperators: [
- { label: "Is", value: "eq" as const },
- { label: "Is not", value: "ne" as const },
- ],
- joinOperators: [
- { label: "And", value: "and" as const },
- { label: "Or", value: "or" as const },
- ],
- sortOrders: [
- { label: "Asc", value: "asc" as const },
- { label: "Desc", value: "desc" as const },
- ],
- columnTypes: [
- "text",
- "number",
- "date",
- "boolean",
- "select",
- "multi-select",
- ] as const,
- globalOperators: [
- "iLike",
- "notILike",
- "eq",
- "ne",
- "isEmpty",
- "isNotEmpty",
- "lt",
- "lte",
- "gt",
- "gte",
- "isBetween",
- "isRelativeToToday",
- "and",
- "or",
- ] as const,
-};
diff --git a/src/hooks/use-data-table.ts b/src/hooks/use-data-table.ts
deleted file mode 100644
index 88e4a27..0000000
--- a/src/hooks/use-data-table.ts
+++ /dev/null
@@ -1,356 +0,0 @@
-"use client";
-
-import {
- type ColumnFiltersState,
- getCoreRowModel,
- getFacetedRowModel,
- getFacetedUniqueValues,
- getFilteredRowModel,
- getPaginationRowModel,
- getSortedRowModel,
- type PaginationState,
- type RowSelectionState,
- type SortingState,
- type TableOptions,
- type TableState,
- type Updater,
- useReactTable,
- type VisibilityState,
-} from "@tanstack/react-table";
-import {
- parseAsArrayOf,
- parseAsInteger,
- parseAsString,
- type Parser,
- useQueryState,
- type UseQueryStateOptions,
- useQueryStates,
-} from "nuqs";
-import * as React from "react";
-
-import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
-import { getSortingStateParser } from "@/lib/parsers";
-import { DataTableFilterField, ExtendedSortingState } from "@/types/table";
-
-interface UseDataTableProps
- extends Omit<
- TableOptions,
- | "state"
- | "pageCount"
- | "getCoreRowModel"
- | "manualFiltering"
- | "manualPagination"
- | "manualSorting"
- >,
- Required, "pageCount">> {
- /**
- * Defines filter fields for the table. Supports both dynamic faceted filters and search filters.
- * - Faceted filters are rendered when `options` are provided for a filter field.
- * - Otherwise, search filters are rendered.
- *
- * The indie filter field `value` represents the corresponding column name in the database table.
- * @default []
- * @type { label: string, value: keyof TData, placeholder?: string, options?: { label: string, value: string, icon?: React.ComponentType<{ className?: string }> }[] }[]
- * @example
- * ```ts
- * // Render a search filter
- * const filterFields = [
- * { label: "Title", value: "title", placeholder: "Search titles" }
- * ];
- * // Render a faceted filter
- * const filterFields = [
- * {
- * label: "Status",
- * value: "status",
- * options: [
- * { label: "Todo", value: "todo" },
- * { label: "In Progress", value: "in-progress" },
- * ]
- * }
- * ];
- * ```
- */
- filterFields?: DataTableFilterField[];
-
- /**
- * Determines how query updates affect history.
- * `push` creates a new history entry; `replace` (default) updates the current entry.
- * @default "replace"
- */
- history?: "push" | "replace";
-
- /**
- * Indicates whether the page should scroll to the top when the URL changes.
- * @default false
- */
- scroll?: boolean;
-
- /**
- * Shallow mode keeps query states client-side, avoiding server calls.
- * Setting to `false` triggers a network request with the updated querystring.
- * @default true
- */
- shallow?: boolean;
-
- /**
- * Maximum time (ms) to wait between URL query string updates.
- * Helps with browser rate-limiting. Minimum effective value is 50ms.
- * @default 50
- */
- throttleMs?: number;
-
- /**
- * Debounce time (ms) for filter updates to enhance performance during rapid input.
- * @default 300
- */
- debounceMs?: number;
-
- /**
- * Observe Server Component loading states for non-shallow updates.
- * Pass `startTransition` from `React.useTransition()`.
- * Sets `shallow` to `false` automatically.
- * So shallow: true` and `startTransition` cannot be used at the same time.
- * @see https://react.dev/reference/react/useTransition
- */
- startTransition?: React.TransitionStartFunction;
-
- /**
- * Clear URL query key-value pair when state is set to default.
- * Keep URL meaning consistent when defaults change.
- * @default false
- */
- clearOnDefault?: boolean;
-
- /**
- * Enable notion like column filters.
- * Advanced filters and column filters cannot be used at the same time.
- * @default false
- * @type boolean
- */
- enableAdvancedFilter?: boolean;
-
- initialState?: Omit, "sorting"> & {
- // Extend to make the sorting id typesafe
- sorting?: ExtendedSortingState;
- };
-}
-
-export function useDataTable({
- pageCount = -1,
- filterFields = [],
- enableAdvancedFilter = false,
- history = "replace",
- scroll = false,
- shallow = true,
- throttleMs = 50,
- debounceMs = 300,
- clearOnDefault = false,
- startTransition,
- initialState,
- ...props
-}: UseDataTableProps) {
- const queryStateOptions = React.useMemo<
- Omit, "parse">
- >(() => {
- return {
- history,
- scroll,
- shallow,
- throttleMs,
- debounceMs,
- clearOnDefault,
- startTransition,
- };
- }, [
- history,
- scroll,
- shallow,
- throttleMs,
- debounceMs,
- clearOnDefault,
- startTransition,
- ]);
-
- const [rowSelection, setRowSelection] = React.useState(
- initialState?.rowSelection ?? {},
- );
- const [columnVisibility, setColumnVisibility] =
- React.useState(initialState?.columnVisibility ?? {});
-
- const [page, setPage] = useQueryState(
- "page",
- parseAsInteger.withOptions(queryStateOptions).withDefault(1),
- );
- const [perPage, setPerPage] = useQueryState(
- "perPage",
- parseAsInteger
- .withOptions(queryStateOptions)
- .withDefault(initialState?.pagination?.pageSize ?? 10),
- );
- const [sorting, setSorting] = useQueryState(
- "sort",
- getSortingStateParser()
- .withOptions(queryStateOptions)
- .withDefault(initialState?.sorting ?? []),
- );
-
- // Create parsers for each filter field
- const filterParsers = React.useMemo(() => {
- return filterFields.reduce<
- Record | Parser>
- >((acc, field) => {
- if (field.options) {
- // Faceted filter
- acc[field.id] = parseAsArrayOf(parseAsString, ",").withOptions(
- queryStateOptions,
- );
- } else {
- // Search filter
- acc[field.id] = parseAsString.withOptions(queryStateOptions);
- }
- return acc;
- }, {});
- }, [filterFields, queryStateOptions]);
-
- const [filterValues, setFilterValues] = useQueryStates(filterParsers);
-
- const debouncedSetFilterValues = useDebouncedCallback(
- setFilterValues,
- debounceMs,
- );
-
- // Paginate
- const pagination: PaginationState = {
- pageIndex: page - 1, // zero-based index -> one-based index
- pageSize: perPage,
- };
-
- function onPaginationChange(updaterOrValue: Updater) {
- if (typeof updaterOrValue === "function") {
- const newPagination = updaterOrValue(pagination);
- void setPage(newPagination.pageIndex + 1);
- void setPerPage(newPagination.pageSize);
- } else {
- void setPage(updaterOrValue.pageIndex + 1);
- void setPerPage(updaterOrValue.pageSize);
- }
- }
-
- // Sort
- function onSortingChange(updaterOrValue: Updater) {
- if (typeof updaterOrValue === "function") {
- const newSorting = updaterOrValue(sorting) as ExtendedSortingState;
- void setSorting(newSorting);
- }
- }
-
- // Filter
- const initialColumnFilters: ColumnFiltersState = React.useMemo(() => {
- return enableAdvancedFilter
- ? []
- : Object.entries(filterValues).reduce(
- (filters, [key, value]) => {
- if (value !== null) {
- filters.push({
- id: key,
- value: Array.isArray(value) ? value : [value],
- });
- }
- return filters;
- },
- [],
- );
- }, [filterValues, enableAdvancedFilter]);
-
- const [columnFilters, setColumnFilters] =
- React.useState(initialColumnFilters);
-
- // Memoize computation of searchableColumns and filterableColumns
- const { searchableColumns, filterableColumns } = React.useMemo(() => {
- return enableAdvancedFilter
- ? { searchableColumns: [], filterableColumns: [] }
- : {
- searchableColumns: filterFields.filter((field) => !field.options),
- filterableColumns: filterFields.filter((field) => field.options),
- };
- }, [filterFields, enableAdvancedFilter]);
-
- const onColumnFiltersChange = React.useCallback(
- (updaterOrValue: Updater) => {
- // Don't process filters if advanced filtering is enabled
- if (enableAdvancedFilter) return;
-
- setColumnFilters((prev) => {
- const next =
- typeof updaterOrValue === "function"
- ? updaterOrValue(prev)
- : updaterOrValue;
-
- const filterUpdates = next.reduce<
- Record
- >((acc, filter) => {
- if (searchableColumns.find((col) => col.id === filter.id)) {
- // For search filters, use the value directly
- acc[filter.id] = filter.value as string;
- } else if (filterableColumns.find((col) => col.id === filter.id)) {
- // For faceted filters, use the array of values
- acc[filter.id] = filter.value as string[];
- }
- return acc;
- }, {});
-
- prev.forEach((prevFilter) => {
- if (!next.some((filter) => filter.id === prevFilter.id)) {
- filterUpdates[prevFilter.id] = null;
- }
- });
-
- void setPage(1);
-
- debouncedSetFilterValues(filterUpdates);
- return next;
- });
- },
- [
- debouncedSetFilterValues,
- enableAdvancedFilter,
- filterableColumns,
- searchableColumns,
- setPage,
- ],
- );
-
- const table = useReactTable({
- ...props,
- initialState,
- pageCount,
- state: {
- pagination,
- sorting,
- columnVisibility,
- rowSelection,
- columnFilters: enableAdvancedFilter ? [] : columnFilters,
- },
- enableRowSelection: true,
- onRowSelectionChange: setRowSelection,
- onPaginationChange,
- onSortingChange,
- onColumnFiltersChange,
- onColumnVisibilityChange: setColumnVisibility,
- getCoreRowModel: getCoreRowModel(),
- getFilteredRowModel: enableAdvancedFilter
- ? undefined
- : getFilteredRowModel(),
- getPaginationRowModel: getPaginationRowModel(),
- getSortedRowModel: getSortedRowModel(),
- getFacetedRowModel: enableAdvancedFilter ? undefined : getFacetedRowModel(),
- getFacetedUniqueValues: enableAdvancedFilter
- ? undefined
- : getFacetedUniqueValues(),
- manualPagination: true,
- manualSorting: true,
- manualFiltering: true,
- });
-
- return { table };
-}
diff --git a/src/hooks/use-fetch-data-values.ts b/src/hooks/use-fetch-data-values.ts
deleted file mode 100644
index cbfc6bf..0000000
--- a/src/hooks/use-fetch-data-values.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-"use client";
-
-import React from "react";
-
-import { EntityValueDefinition } from "@/types/commons";
-
-// Generic hook to load data
-export const useFetchData = (
- fetchData: () => Promise>,
-) => {
- const [items, setItems] = React.useState>([]);
-
- const loadItems = React.useCallback(async () => {
- const data = await fetchData();
- if (data) {
- setItems(data.map((it) => it.value)); // Apply transformation to the fetched data
- }
- }, [fetchData]);
-
- React.useEffect(() => {
- loadItems();
- }, [loadItems]);
-
- return items;
-};
diff --git a/src/lib/actions/accounts.action.ts b/src/lib/actions/accounts.action.ts
deleted file mode 100644
index c3fafc1..0000000
--- a/src/lib/actions/accounts.action.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-"use server";
-
-import { revalidatePath, unstable_noStore as noStore } from "next/cache";
-import { redirect } from "next/navigation";
-
-import {
- deleteExec,
- doAdvanceSearch,
- get,
- post,
- put,
-} from "@/lib/actions/commons.action";
-import { findEntitiesFilterOptions } from "@/lib/actions/shared.action";
-import { BACKEND_API } from "@/lib/constants";
-import {
- AccountDTO,
- AccountDTOSchema,
- AccountSearchParams,
-} from "@/types/accounts";
-import { EntityValueDefinition, PageableResult } from "@/types/commons";
-
-export const findAccounts = async (): Promise> => {
- return get>(`${BACKEND_API}/api/crm/accounts`);
-};
-
-export const findAccountStatuses = async (): Promise<
- Array
-> => {
- return get>(
- `${BACKEND_API}/api/crm/values?entityType=account&&valueKey=status`,
- );
-};
-
-export const findAccountStatusesFilterOptions = async () => {
- return findEntitiesFilterOptions(findAccountStatuses);
-};
-
-export const findAccountTypes = async (): Promise<
- Array
-> => {
- return get>(
- `${BACKEND_API}/api/crm/values?entityType=account&&valueKey=type`,
- );
-};
-
-export const findAccountTypesFilterOptions = async () => {
- return findEntitiesFilterOptions(findAccountTypes);
-};
-
-export const findAccountIndustries = async (): Promise<
- Array
-> => {
- return get>(
- `${BACKEND_API}/api/crm/values?entityType=account&&valueKey=industry`,
- );
-};
-
-export const findAccountIndustriesFilterOptions = async () => {
- return findEntitiesFilterOptions(findAccountIndustries);
-};
-
-export const saveOrUpdateAccount = async (
- isEdit: boolean,
- account: AccountDTO,
-): Promise => {
- const validation = AccountDTOSchema.safeParse(account);
-
- if (validation.success) {
- if (isEdit) {
- await put(
- `${BACKEND_API}/api/crm/accounts/${account.id}`,
- account,
- );
- } else {
- await post(
- `${BACKEND_API}/api/crm/accounts`,
- account,
- );
- }
- redirect("/portal/accounts");
- }
-};
-
-export const findAccountById = async (accountId: number) => {
- return get(`${BACKEND_API}/api/crm/accounts/${accountId}`);
-};
-
-export const findPreviousAccount = async (accountId: number) => {
- return get(
- `${BACKEND_API}/api/crm/accounts/${accountId}/previous`,
- );
-};
-
-export const findNextAccount = async (accountId: number) => {
- return get(`${BACKEND_API}/api/crm/accounts/${accountId}/next`);
-};
-
-export async function searchAccounts(input: AccountSearchParams) {
- noStore();
- return doAdvanceSearch(`${BACKEND_API}/api/crm/accounts/search`);
-}
-
-export async function deleteAccounts(ids: number[]) {
- revalidatePath("/");
-
- return deleteExec(`${BACKEND_API}/api/crm/accounts`, ids).then(() => {});
-}
diff --git a/src/lib/actions/authorities.action.ts b/src/lib/actions/authorities.action.ts
index 5d22e14..0aa868e 100644
--- a/src/lib/actions/authorities.action.ts
+++ b/src/lib/actions/authorities.action.ts
@@ -25,6 +25,12 @@ export const createAuthority = async (authority: AuthorityDTO) => {
);
};
+export const deleteAuthority = async (authority_name: string) => {
+ return deleteExec(
+ `${BACKEND_API}/api/authorities/${authority_name}`,
+ );
+};
+
export const findPermissionsByAuthorityName = async (authorityName: string) => {
return get>(
`${BACKEND_API}/api/authority-permissions/${authorityName}`,
diff --git a/src/lib/actions/contacts.action.ts b/src/lib/actions/contacts.action.ts
deleted file mode 100644
index 3e0cb52..0000000
--- a/src/lib/actions/contacts.action.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-"use server";
-
-import { redirect } from "next/navigation";
-
-import { doAdvanceSearch, get, post, put } from "@/lib/actions/commons.action";
-import { findEntitiesFilterOptions } from "@/lib/actions/shared.action";
-import { BACKEND_API } from "@/lib/constants";
-import { EntityValueDefinition, PageableResult } from "@/types/commons";
-import { ConTactDTO, ContactDTOSchema } from "@/types/contacts";
-import { QueryDTO } from "@/types/query";
-
-export const findContactById = async (
- contactId: number,
-): Promise => {
- return get(`${BACKEND_API}/api/crm/contacts/${contactId}`);
-};
-
-export const findContactsByAccountId = async (
- accountId: number,
-): Promise> => {
- return get>(
- `${BACKEND_API}/api/crm/contacts/account/${accountId}`,
- );
-};
-
-export const findContactStatuses = async (): Promise<
- Array
-> => {
- return get>(
- `${BACKEND_API}/api/crm/values?entityType=contact&&valueKey=status`,
- );
-};
-
-export const findContactStatusesFilterOptions = async () => {
- return findEntitiesFilterOptions(findContactStatuses);
-};
-
-export async function searchContacts(query: QueryDTO) {
- return doAdvanceSearch(
- `${BACKEND_API}/api/crm/contacts/search`,
- query,
- );
-}
-
-export const saveOrUpdateContact = async (
- isEdit: boolean,
- contact: ConTactDTO,
-): Promise => {
- const validation = ContactDTOSchema.safeParse(contact);
-
- if (validation.success) {
- if (isEdit) {
- await put(
- `${BACKEND_API}/api/crm/contacts/${contact.id}`,
- contact,
- );
- } else {
- await post(
- `${BACKEND_API}/api/crm/contacts`,
- contact,
- );
- }
-
- redirect("/portal/contacts");
- }
-};
-
-export const findPreviousContact = async (contactId: number) => {
- return get(
- `${BACKEND_API}/api/crm/contacts/previous/${contactId}`,
- );
-};
-
-export const findNextContact = async (contactId: number) => {
- return get(`${BACKEND_API}/api/crm/contacts/next/${contactId}`);
-};
diff --git a/src/lib/data-table.ts b/src/lib/data-table.ts
deleted file mode 100644
index 0dd168d..0000000
--- a/src/lib/data-table.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import { type Column } from "@tanstack/react-table";
-
-import { dataTableConfig } from "@/config/data-table";
-import { ColumnType, Filter, FilterOperator } from "@/types/table";
-
-/**
- * Generate common pinning styles for a table column.
- *
- * This function calculates and returns CSS properties for pinned columns in a data table.
- * It handles both left and right pinning, applying appropriate styles for positioning,
- * shadows, and z-index. The function also considers whether the column is the last left-pinned
- * or first right-pinned column to apply specific shadow effects.
- *
- * @param options - The options for generating pinning styles.
- * @param options.column - The column object for which to generate styles.
- * @param options.withBorder - Whether to show a box shadow between pinned and scrollable columns.
- * @returns A React.CSSProperties object containing the calculated styles.
- */
-export function getCommonPinningStyles({
- column,
- withBorder = false,
-}: {
- column: Column;
- /**
- * Show box shadow between pinned and scrollable columns.
- * @default false
- */
- withBorder?: boolean;
-}): React.CSSProperties {
- const isPinned = column.getIsPinned();
- const isLastLeftPinnedColumn =
- isPinned === "left" && column.getIsLastColumn("left");
- const isFirstRightPinnedColumn =
- isPinned === "right" && column.getIsFirstColumn("right");
-
- return {
- boxShadow: withBorder
- ? isLastLeftPinnedColumn
- ? "-4px 0 4px -4px hsl(var(--border)) inset"
- : isFirstRightPinnedColumn
- ? "4px 0 4px -4px hsl(var(--border)) inset"
- : undefined
- : undefined,
- left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
- right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
- opacity: isPinned ? 0.97 : 1,
- position: isPinned ? "sticky" : "relative",
- background: isPinned ? "hsl(var(--background))" : "hsl(var(--background))",
- width: column.getSize(),
- zIndex: isPinned ? 1 : 0,
- };
-}
-
-/**
- * Determine the default filter operator for a given column type.
- *
- * This function returns the most appropriate default filter operator based on the
- * column's data type. For text columns, it returns 'iLike' (case-insensitive like),
- * while for all other types, it returns 'eq' (equality).
- *
- * @param columnType - The type of the column (e.g., 'text', 'number', 'date', etc.).
- * @returns The default FilterOperator for the given column type.
- */
-export function getDefaultFilterOperator(
- columnType: ColumnType,
-): FilterOperator {
- if (columnType === "text") {
- return "iLike";
- }
-
- return "eq";
-}
-
-/**
- * Retrieve the list of applicable filter operators for a given column type.
- *
- * This function returns an array of filter operators that are relevant and applicable
- * to the specified column type. It uses a predefined mapping of column types to
- * operator lists, falling back to text operators if an unknown column type is provided.
- *
- * @param columnType - The type of the column for which to get filter operators.
- * @returns An array of objects, each containing a label and value for a filter operator.
- */
-export function getFilterOperators(columnType: ColumnType) {
- const operatorMap: Record<
- ColumnType,
- { label: string; value: FilterOperator }[]
- > = {
- text: dataTableConfig.textOperators,
- number: dataTableConfig.numericOperators,
- select: dataTableConfig.selectOperators,
- "multi-select": dataTableConfig.selectOperators,
- boolean: dataTableConfig.booleanOperators,
- date: dataTableConfig.dateOperators,
- };
-
- return operatorMap[columnType] ?? dataTableConfig.textOperators;
-}
-
-/**
- * Filters out invalid or empty filters from an array of filters.
- *
- * This function processes an array of filters and returns a new array
- * containing only the valid filters. A filter is considered valid if:
- * - It has an 'isEmpty' or 'isNotEmpty' operator, or
- * - Its value is not empty (for array values, at least one element must be present;
- * for other types, the value must not be an empty string, null, or undefined)
- *
- * @param filters - An array of Filter objects to be validated.
- * @returns A new array containing only the valid filters.
- */
-export function getValidFilters(
- filters: Filter[],
-): Filter[] {
- return filters.filter(
- (filter) =>
- filter.operator === "isEmpty" ||
- filter.operator === "isNotEmpty" ||
- (Array.isArray(filter.value)
- ? filter.value.length > 0
- : filter.value !== "" &&
- filter.value !== null &&
- filter.value !== undefined),
- );
-}
diff --git a/src/lib/menu-list.ts b/src/lib/menu-list.ts
index 492f940..bad942f 100644
--- a/src/lib/menu-list.ts
+++ b/src/lib/menu-list.ts
@@ -1,4 +1,12 @@
-import { BookLock, Layers, LayoutGrid, LucideIcon, Users } from "lucide-react";
+import {
+ BookLock,
+ Layers,
+ LayoutGrid,
+ LucideIcon,
+ ShieldCheck,
+ UserCog,
+ Users,
+} from "lucide-react";
import { Permission } from "@/providers/permissions-provider";
@@ -74,11 +82,6 @@ export function getMenuList(
{
groupLabel: "",
menus: [
- // {
- // href: "/portal/accounts",
- // label: "Accounts",
- // icon: Building2,
- // },
{
href: "/portal/teams",
label: "Teams",
@@ -97,7 +100,7 @@ export function getMenuList(
{
href: "/portal/settings/authorities",
label: "Authorities",
- icon: BookLock,
+ icon: ShieldCheck,
},
],
},
diff --git a/src/lib/parsers.ts b/src/lib/parsers.ts
deleted file mode 100644
index 18334ab..0000000
--- a/src/lib/parsers.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import { type Row } from "@tanstack/react-table";
-import { createParser } from "nuqs/server";
-import { z } from "zod";
-
-import { dataTableConfig } from "@/config/data-table";
-import { ExtendedSortingState, Filter } from "@/types/table";
-
-export const sortingItemSchema = z.object({
- id: z.string(),
- desc: z.boolean(),
-});
-
-/**
- * Creates a parser for TanStack Table sorting state.
- * @param originalRow The original row data to validate sorting keys against.
- * @returns A parser for TanStack Table sorting state.
- */
-export const getSortingStateParser = (
- originalRow?: Row["original"],
-) => {
- const validKeys = originalRow ? new Set(Object.keys(originalRow)) : null;
-
- return createParser>({
- parse: (value) => {
- try {
- const parsed = JSON.parse(value);
- const result = z.array(sortingItemSchema).safeParse(parsed);
-
- if (!result.success) return null;
-
- if (validKeys && result.data.some((item) => !validKeys.has(item.id))) {
- return null;
- }
-
- return result.data as ExtendedSortingState;
- } catch {
- return null;
- }
- },
- serialize: (value) => JSON.stringify(value),
- eq: (a, b) =>
- a.length === b.length &&
- a.every(
- (item, index) =>
- item.id === b[index]?.id && item.desc === b[index]?.desc,
- ),
- });
-};
-
-export const filterSchema = z.object({
- id: z.string(),
- value: z.union([z.string(), z.array(z.string())]),
- type: z.enum(dataTableConfig.columnTypes),
- operator: z.enum(dataTableConfig.globalOperators),
- rowId: z.string(),
-});
-
-/**
- * Create a parser for data table filters.
- * @param originalRow The original row data to create the parser for.
- * @returns A parser for data table filters state.
- */
-export const getFiltersStateParser = (originalRow?: Row["original"]) => {
- const validKeys = originalRow ? new Set(Object.keys(originalRow)) : null;
-
- return createParser[]>({
- parse: (value) => {
- try {
- const parsed = JSON.parse(value);
- const result = z.array(filterSchema).safeParse(parsed);
-
- if (!result.success) return null;
-
- if (validKeys && result.data.some((item) => !validKeys.has(item.id))) {
- return null;
- }
-
- return result.data as Filter[];
- } catch {
- return null;
- }
- },
- serialize: (value) => JSON.stringify(value),
- eq: (a, b) =>
- a.length === b.length &&
- a.every(
- (filter, index) =>
- filter.id === b[index]?.id &&
- filter.value === b[index]?.value &&
- filter.type === b[index]?.type &&
- filter.operator === b[index]?.operator,
- ),
- });
-};
diff --git a/src/types/accounts.ts b/src/types/accounts.ts
deleted file mode 100644
index 4bbf09e..0000000
--- a/src/types/accounts.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { z } from "zod";
-
-export const AccountDTOSchema = z.object({
- id: z.number().nullish(),
- name: z.string().min(1),
- type: z.string().min(1),
- industry: z.string().min(1),
- email: z.string().email().nullish(),
- addressLine1: z.string().nullish(),
- addressLine2: z.string().nullish(),
- city: z.string().nullish(),
- state: z.string().nullish(),
- postalCode: z.string().nullish(),
- country: z.string().nullish(),
- phoneNumber: z.string().nullish(),
- status: z.string().min(1),
- parentAccountId: z.number().optional(),
- parentAccountName: z.string().optional(),
- website: z
- .union([
- z.string().url(), // Full URL with scheme
- z.string().regex(/^(https?:\/\/)?(www\.)?[\w-]+(\.[\w-]+)+.*$/), // Allow optional scheme
- z.string().length(0), // Allow empty string
- ])
- .optional(),
- annualRevenue: z.string().nullish(),
- createdAt: z.string().nullish(),
- updatedAt: z.string().nullish(),
- notes: z.string().nullish(),
-});
-
-export type AccountDTO = z.infer;
-
-export const accountSearchParamsSchema = z.object({
- page: z.coerce.number().default(1), // page number
- size: z.coerce.number().default(10), // size per page
- sort: z.string().optional(),
- name: z.string().optional(),
- status: z.string().optional(),
- industry: z.string().optional(),
- type: z.string().optional(),
- operator: z.enum(["and", "or"]).optional(),
-});
-
-export type AccountSearchParams = z.infer;
diff --git a/src/types/authorities.ts b/src/types/authorities.ts
index 0d77cf6..32fa79b 100644
--- a/src/types/authorities.ts
+++ b/src/types/authorities.ts
@@ -6,6 +6,7 @@ export const AuthorityDTOSchema = z
descriptiveName: z.string().min(1),
systemRole: z.boolean().default(false),
description: z.string().optional(),
+ usersCount: z.onumber(),
})
.transform((data) => {
// If `name` is null, set it to `descriptiveName`
diff --git a/src/types/contacts.ts b/src/types/contacts.ts
deleted file mode 100644
index dd2c482..0000000
--- a/src/types/contacts.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { z } from "zod";
-
-export const ContactDTOSchema = z.object({
- id: z.number().nullish(),
- accountId: z.number().nullish(),
- accountName: z.string().nullish(),
- firstName: z.string().min(1),
- lastName: z.string().min(1),
- email: z.string().email().nullish(),
- address: z.string().nullish(),
- city: z.string().nullish(),
- state: z.string().nullish(),
- postalCode: z.string().nullish(),
- country: z.string().nullish(),
- phoneNumber: z.string().nullish(),
- position: z.string().nullish(),
- createdAt: z.string().nullish(),
- updatedAt: z.string().nullish(),
- status: z.string().min(1),
- notes: z.string().nullish(),
-});
-
-export type ConTactDTO = z.infer;
-
-export type ContactSearchSchema = {
- account_id: number;
-};
diff --git a/src/types/resources.ts b/src/types/resources.ts
index 5490275..a519823 100644
--- a/src/types/resources.ts
+++ b/src/types/resources.ts
@@ -1,11 +1,4 @@
-export type ResourceId =
- | "Users"
- | "Files"
- | "Accounts"
- | "Contacts"
- | "Teams"
- | "Organizations"
- | "Authorities";
+export type ResourceId = "Users" | "Teams" | "Organizations" | "Authorities";
export type PermissionLevel = "NONE" | "READ" | "WRITE" | "ACCESS";
diff --git a/src/types/table.ts b/src/types/table.ts
deleted file mode 100644
index 61162f6..0000000
--- a/src/types/table.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import type { ColumnSort, Row } from "@tanstack/react-table";
-import { type z } from "zod";
-
-import { type DataTableConfig } from "@/config/data-table";
-import { type filterSchema } from "@/lib/parsers";
-
-export type Prettify = {
- [K in keyof T]: T[K];
-} & {};
-
-export type StringKeyOf = Extract;
-
-export interface SearchParams {
- [key: string]: string | string[] | undefined;
-}
-
-export interface Option {
- label: string;
- value: string;
- icon?: React.ComponentType<{ className?: string }>;
- count?: number;
-}
-
-export interface ExtendedColumnSort extends Omit {
- id: StringKeyOf;
-}
-
-export type ExtendedSortingState = ExtendedColumnSort[];
-
-export type ColumnType = DataTableConfig["columnTypes"][number];
-
-export type FilterOperator = DataTableConfig["globalOperators"][number];
-
-export type JoinOperator = DataTableConfig["joinOperators"][number]["value"];
-
-export interface DataTableFilterField {
- id: StringKeyOf;
- label: string;
- placeholder?: string;
- options?: Option[];
-}
-
-export interface DataTableAdvancedFilterField
- extends DataTableFilterField {
- type: ColumnType;
-}
-
-export type Filter = Prettify<
- Omit, "id"> & {
- id: StringKeyOf;
- }
->;
-
-export interface DataTableRowAction {
- row: Row;
- type: "update" | "delete";
-}