import { Fragment, ReactNode, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, Outlet, useActionData, useLocation, useNavigation, useSearchParams, useSubmit } from "react-router"; import RowsList from "~/components/entities/rows/RowsList"; import ButtonPrimary from "~/components/ui/buttons/ButtonPrimary"; import InputFilters, { FilterDto } from "~/components/ui/input/InputFilters"; import TabsWithIcons from "~/components/ui/tabs/TabsWithIcons"; import EntityHelper from "~/utils/helpers/EntityHelper"; import { EntitiesApi } from "~/utils/api/.server/EntitiesApi"; import { EntityViewWithDetails } from "~/utils/db/entities/entityViews.db.server"; import { RowWithDetails } from "~/utils/db/entities/rows.db.server"; import { useAppData } from "~/utils/data/useAppData"; import EntityViewForm from "~/components/entities/views/EntityViewForm"; import { UserSimple } from "~/utils/db/users.db.server"; import SlideOverWideEmpty from "~/components/ui/slideOvers/SlideOverWideEmpty"; import { Rows_List } from "../routes/Rows_List.server"; import { toast } from "sonner"; import { RowsApi } from "~/utils/api/.server/RowsApi"; import ConfirmModal, { RefConfirmModal } from "~/components/ui/modals/ConfirmModal"; import TrashIcon from "~/components/ui/icons/TrashIcon"; import clsx from "clsx"; import { translateEntityTitle, translateEntityTitlePlural, translateEntityViewTitle } from "~/utils/i18n/translateLabel"; import { useLocaleFormatters } from "~/locale/format"; interface Props { title?: ReactNode; rowsData: RowsApi.GetRowsData; items: RowWithDetails[]; routes?: EntitiesApi.Routes; onNewRow?: () => void; onEditRow?: (item: RowWithDetails) => void; saveCustomViews?: boolean; permissions: { create: boolean; }; currentSession: { user: UserSimple; isSuperAdmin: boolean; } | null; } export default function RowsViewRoute({ title, rowsData, items, routes, onNewRow, onEditRow, saveCustomViews, permissions, currentSession }: Props) { const { t, i18n } = useTranslation(); const formatters = useLocaleFormatters(); const actionData = useActionData(); const appData = useAppData(); const submit = useSubmit(); const location = useLocation(); const [searchParams] = useSearchParams(); const confirmDeleteRows = useRef(null); const [bulkActions, setBulkActions] = useState([]); const [view, setView] = useState(rowsData.currentView?.layout ?? searchParams.get("view") ?? "table"); const [filters, setFilters] = useState([]); const [showCustomViewModal, setShowCustomViewModal] = useState(false); const [editingView, setEditingView] = useState(null); const [selectedRows, setSelectedRows] = useState([]); useEffect(() => { setFilters(EntityHelper.getFilters({ t, entity: rowsData.entity, pagination: rowsData.pagination })); const bulkActions: string[] = []; if (rowsData.entity.hasBulkDelete) { bulkActions.push("bulk-delete"); } setBulkActions(bulkActions); }, [rowsData, t]); useEffect(() => { const newView = rowsData.currentView?.layout ?? searchParams.get("view") ?? "table"; setView(newView); }, [searchParams, rowsData.entity, rowsData.currentView?.layout]); useEffect(() => { if (actionData?.error) { toast.error(actionData.error); } else if (actionData?.success) { toast.success(actionData.success); setSelectedRows([]); } else if (actionData?.rowsDeleted) { setSelectedRows((rows) => rows.filter((row) => !actionData?.rowsDeleted?.includes(row.id))); } if (actionData?.updatedView) { setShowCustomViewModal(false); setEditingView(null); } }, [actionData]); useEffect(() => { setShowCustomViewModal(false); setEditingView(null); }, [searchParams]); function onCreateView() { setShowCustomViewModal(true); setEditingView(null); } function onUpdateView() { setShowCustomViewModal(true); setEditingView(rowsData.currentView); } function isCurrenView(view: EntityViewWithDetails) { return rowsData.currentView?.id === view.id; } function canUpdateCurrentView() { if (currentSession?.isSuperAdmin) { return true; } if (!rowsData.currentView) { return false; } if (rowsData.currentView.userId === currentSession?.user.id) { return true; } if (appData?.currentTenant?.id && rowsData.currentView.tenantId === appData?.currentTenant.id && appData?.isSuperUser) { return true; } return false; } function onDeleteSelectedRows() { confirmDeleteRows.current?.show(t("shared.confirmDelete"), t("shared.delete"), t("shared.cancel"), t("shared.warningCannotUndo")); } function onDeleteSelectedRowsConfirmed() { const form = new FormData(); form.set("action", "bulk-delete"); selectedRows.forEach((item) => { form.append("rowIds[]", item.id); }); submit(form, { method: "post", }); } return (
{selectedRows.length > 0 ? (
{bulkActions.includes("bulk-delete") && }
) : ( {rowsData.views.length > 1 ? ( { // if (views.find((f) => f.name === item.name && f.isDefault)) { // searchParams.delete("v"); // } else { searchParams.set("v", item.name); // } searchParams.delete("page"); return { name: translateEntityViewTitle(t, i18n, rowsData.entity.name, item), href: location.pathname + "?" + searchParams.toString(), current: isCurrenView(item), }; })} /> ) : ( title ?? (

{rowsData.currentView ? translateEntityViewTitle(t, i18n, rowsData.entity.name, rowsData.currentView) : translateEntityTitlePlural(t, i18n, rowsData.entity)}

) )}
)}
{filters.length > 0 && } {permissions.create && ( {t("shared.new")} {translateEntityTitle(t, i18n, rowsData.entity)} )}
setSelectedRows(rows)} />
{rowsData.pagination && rowsData.pagination.totalItems > 0 && routes && (
{t("shared.exportResults", { count: rowsData.pagination.totalItems, formattedCount: formatters.number(rowsData.pagination.totalItems) })}
)}
{saveCustomViews && rowsData.entity.hasViews && ( {canUpdateCurrentView() ? (
•
) : ( )}
)}
setShowCustomViewModal(false)} > {showCustomViewModal && ( setShowCustomViewModal(false)} actionNames={{ create: "view-create", update: "view-edit", delete: "view-delete", }} isSystem={false} showViewType={currentSession?.isSuperAdmin ?? false} /> )}
); } function DeleteIconButton({ onClick }: { onClick: () => void }) { const navigation = useNavigation(); const { t } = useTranslation(); return ( ); }