import { useMutation } from "@tanstack/react-query"; import { KeyRound, MoreHorizontal, RefreshCw } from "lucide-react"; import * as React from "react"; import { useDataProvider, useGetIdentity, useListContext, useNotify, useRecordContext, useRefresh, useTranslate, } from "ra-core"; import { CreateButton } from "@/components/ds/admin/create-button"; import { DataTable } from "@/components/ds/admin/data-table"; import { ExportButton } from "@/components/ds/admin/export-button"; import { List } from "@/components/ds/admin/list"; import { SearchInput } from "@/components/ds/admin/search-input"; import { Badge } from "@/components/ds/ui/badge"; import { Button } from "@/components/ds/ui/button"; import { Card } from "@/components/ds/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ds/ui/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ds/ui/dropdown-menu"; import { Skeleton } from "@/components/ds/ui/skeleton"; import { TopToolbar } from "../layout/TopToolbar"; import useAppBarHeight from "../misc/useAppBarHeight"; import type { CrmDataProvider } from "../providers/types"; const SalesListActions = () => { const translate = useTranslate(); return ( ); }; const filters = []; const OptionsField = (_props: { label?: string | boolean }) => { const record = useRecordContext(); const translate = useTranslate(); if (!record) return null; return (
{record.administrator && ( {translate("crm.user.field.administrator")} )} {record.disabled && ( {translate("crm.user.field.disabled")} )}
); }; const RowActions = () => { const record = useRecordContext(); const dataProvider = useDataProvider(); const notify = useNotify(); const refresh = useRefresh(); const translate = useTranslate(); const [inviteDialogOpen, setInviteDialogOpen] = React.useState(false); const [resetDialogOpen, setResetDialogOpen] = React.useState(false); const { mutate: resendInvite, isPending: isInvitePending } = useMutation({ mutationFn: async () => { return dataProvider.resendInvite(record.id); }, onSuccess: () => { notify(translate("crm.user.notification.invite_sent")); setInviteDialogOpen(false); refresh(); }, onError: () => { notify(translate("crm.user.notification.invite_error"), { type: "error", }); setInviteDialogOpen(false); }, }); const { mutate: resetPassword, isPending: isResetPending } = useMutation({ mutationFn: async () => { return dataProvider.resetPassword(record.id); }, onSuccess: () => { notify(translate("crm.user.notification.reset_sent")); setResetDialogOpen(false); refresh(); }, onError: () => { notify(translate("crm.user.notification.reset_error"), { type: "error" }); setResetDialogOpen(false); }, }); if (!record) return null; // Determine which action to show based on confirmation status const isConfirmed = record.email_confirmed_at !== null; const isDisabled = record.disabled === true; return ( <> {translate("crm.user.field.email_actions")} {!isConfirmed && ( { e.preventDefault(); e.stopPropagation(); setInviteDialogOpen(true); }} > {translate("crm.user.action.resend_invite")} )} {isConfirmed && !isDisabled && ( setResetDialogOpen(true)}> {translate("crm.user.action.send_password_reset")} )} {/* Resend Invite Dialog */} e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} > {translate("crm.user.dialog.resend_invitation.title")} {translate("crm.user.dialog.resend_invitation.description", { email: record.email, })}

{translate("crm.user.dialog.resend_invitation.fresh_link")}
{/* Password Reset Dialog */} e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} > {translate("crm.user.dialog.reset_password.title")} {translate("crm.user.dialog.reset_password.description", { email: record.email, })}

{translate("crm.user.dialog.reset_password.reset_link")}
); }; export function SalesList() { return ( } sort={{ field: "first_name", order: "ASC" }} > ); } const SalesListLayout = () => { const { data, isPending, filterValues } = useListContext(); const { identity } = useGetIdentity(); const hasFilters = filterValues && Object.keys(filterValues).length > 0; // Show loading skeleton while identity or data is loading if (!identity || isPending) { return (
); } // Show empty state when no data if (!data?.length && !hasFilters) { return ; } return ( ); }; const SalesEmpty = () => { const appbarHeight = useAppBarHeight(); const translate = useTranslate(); return (
{translate("crm.user.empty.title")}
{translate("crm.user.empty.title")}

{translate("crm.user.empty.description")}

); };