PROFORMAS DELETE
This commit is contained in:
parent
8bbbe93d87
commit
e32dbfa3f1
@ -13,8 +13,10 @@ if (rootElement) {
|
|||||||
createRoot(rootElement).render(
|
createRoot(rootElement).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
|
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
|
||||||
<App />
|
<>
|
||||||
<TailwindIndicator />
|
<App />
|
||||||
|
<TailwindIndicator />
|
||||||
|
</>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,60 +1,52 @@
|
|||||||
|
// proformas/delete/controllers/use-delete-proforma-dialog-controller.ts
|
||||||
import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
||||||
import React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { useTranslation } from "../../../i18n";
|
import { useTranslation } from "../../../i18n";
|
||||||
import { type ProformaListRow, useDeleteProformaMutation } from "../../shared";
|
import type { DeleteProformaByIdParams } from "../../shared";
|
||||||
|
import { useProformaDeleteMutation } from "../../shared";
|
||||||
|
import type { DeleteProformaTarget } from "../entities";
|
||||||
|
|
||||||
|
type ConfirmStep = "initial" | "second";
|
||||||
|
|
||||||
interface DeleteProformaDialogState {
|
interface DeleteProformaDialogState {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
proformas: ProformaListRow[];
|
targets: DeleteProformaTarget[];
|
||||||
isSubmitting: boolean;
|
confirmStep: ConfirmStep;
|
||||||
requiresSecondConfirm: boolean;
|
|
||||||
confirmStep: "initial" | "second";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const INITIAL_STATE: DeleteProformaDialogState = {
|
const INITIAL_STATE: DeleteProformaDialogState = {
|
||||||
open: false,
|
open: false,
|
||||||
proformas: [],
|
targets: [],
|
||||||
isSubmitting: false,
|
|
||||||
requiresSecondConfirm: false,
|
|
||||||
confirmStep: "initial",
|
confirmStep: "initial",
|
||||||
};
|
};
|
||||||
|
|
||||||
const SECOND_CONFIRM_THRESHOLD = 5;
|
const buildDeleteParams = (target: DeleteProformaTarget): DeleteProformaByIdParams => ({
|
||||||
|
id: target.id,
|
||||||
|
});
|
||||||
|
|
||||||
function canSubmitDelete(isSubmitting: boolean, proformas: ProformaListRow[]): boolean {
|
const getProformaLabel = (target: DeleteProformaTarget) => target.reference || `#${target.id}`;
|
||||||
return !isSubmitting && proformas.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldMoveToSecondConfirmStep(
|
export const useDeleteProformaDialogController = () => {
|
||||||
requiresSecondConfirm: boolean,
|
|
||||||
confirmStep: "initial" | "second"
|
|
||||||
): boolean {
|
|
||||||
return requiresSecondConfirm && confirmStep === "initial";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDeleteProformaDialogController() {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { deleteProforma } = useDeleteProformaMutation();
|
const deleteMutation = useProformaDeleteMutation();
|
||||||
|
|
||||||
const [state, setState] = React.useState<DeleteProformaDialogState>(INITIAL_STATE);
|
const [state, setState] = React.useState<DeleteProformaDialogState>(INITIAL_STATE);
|
||||||
const { isSubmitting, proformas, requiresSecondConfirm, confirmStep } = state;
|
|
||||||
|
|
||||||
const openDialog = React.useCallback((proformas: ProformaListRow[]) => {
|
const openDialog = React.useCallback((targets: DeleteProformaTarget[]) => {
|
||||||
const requiresSecondConfirm = proformas.length > SECOND_CONFIRM_THRESHOLD;
|
if (targets.length === 0) return;
|
||||||
|
|
||||||
setState({
|
setState({
|
||||||
open: true,
|
open: true,
|
||||||
proformas,
|
targets,
|
||||||
isSubmitting: false,
|
|
||||||
requiresSecondConfirm,
|
|
||||||
confirmStep: "initial",
|
confirmStep: "initial",
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const closeDialog = React.useCallback(() => {
|
const closeDialog = React.useCallback(() => {
|
||||||
|
if (deleteMutation.isPending) return;
|
||||||
setState(INITIAL_STATE);
|
setState(INITIAL_STATE);
|
||||||
}, []);
|
}, [deleteMutation.isPending]);
|
||||||
|
|
||||||
const moveToSecondConfirmStep = React.useCallback(() => {
|
const moveToSecondConfirmStep = React.useCallback(() => {
|
||||||
setState((current) => ({
|
setState((current) => ({
|
||||||
@ -63,124 +55,109 @@ export function useDeleteProformaDialogController() {
|
|||||||
}));
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const deleteSelectedProformas = React.useCallback(
|
const notifySuccess = React.useCallback(
|
||||||
async (proformas: ProformaListRow[]) => {
|
(targets: DeleteProformaTarget[]) => {
|
||||||
const results = await Promise.allSettled(
|
if (targets.length === 1) {
|
||||||
proformas.map((proforma) =>
|
|
||||||
deleteProforma({
|
|
||||||
proformaId: proforma.id,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const successCount = results.filter((result) => result.status === "fulfilled").length;
|
|
||||||
const errorCount = results.length - successCount;
|
|
||||||
|
|
||||||
return {
|
|
||||||
successCount,
|
|
||||||
errorCount,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
[deleteProforma]
|
|
||||||
);
|
|
||||||
|
|
||||||
const notifyDeleteResult = React.useCallback(
|
|
||||||
(proformas: ProformaListRow[], successCount: number, errorCount: number) => {
|
|
||||||
if (proformas.length === 1 && successCount === 1) {
|
|
||||||
const proforma = proformas[0];
|
|
||||||
|
|
||||||
showSuccessToast(
|
showSuccessToast(
|
||||||
t("pages.proformas.delete.successTitle"),
|
t("pages.proformas.delete.successTitle"),
|
||||||
t("pages.proformas.delete.successSingleMessage", {
|
t("pages.proformas.delete.successSingleMessage", {
|
||||||
reference: proforma.reference || `#${proforma.id}`,
|
reference: getProformaLabel(targets[0]),
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (successCount > 0) {
|
|
||||||
showSuccessToast(
|
|
||||||
t("pages.proformas.delete.successTitle"),
|
|
||||||
t("pages.proformas.delete.successMultipleMessage", {
|
|
||||||
count: successCount,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCount > 0) {
|
showSuccessToast(
|
||||||
|
t("pages.proformas.delete.successTitle"),
|
||||||
|
t("pages.proformas.delete.successMultipleMessage", {
|
||||||
|
count: targets.length,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[t]
|
||||||
|
);
|
||||||
|
|
||||||
|
const notifyPartialError = React.useCallback(
|
||||||
|
(targets: DeleteProformaTarget[], errorCount: number) => {
|
||||||
|
if (targets.length === 1) {
|
||||||
showErrorToast(
|
showErrorToast(
|
||||||
t("pages.proformas.delete.errorTitle"),
|
t("pages.proformas.delete.errorTitle"),
|
||||||
proformas.length === 1
|
t("pages.proformas.delete.errorSingleMessage")
|
||||||
? t("pages.proformas.delete.errorSingleMessage")
|
|
||||||
: t("pages.proformas.delete.errorMultipleMessage", {
|
|
||||||
count: errorCount,
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showErrorToast(
|
||||||
|
t("pages.proformas.delete.errorTitle"),
|
||||||
|
t("pages.proformas.delete.errorMultipleMessage", {
|
||||||
|
count: errorCount,
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[t]
|
[t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const submitDelete = React.useCallback(async () => {
|
const submitDelete = React.useCallback(async () => {
|
||||||
setState((current) => ({
|
const { targets } = state;
|
||||||
...current,
|
|
||||||
isSubmitting: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
try {
|
const results = await Promise.allSettled(
|
||||||
const { successCount, errorCount } = await deleteSelectedProformas(proformas);
|
targets.map((target) => deleteMutation.mutateAsync(buildDeleteParams(target)))
|
||||||
|
);
|
||||||
|
|
||||||
notifyDeleteResult(proformas, successCount, errorCount);
|
const successCount = results.filter((result) => result.status === "fulfilled").length;
|
||||||
|
const errorCount = results.length - successCount;
|
||||||
|
|
||||||
if (errorCount === 0) {
|
if (successCount > 0) {
|
||||||
closeDialog();
|
const fulfilledTargets = targets.filter((_, index) => results[index]?.status === "fulfilled");
|
||||||
return;
|
const rejectedTargets = targets.filter((_, index) => results[index]?.status === "rejected");
|
||||||
|
|
||||||
|
if (fulfilledTargets.length > 0) {
|
||||||
|
notifySuccess(fulfilledTargets);
|
||||||
}
|
}
|
||||||
|
|
||||||
setState((current) => ({
|
if (rejectedTargets.length > 0) {
|
||||||
...current,
|
notifyPartialError(targets, rejectedTargets.length);
|
||||||
isSubmitting: false,
|
return;
|
||||||
}));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorCount > 0) {
|
||||||
|
notifyPartialError(targets, errorCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(INITIAL_STATE);
|
||||||
|
}, [deleteMutation, notifyPartialError, notifySuccess, state]);
|
||||||
|
|
||||||
|
const confirmDelete = React.useCallback(async () => {
|
||||||
|
if (deleteMutation.isPending || state.targets.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.confirmStep === "initial") {
|
||||||
|
moveToSecondConfirmStep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await submitDelete();
|
||||||
} catch {
|
} catch {
|
||||||
showErrorToast(
|
showErrorToast(
|
||||||
t("pages.proformas.delete.errorTitle"),
|
t("pages.proformas.delete.errorTitle"),
|
||||||
t("pages.proformas.delete.errorUnexpectedMessage")
|
t("pages.proformas.delete.errorUnexpectedMessage")
|
||||||
);
|
);
|
||||||
|
|
||||||
setState((current) => ({
|
|
||||||
...current,
|
|
||||||
isSubmitting: false,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}, [closeDialog, deleteSelectedProformas, notifyDeleteResult, proformas, t]);
|
}, [deleteMutation.isPending, moveToSecondConfirmStep, state, submitDelete, t]);
|
||||||
|
|
||||||
const confirmDelete = React.useCallback(async () => {
|
|
||||||
if (!canSubmitDelete(isSubmitting, proformas)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldMoveToSecondConfirmStep(requiresSecondConfirm, confirmStep)) {
|
|
||||||
moveToSecondConfirmStep();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await submitDelete();
|
|
||||||
}, [
|
|
||||||
moveToSecondConfirmStep,
|
|
||||||
submitDelete,
|
|
||||||
isSubmitting,
|
|
||||||
proformas,
|
|
||||||
requiresSecondConfirm,
|
|
||||||
confirmStep,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
open: state.open,
|
open: state.open,
|
||||||
proformas: state.proformas,
|
targets: state.targets,
|
||||||
isSubmitting: state.isSubmitting,
|
isSubmitting: deleteMutation.isPending,
|
||||||
requiresSecondConfirm: state.requiresSecondConfirm,
|
|
||||||
isSecondConfirmStep: state.confirmStep === "second",
|
isSecondConfirmStep: state.confirmStep === "second",
|
||||||
isBulkDelete: state.proformas.length > 1,
|
isBulkDelete: state.targets.length > 1,
|
||||||
|
|
||||||
openDialog,
|
openDialog,
|
||||||
closeDialog,
|
closeDialog,
|
||||||
confirmDelete,
|
confirmDelete,
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|||||||
@ -0,0 +1,4 @@
|
|||||||
|
export interface DeleteProformaTarget {
|
||||||
|
id: string;
|
||||||
|
reference?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./delete-proforma-target.entity";
|
||||||
@ -10,46 +10,60 @@ import {
|
|||||||
} from "@repo/shadcn-ui/components";
|
} from "@repo/shadcn-ui/components";
|
||||||
|
|
||||||
import { useTranslation } from "../../../../i18n";
|
import { useTranslation } from "../../../../i18n";
|
||||||
import type { ProformaListRow } from "../../../shared";
|
import type { DeleteProformaTarget } from "../../entities";
|
||||||
|
|
||||||
interface DeleteProformaDialogProps {
|
interface DeleteProformaDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
proformas: ProformaListRow[];
|
targets: DeleteProformaTarget[];
|
||||||
isSubmitting: boolean;
|
isSubmitting: boolean;
|
||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
requiresSecondConfirm: boolean;
|
|
||||||
isSecondConfirmStep: boolean;
|
isSecondConfirmStep: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DeleteProformaDialog({
|
const getTargetLabel = (target: DeleteProformaTarget) => target.reference || `#${target.id}`;
|
||||||
|
|
||||||
|
export const DeleteProformaDialog = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
proformas,
|
targets,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
requiresSecondConfirm,
|
|
||||||
isSecondConfirmStep,
|
isSecondConfirmStep,
|
||||||
}: DeleteProformaDialogProps) {
|
}: DeleteProformaDialogProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const total = proformas.length;
|
const total = targets.length;
|
||||||
const isSingle = total === 1;
|
const isSingle = total === 1;
|
||||||
const firstProforma = proformas[0];
|
const firstTarget = targets[0];
|
||||||
|
|
||||||
const title = isSecondConfirmStep
|
const title = isSecondConfirmStep
|
||||||
? t("proformas.delete_proforma_dialog.second_confirm_title", { count: total })
|
? isSingle
|
||||||
: isSingle
|
? t("components.delete_proforma_dialog.second_confirm_single_title", {
|
||||||
? t("proformas.delete_proforma_dialog.single_title", {
|
reference: getTargetLabel(firstTarget),
|
||||||
reference: firstProforma?.reference ?? `#${firstProforma?.id}`,
|
|
||||||
})
|
})
|
||||||
: t("proformas.delete_proforma_dialog.multiple_title", { count: total });
|
: t("components.delete_proforma_dialog.second_confirm_multiple_title", {
|
||||||
|
count: total,
|
||||||
|
})
|
||||||
|
: isSingle
|
||||||
|
? t("components.delete_proforma_dialog.single_title", {
|
||||||
|
reference: getTargetLabel(firstTarget),
|
||||||
|
})
|
||||||
|
: t("components.delete_proforma_dialog.multiple_title", {
|
||||||
|
count: total,
|
||||||
|
});
|
||||||
|
|
||||||
const description = isSecondConfirmStep
|
const description = isSecondConfirmStep
|
||||||
? t("proformas.delete_proforma_dialog.second_confirm_description", { count: total })
|
? isSingle
|
||||||
|
? t("components.delete_proforma_dialog.second_confirm_single_description")
|
||||||
|
: t("components.delete_proforma_dialog.second_confirm_multiple_description", {
|
||||||
|
count: total,
|
||||||
|
})
|
||||||
: isSingle
|
: isSingle
|
||||||
? t("proformas.delete_proforma_dialog.single_description")
|
? t("components.delete_proforma_dialog.single_description")
|
||||||
: t("proformas.delete_proforma_dialog.multiple_description", { count: total });
|
: t("components.delete_proforma_dialog.multiple_description", {
|
||||||
|
count: total,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog
|
<AlertDialog
|
||||||
@ -65,45 +79,39 @@ export function DeleteProformaDialog({
|
|||||||
<AlertDialogDescription>{description}</AlertDialogDescription>
|
<AlertDialogDescription>{description}</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
|
|
||||||
{!isSecondConfirmStep && total > 1 && (
|
{!isSecondConfirmStep && total > 1 ? (
|
||||||
<div className="mt-4 max-h-48 overflow-y-auto rounded-md border p-3 text-sm">
|
<div className="mt-4 max-h-48 overflow-y-auto rounded-md border p-3 text-sm">
|
||||||
<ul className="space-y-1">
|
<ul className="space-y-1">
|
||||||
{proformas.map((proforma) => (
|
{targets.map((target) => (
|
||||||
<li className="flex justify-between text-muted-foreground" key={proforma.id}>
|
<li className="text-muted-foreground" key={target.id}>
|
||||||
<span>
|
{t("components.delete_proforma_dialog.list_item", {
|
||||||
{t("proformas.delete_proforma_dialog.list_item", {
|
reference: getTargetLabel(target),
|
||||||
reference: proforma.reference ?? `#${proforma.id}`,
|
})}
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : null}
|
||||||
|
|
||||||
<AlertDialogFooter className="sm:justify-between">
|
<AlertDialogFooter className="sm:justify-between">
|
||||||
<Button disabled={isSubmitting} onClick={() => onOpenChange(false)} variant="outline">
|
<Button disabled={isSubmitting} onClick={() => onOpenChange(false)} variant="outline">
|
||||||
{t("proformas.delete_proforma_dialog.cancel")}
|
{t("components.delete_proforma_dialog.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button disabled={isSubmitting} onClick={onConfirm} variant="destructive">
|
<Button disabled={isSubmitting || total === 0} onClick={onConfirm} variant="destructive">
|
||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
<>
|
<>
|
||||||
<Spinner className="mr-2 size-4" />
|
<Spinner className="mr-2 size-4" />
|
||||||
{t("proformas.delete_proforma_dialog.deleting")}
|
{t("components.delete_proforma_dialog.deleting")}
|
||||||
</>
|
</>
|
||||||
) : isSecondConfirmStep ? (
|
) : isSecondConfirmStep ? (
|
||||||
t("proformas.delete_proforma_dialog.confirm_mass_delete")
|
t("components.delete_proforma_dialog.confirm_delete")
|
||||||
) : isSingle ? (
|
|
||||||
t("proformas.delete_proforma_dialog.delete")
|
|
||||||
) : requiresSecondConfirm ? (
|
|
||||||
t("proformas.delete_proforma_dialog.continue")
|
|
||||||
) : (
|
) : (
|
||||||
t("proformas.delete_proforma_dialog.delete_plural")
|
t("components.delete_proforma_dialog.continue")
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import type { Proforma, ProformaListRow } from "../../shared";
|
||||||
|
import type { DeleteProformaTarget } from "../entities";
|
||||||
|
|
||||||
|
export const buildDeleteProformaTargetFromListRow = (
|
||||||
|
proforma: ProformaListRow
|
||||||
|
): DeleteProformaTarget => ({
|
||||||
|
id: proforma.id,
|
||||||
|
reference: proforma.reference,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const buildDeleteProformaTargetFromEntity = (proforma: Proforma): DeleteProformaTarget => ({
|
||||||
|
id: proforma.id,
|
||||||
|
reference: proforma.reference,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const buildDeleteProformaTargetsFromListRows = (
|
||||||
|
proformas: ProformaListRow[]
|
||||||
|
): DeleteProformaTarget[] => proformas.map(buildDeleteProformaTargetFromListRow);
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./build-delete-proforma-targets";
|
||||||
@ -32,7 +32,7 @@ export const useListProformasController = () => {
|
|||||||
pageNumber: pageIndex,
|
pageNumber: pageIndex,
|
||||||
pageSize,
|
pageSize,
|
||||||
order: "desc",
|
order: "desc",
|
||||||
orderBy: "invoiceDate",
|
orderBy: "invoice_date",
|
||||||
filters:
|
filters:
|
||||||
statusFilter === "all" ? [] : [{ field: "status", operator: "eq", value: statusFilter }],
|
statusFilter === "all" ? [] : [{ field: "status", operator: "eq", value: statusFilter }],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -22,6 +22,7 @@ interface ProformasGridProps {
|
|||||||
export const ProformasGrid = ({
|
export const ProformasGrid = ({
|
||||||
data,
|
data,
|
||||||
loading,
|
loading,
|
||||||
|
fetching,
|
||||||
columns,
|
columns,
|
||||||
pageIndex,
|
pageIndex,
|
||||||
pageSize,
|
pageSize,
|
||||||
@ -32,7 +33,7 @@ export const ProformasGrid = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { items, totalItems } = data || { items: [], totalItems: 0 };
|
const { items, totalItems } = data || { items: [], totalItems: 0 };
|
||||||
|
|
||||||
if (loading)
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<SkeletonDataTable
|
<SkeletonDataTable
|
||||||
columns={columns.length}
|
columns={columns.length}
|
||||||
@ -41,6 +42,7 @@ export const ProformasGrid = ({
|
|||||||
showFooter
|
showFooter
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
@ -51,7 +53,6 @@ export const ProformasGrid = ({
|
|||||||
manualPagination
|
manualPagination
|
||||||
onPageChange={onPageChange}
|
onPageChange={onPageChange}
|
||||||
onPageSizeChange={onPageSizeChange}
|
onPageSizeChange={onPageSizeChange}
|
||||||
onRowClick={(row, _index) => onRowClick?.(row.id)}
|
|
||||||
pageIndex={pageIndex}
|
pageIndex={pageIndex}
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
totalItems={totalItems}
|
totalItems={totalItems}
|
||||||
|
|||||||
@ -96,7 +96,6 @@ export function useProformasGridColumns(
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<ProformaStatusBadge status={proforma.status} />
|
<ProformaStatusBadge status={proforma.status} />
|
||||||
|
|
||||||
{/* Enlace discreto a factura real */}
|
{/* Enlace discreto a factura real */}
|
||||||
{isIssued && (
|
{isIssued && (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
@ -105,12 +104,13 @@ export function useProformasGridColumns(
|
|||||||
<Button
|
<Button
|
||||||
asChild
|
asChild
|
||||||
className="size-6 text-foreground hover:text-primary"
|
className="size-6 text-foreground hover:text-primary"
|
||||||
onClick={() => actionHandlers.onLinkedInvoiceClick?.(proforma)}
|
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
<ExternalLinkIcon />
|
<a href={`/facturas/${invoiceId}`}>
|
||||||
<span className="sr-only">Ver factura {invoiceId}</span>
|
<ExternalLinkIcon />
|
||||||
|
<span className="sr-only">Ver factura {invoiceId}</span>
|
||||||
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>Ver factura {invoiceId}</TooltipContent>
|
<TooltipContent>Ver factura {invoiceId}</TooltipContent>
|
||||||
@ -148,8 +148,6 @@ export function useProformasGridColumns(
|
|||||||
>
|
>
|
||||||
{proforma.recipient.name}
|
{proforma.recipient.name}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<br />
|
|
||||||
<div className="text-xs text-muted-foreground">{proforma.recipient.tin}</div>
|
<div className="text-xs text-muted-foreground">{proforma.recipient.tin}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -15,8 +15,9 @@ import { FilterIcon, PlusIcon } from "lucide-react";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { useTranslation } from "../../../../i18n";
|
import { useTranslation } from "../../../../i18n";
|
||||||
import { ChangeStatusDialog } from "../../../change-status";
|
import { DeleteProformaDialog, useDeleteProformaDialogController } from "../../../delete";
|
||||||
import { ProformaIssueDialog } from "../../../issue-proforma";
|
import { buildDeleteProformaTargetFromListRow } from "../../../delete/utils";
|
||||||
|
import type { ProformaListRow } from "../../../shared";
|
||||||
import { useListProformasPageController } from "../../controllers";
|
import { useListProformasPageController } from "../../controllers";
|
||||||
import { ProformaSummaryPanel, ProformasGrid, useProformasGridColumns } from "../blocks";
|
import { ProformaSummaryPanel, ProformasGrid, useProformasGridColumns } from "../blocks";
|
||||||
|
|
||||||
@ -25,12 +26,17 @@ export const ListProformasPage = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { listCtrl, panelCtrl } = useListProformasPageController();
|
const { listCtrl, panelCtrl } = useListProformasPageController();
|
||||||
|
const deleteDialogCtrl = useDeleteProformaDialogController();
|
||||||
|
|
||||||
|
const handleDeleteProforma = (proforma: ProformaListRow) => {
|
||||||
|
deleteDialogCtrl.openDialog([buildDeleteProformaTargetFromListRow(proforma)]);
|
||||||
|
};
|
||||||
|
|
||||||
const columns = useProformasGridColumns({
|
const columns = useProformasGridColumns({
|
||||||
onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`),
|
onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`),
|
||||||
onIssueClick: handleIssueProforma,
|
//onIssueClick: handleIssueProforma,
|
||||||
onDeleteClick: handleDeleteProforma,
|
onDeleteClick: handleDeleteProforma,
|
||||||
onChangeStatusClick: handleChangeStatusProforma,
|
//onChangeStatusClick: handleChangeStatusProforma,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isPanelOpen = panelCtrl.panelState.isOpen;
|
const isPanelOpen = panelCtrl.panelState.isOpen;
|
||||||
@ -143,32 +149,18 @@ export const ListProformasPage = () => {
|
|||||||
<div className="flex min-h-0 flex-1 overflow-hidden">{listContent}</div>
|
<div className="flex min-h-0 flex-1 overflow-hidden">{listContent}</div>
|
||||||
)}
|
)}
|
||||||
<>
|
<>
|
||||||
{/* Emitir factura */}
|
|
||||||
<ProformaIssueDialog
|
|
||||||
isSubmitting={issueDialogCtrl.isSubmitting}
|
|
||||||
onConfirm={issueDialogCtrl.confirmIssue}
|
|
||||||
onOpenChange={(open) => !open && issueDialogCtrl.closeDialog()}
|
|
||||||
open={issueDialogCtrl.open}
|
|
||||||
proforma={issueDialogCtrl.proforma}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Cambiar estado */}
|
|
||||||
<ChangeStatusDialog
|
|
||||||
isSubmitting={changeStatusDialogCtrl.isSubmitting}
|
|
||||||
onConfirm={changeStatusDialogCtrl.confirmChangeStatus}
|
|
||||||
onOpenChange={(open) => !open && changeStatusDialogCtrl.closeDialog()}
|
|
||||||
open={changeStatusDialogCtrl.open}
|
|
||||||
proformas={changeStatusDialogCtrl.proformas} // ← recibe el status seleccionado
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Eliminar */}
|
{/* Eliminar */}
|
||||||
<DeleteProformaDialog
|
<DeleteProformaDialog
|
||||||
|
isSecondConfirmStep={deleteDialogCtrl.isSecondConfirmStep}
|
||||||
isSubmitting={deleteDialogCtrl.isSubmitting}
|
isSubmitting={deleteDialogCtrl.isSubmitting}
|
||||||
onConfirm={deleteDialogCtrl.confirmDelete}
|
onConfirm={deleteDialogCtrl.confirmDelete}
|
||||||
onOpenChange={(open) => !open && deleteDialogCtrl.closeDialog()}
|
onOpenChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
deleteDialogCtrl.closeDialog();
|
||||||
|
}
|
||||||
|
}}
|
||||||
open={deleteDialogCtrl.open}
|
open={deleteDialogCtrl.open}
|
||||||
proformas={deleteDialogCtrl.proformas}
|
targets={deleteDialogCtrl.targets}
|
||||||
requireSecondConfirm={true}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
</AppContent>
|
</AppContent>
|
||||||
|
|||||||
@ -192,6 +192,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
"delete_proforma_dialog": {
|
||||||
|
"single_title": "Eliminar proforoma",
|
||||||
|
"single_description": "",
|
||||||
|
"second_confirm_single_title": "Confirmar eliminación",
|
||||||
|
"second_confirm_single_description": "",
|
||||||
|
"confirm_delete": "Confirmar eliminación",
|
||||||
|
"deleting": "Eliminando...",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"continue": "Eliminar"
|
||||||
|
},
|
||||||
"entity_selector": {
|
"entity_selector": {
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"select_entity": "Select entity",
|
"select_entity": "Select entity",
|
||||||
|
|||||||
@ -194,6 +194,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
"delete_proforma_dialog": {
|
||||||
|
"single_title": "Eliminar proforoma",
|
||||||
|
"single_description": "",
|
||||||
|
"second_confirm_single_title": "Confirmar eliminación",
|
||||||
|
"second_confirm_single_description": "",
|
||||||
|
"multiple_description": "",
|
||||||
|
"confirm_delete": "Confirmar eliminación",
|
||||||
|
"deleting": "Eliminando...",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"continue": "Eliminar",
|
||||||
|
"list_item": ""
|
||||||
|
},
|
||||||
"entity_selector": {
|
"entity_selector": {
|
||||||
"close": "Cerrar",
|
"close": "Cerrar",
|
||||||
"select_entity": "Seleccionar entidad",
|
"select_entity": "Seleccionar entidad",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user