@premieroctet/next-admin
Version:
Next-Admin provides a customizable and turnkey admin dashboard for applications built with Next.js and powered by the Prisma ORM. It aims to simplify the development process by providing a turnkey admin system that can be easily integrated into your proje
297 lines (296 loc) • 15.2 kB
JavaScript
"use client";
import { jsx, jsxs } from "react/jsx-runtime";
import { EllipsisVerticalIcon, TrashIcon } from "@heroicons/react/24/outline";
import external_clsx_default from "clsx";
import external_lodash_debounce_default from "lodash.debounce";
import { useEffect, useMemo, useOptimistic, useState, useTransition } from "react";
import { twMerge } from "tailwind-merge";
import { ITEMS_PER_PAGE } from "../config.mjs";
import ClientActionDialogContext_mjs_default from "../context/ClientActionDialogContext.mjs";
import { useConfig } from "../context/ConfigContext.mjs";
import { useI18n } from "../context/I18nContext.mjs";
import useDataColumns_mjs_default from "../hooks/useDataColumns.mjs";
import { useDeleteAction } from "../hooks/useDeleteAction.mjs";
import { useRouterInternal } from "../hooks/useRouterInternal.mjs";
import { reorderData, slugify } from "../utils/tools.mjs";
import external_ActionDropdownItem_mjs_default from "./ActionDropdownItem.mjs";
import { DataTable } from "./DataTable.mjs";
import external_Filters_mjs_default from "./Filters.mjs";
import external_ListHeader_mjs_default from "./ListHeader.mjs";
import { Message } from "./Message.mjs";
import { Pagination } from "./Pagination.mjs";
import external_TableRowsIndicator_mjs_default from "./TableRowsIndicator.mjs";
import Button_mjs_default from "./radix/Button.mjs";
import Checkbox_mjs_default from "./radix/Checkbox.mjs";
import { Dropdown, DropdownBody, DropdownContent, DropdownItem, DropdownTrigger } from "./radix/Dropdown.mjs";
import { Select, SelectContent, SelectItem, SelectTrigger } from "./radix/Select.mjs";
const itemsPerPageSizes = [
10,
25,
50,
100
];
function List({ resource, data, total, actions, resourcesIdProperty, title, icon, schema, clientActionsComponents, rawData, listFilterOptions }) {
const { router, query } = useRouterInternal();
const [isPending, startTransition] = useTransition();
const [_orderPending, startOrderTransition] = useTransition();
const { options, apiBasePath } = useConfig();
const [rowSelection, setRowSelection] = useState({});
const pageIndex = "string" == typeof query.page ? Number(query.page) - 1 : 0;
const modelDefaultListSize = options?.model?.[resource]?.list?.defaultListSize;
const pageSize = Number(query.itemsPerPage) || modelDefaultListSize || ITEMS_PER_PAGE;
const modelOptions = options?.["model"]?.[resource];
const sortColumn = modelOptions?.list?.orderField ? void 0 : query.sortColumn;
const sortDirection = modelOptions?.list?.orderField ? void 0 : query.sortDirection;
const { deleteItems } = useDeleteAction(resource);
const { t } = useI18n();
const columns = useDataColumns_mjs_default({
data,
resource,
sortable: !modelOptions?.list?.orderField,
resourcesIdProperty,
sortColumn,
sortDirection,
rawData
});
const allListSizes = useMemo(()=>{
if (modelDefaultListSize) return [
...itemsPerPageSizes,
modelDefaultListSize
].sort((a, b)=>a - b);
return itemsPerPageSizes;
}, [
modelDefaultListSize
]);
let onSearchChange;
const [optimisticData, optimisticOrderData] = useOptimistic(data, (prevData, newData)=>{
if (!modelOptions?.list?.orderField) return prevData;
return newData;
});
const hasDeletePermission = !modelOptions?.permissions || modelOptions?.permissions?.includes("delete");
if (!(modelOptions?.list?.search && modelOptions?.list?.search?.length === 0)) onSearchChange = external_lodash_debounce_default((e)=>{
startTransition(()=>{
router?.push({
pathname: location.pathname,
query: {
...query,
search: e.target.value
}
});
});
}, 300);
const checkboxColumn = {
id: "__nextadmin-select-row",
header: ({ table })=>{
if (0 === table.getRowModel().rows.length) return null;
return /*#__PURE__*/ jsx("div", {
className: "px-1",
children: /*#__PURE__*/ jsx(Checkbox_mjs_default, {
checked: table.getIsAllRowsSelected(),
indeterminate: table.getIsSomeRowsSelected(),
onChange: table.getToggleAllRowsSelectedHandler()
})
});
},
cell: ({ row })=>/*#__PURE__*/ jsx("div", {
className: "px-1",
children: /*#__PURE__*/ jsx(Checkbox_mjs_default, {
checked: row.getIsSelected(),
indeterminate: row.getIsSomeSelected(),
onChange: row.getToggleSelectedHandler(),
disabled: !row.getCanSelect(),
onClick: (e)=>e.stopPropagation()
})
})
};
const actionsColumn = {
id: "__nextadmin-actions",
header: ()=>null,
cell: ({ row })=>{
const idProperty = resourcesIdProperty[resource];
if (!hasDeletePermission) return;
return /*#__PURE__*/ jsxs(Dropdown, {
children: [
/*#__PURE__*/ jsx(DropdownTrigger, {
asChild: true,
children: /*#__PURE__*/ jsx(Button_mjs_default, {
variant: "ghost",
size: "sm",
className: "hover:bg-nextadmin-background-emphasis !px-2 py-2",
children: /*#__PURE__*/ jsx(EllipsisVerticalIcon, {
className: "text-nextadmin-content-default dark:text-dark-nextadmin-content-default h-6 w-6"
})
})
}),
/*#__PURE__*/ jsx(DropdownBody, {
children: /*#__PURE__*/ jsxs(DropdownContent, {
side: "left",
align: "start",
sideOffset: 5,
className: "z-50 space-y-1.5 px-2 py-2",
children: [
actions?.map((action)=>/*#__PURE__*/ jsx(external_ActionDropdownItem_mjs_default, {
action: action,
resourceIds: [
row.original[idProperty].value
],
resource: resource
}, action.id)),
/*#__PURE__*/ jsxs(DropdownItem, {
className: twMerge(external_clsx_default("flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-red-700 hover:bg-red-50 dark:text-red-400")),
onClick: (evt)=>{
evt.stopPropagation();
deleteItems([
row.original[idProperty].value
]);
},
children: [
/*#__PURE__*/ jsx(TrashIcon, {
className: "h-5 w-5"
}),
t("list.row.actions.delete.label")
]
})
]
})
})
]
});
}
};
useEffect(()=>{
setRowSelection({});
}, [
data
]);
const getSelectedRowsIds = ()=>{
const indices = Object.keys(rowSelection);
const selectedRows = data.filter((_, index)=>indices.includes(index.toString()));
const idField = resourcesIdProperty[resource];
return selectedRows.map((row)=>row[idField].value);
};
const handleOrderChange = modelOptions?.list?.orderField ? (value)=>{
startOrderTransition(async ()=>{
const idField = resourcesIdProperty[resource];
const newData = reorderData(optimisticData, value.currentId, value.moveOverId, modelOptions?.list?.orderField, idField);
optimisticOrderData(newData);
await fetch(`${apiBasePath}/${slugify(resource)}/order`, {
method: "POST",
body: JSON.stringify(optimisticData)
});
router.refresh();
});
} : void 0;
return /*#__PURE__*/ jsx(ClientActionDialogContext_mjs_default, {
componentsMap: clientActionsComponents,
children: /*#__PURE__*/ jsxs("div", {
className: "flow-root h-full",
children: [
/*#__PURE__*/ jsx(external_ListHeader_mjs_default, {
title: title,
icon: icon,
resource: resource,
search: query.search || "",
onSearchChange: onSearchChange,
isPending: isPending,
selectedRows: rowSelection,
actions: actions,
getSelectedRowsIds: getSelectedRowsIds,
onDelete: ()=>deleteItems(getSelectedRowsIds()),
totalCount: total,
schema: schema
}),
/*#__PURE__*/ jsxs("div", {
className: "bg-nextadmin-background-default dark:bg-dark-nextadmin-background-default max-w-full p-4 align-middle sm:p-8",
children: [
/*#__PURE__*/ jsxs("div", {
className: "-mt-2 mb-2 space-y-4 sm:-mt-4 sm:mb-4",
children: [
/*#__PURE__*/ jsx(Message, {}),
/*#__PURE__*/ jsx(external_Filters_mjs_default, {
filters: listFilterOptions
})
]
}),
/*#__PURE__*/ jsx(DataTable, {
resource: resource,
data: optimisticData,
columns: [
checkboxColumn,
...columns,
actionsColumn
],
resourcesIdProperty: resourcesIdProperty,
rowSelection: rowSelection,
setRowSelection: setRowSelection,
icon: icon,
onOrderChange: handleOrderChange,
orderField: modelOptions?.list?.orderField
}),
optimisticData.length ? /*#__PURE__*/ jsxs("div", {
className: "flex flex-1 flex-wrap items-center justify-between gap-2 py-4",
children: [
/*#__PURE__*/ jsx("div", {
children: /*#__PURE__*/ jsx(external_TableRowsIndicator_mjs_default, {
pageIndex: pageIndex,
totalRows: total,
currentPageIndex: pageIndex,
pageSize: pageSize
})
}),
/*#__PURE__*/ jsxs("div", {
className: "flex flex-1 items-center justify-end gap-y-2 space-x-4",
children: [
/*#__PURE__*/ jsxs(Select, {
onValueChange: (value)=>{
if (isNaN(Number(value))) return;
router?.push({
pathname: location.pathname,
query: {
...query,
page: 1,
itemsPerPage: value
}
});
},
children: [
/*#__PURE__*/ jsx(SelectTrigger, {
className: "bg-nextadmin-background-default dark:bg-dark-nextadmin-background-subtle max-h-[36px] max-w-[100px]",
children: /*#__PURE__*/ jsx("span", {
className: "text-nextadmin-content-inverted dark:text-dark-nextadmin-content-inverted pointer-events-none",
children: pageSize
})
}),
/*#__PURE__*/ jsx(SelectContent, {
children: allListSizes.map((size)=>/*#__PURE__*/ jsx(SelectItem, {
value: size.toString(),
children: size
}, size))
})
]
}),
/*#__PURE__*/ jsx(Pagination, {
currentPageIndex: pageIndex,
totalPageCount: Math.ceil(total / pageSize),
onPageChange: (pageIndex)=>{
router?.push({
pathname: location.pathname,
query: {
...query,
page: pageIndex + 1
}
});
}
})
]
})
]
}) : null
]
})
]
})
});
}
const components_List = List;
export { components_List as default };