PROFORMAS CAMBIO DE ESTADO
This commit is contained in:
parent
b2dee4ae8b
commit
cd0940cb20
@ -120,6 +120,27 @@
|
||||
"cancel": "Cancel",
|
||||
"continue": "Delete",
|
||||
"list_item": "Proforma {{reference}}"
|
||||
},
|
||||
"change_proforma_status_dialog": {
|
||||
"title": "Change proforma status",
|
||||
"single_description": "Select the new status for proforma {{reference}}.",
|
||||
"multiple_description": "Select the new status for {{count}} selected proformas.",
|
||||
"empty_available_statuses": "No status transitions are available for the selected proformas.",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Change status",
|
||||
"submitting": "Changing...",
|
||||
"success_title": "Status updated successfully",
|
||||
"success_single_description": "Proforma {{reference}} status has been updated successfully.",
|
||||
"success_multiple_description": "The status of {{count}} proformas has been updated.",
|
||||
"error_title": "Error changing status",
|
||||
"error_single_description": "The status of the selected proforma could not be changed.",
|
||||
"error_multiple_description": "The status of {{count}} proformas could not be changed.",
|
||||
"error_unexpected_description": "An unexpected error has occurred.",
|
||||
"legend": {
|
||||
"current": "Current status",
|
||||
"available": "Available statuses",
|
||||
"unavailable": "Unavailable statuses"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
|
||||
@ -121,6 +121,27 @@
|
||||
"cancel": "Cancelar",
|
||||
"continue": "Eliminar",
|
||||
"list_item": "Proforma {{reference}}"
|
||||
},
|
||||
"change_proforma_status_dialog": {
|
||||
"title": "Cambiar estado de la proforma",
|
||||
"single_description": "Selecciona el nuevo estado para la proforma {{reference}}.",
|
||||
"multiple_description": "Selecciona el nuevo estado para {{count}} proformas seleccionadas.",
|
||||
"empty_available_statuses": "No hay transiciones de estado disponibles para las proformas seleccionadas.",
|
||||
"cancel": "Cancelar",
|
||||
"confirm": "Cambiar estado",
|
||||
"submitting": "Cambiando...",
|
||||
"success_title": "Estado actualizado correctamente",
|
||||
"success_single_description": "La proforma {{reference}} ha cambiado de estado correctamente.",
|
||||
"success_multiple_description": "Se ha actualizado el estado de {{count}} proformas.",
|
||||
"error_title": "Error al cambiar el estado",
|
||||
"error_single_description": "No se ha podido cambiar el estado de la proforma seleccionada.",
|
||||
"error_multiple_description": "No se ha podido cambiar el estado de {{count}} proformas.",
|
||||
"error_unexpected_description": "Ha ocurrido un error inesperado.",
|
||||
"legend": {
|
||||
"current": "Estado actual",
|
||||
"available": "Estados disponibles",
|
||||
"unavailable": "Estados no disponibles"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
|
||||
@ -1,160 +1,122 @@
|
||||
// proformas/change-status/controllers/use-change-proforma-status-dialog.controller.ts
|
||||
import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
||||
import * as React from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { useTranslation } from "../../../i18n";
|
||||
import type { ProformaListRow } from "../../shared";
|
||||
import type { PROFORMA_STATUS } from "../../shared/entities";
|
||||
import { useChangeProformaStatusMutation } from "../../shared/hooks";
|
||||
import { type ProformaStatus, useProformaChangeStatusMutation } from "../../shared";
|
||||
import type { ChangeProformaStatusTarget } from "../entities";
|
||||
|
||||
interface ChangeStatusDialogState {
|
||||
open: boolean;
|
||||
proformas: ProformaListRow[];
|
||||
isSubmitting: boolean;
|
||||
targets: ChangeProformaStatusTarget[];
|
||||
}
|
||||
|
||||
const INITIAL_STATE: ChangeStatusDialogState = {
|
||||
open: false,
|
||||
proformas: [],
|
||||
isSubmitting: false,
|
||||
targets: [],
|
||||
};
|
||||
|
||||
function canChangeStatus(isSubmitting: boolean, proformas: ProformaListRow[]): boolean {
|
||||
return !isSubmitting && proformas.length > 0;
|
||||
}
|
||||
const canChangeStatus = (targets: ChangeProformaStatusTarget[]) => targets.length > 0;
|
||||
|
||||
export function useChangeProformaStatusDialogController() {
|
||||
export const useChangeProformaStatusDialogController = () => {
|
||||
const { t } = useTranslation();
|
||||
const { changeProformaStatus } = useChangeProformaStatusMutation();
|
||||
const changeStatusMutation = useProformaChangeStatusMutation();
|
||||
|
||||
const [state, setState] = React.useState<ChangeStatusDialogState>(INITIAL_STATE);
|
||||
const { isSubmitting, proformas } = state;
|
||||
const [state, setState] = useState<ChangeStatusDialogState>(INITIAL_STATE);
|
||||
|
||||
const openDialog = (targets: ChangeProformaStatusTarget[]) => {
|
||||
if (!canChangeStatus(targets)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const openDialog = React.useCallback((proformas: ProformaListRow[]) => {
|
||||
setState({
|
||||
open: true,
|
||||
proformas,
|
||||
isSubmitting: false,
|
||||
targets,
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
const closeDialog = React.useCallback(() => {
|
||||
const closeDialog = () => {
|
||||
setState(INITIAL_STATE);
|
||||
}, []);
|
||||
};
|
||||
|
||||
const changeStatusSelectedProformas = React.useCallback(
|
||||
async (proformas: ProformaListRow[], newStatus: PROFORMA_STATUS) => {
|
||||
const notifyChangeResult = (
|
||||
targets: ChangeProformaStatusTarget[],
|
||||
successCount: number,
|
||||
errorCount: number
|
||||
) => {
|
||||
if (targets.length === 1 && successCount === 1) {
|
||||
const target = targets[0];
|
||||
|
||||
showSuccessToast(
|
||||
t("proformas.change_proforma_status_dialog.success_title"),
|
||||
t("proformas.change_proforma_status_dialog.success_single_description", {
|
||||
reference: target.reference || `#${target.id}`,
|
||||
})
|
||||
);
|
||||
} else if (successCount > 0) {
|
||||
showSuccessToast(
|
||||
t("proformas.change_proforma_status_dialog.success_title"),
|
||||
t("proformas.change_proforma_status_dialog.success_multiple_description", {
|
||||
count: successCount,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (errorCount > 0) {
|
||||
showErrorToast(
|
||||
t("proformas.change_proforma_status_dialog.error_title"),
|
||||
targets.length === 1
|
||||
? t("proformas.change_proforma_status_dialog.error_single_description")
|
||||
: t("proformas.change_proforma_status_dialog.error_multiple_description", {
|
||||
count: errorCount,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmChangeStatus = async (newStatus: ProformaStatus) => {
|
||||
const targets = state.targets;
|
||||
|
||||
if (!canChangeStatus(targets) || changeStatusMutation.isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const results = await Promise.allSettled(
|
||||
proformas.map((proforma) =>
|
||||
changeProformaStatus({
|
||||
proformaId: proforma.id,
|
||||
newStatus,
|
||||
targets.map((target) =>
|
||||
changeStatusMutation.mutateAsync({
|
||||
id: target.id,
|
||||
data: {
|
||||
new_status: newStatus,
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const successCount = results.filter((result) => result.status === "fulfilled").length;
|
||||
|
||||
const errorCount = results.length - successCount;
|
||||
|
||||
return {
|
||||
successCount,
|
||||
errorCount,
|
||||
};
|
||||
},
|
||||
[changeProformaStatus]
|
||||
);
|
||||
notifyChangeResult(targets, successCount, errorCount);
|
||||
|
||||
const notifyChangeResult = React.useCallback(
|
||||
(proformas: ProformaListRow[], successCount: number, errorCount: number) => {
|
||||
if (proformas.length === 1 && successCount === 1) {
|
||||
const proforma = proformas[0];
|
||||
|
||||
showSuccessToast(
|
||||
t("pages.proformas.change_status.successTitle"),
|
||||
t("pages.proformas.change_status.successSingleMessage", {
|
||||
reference: proforma.reference || `#${proforma.id}`,
|
||||
})
|
||||
);
|
||||
} else if (successCount > 0) {
|
||||
showSuccessToast(
|
||||
t("pages.proformas.change_status.successTitle"),
|
||||
t("pages.proformas.change_status.successMultipleMessage", {
|
||||
count: successCount,
|
||||
})
|
||||
);
|
||||
if (errorCount === 0) {
|
||||
closeDialog();
|
||||
}
|
||||
|
||||
if (errorCount > 0) {
|
||||
showErrorToast(
|
||||
t("pages.proformas.change_status.errorTitle"),
|
||||
proformas.length === 1
|
||||
? t("pages.proformas.change_status.errorSingleMessage")
|
||||
: t("pages.proformas.change_status.errorMultipleMessage", {
|
||||
count: errorCount,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
[t]
|
||||
);
|
||||
|
||||
const changeStatus = React.useCallback(
|
||||
async (newStatus: PROFORMA_STATUS) => {
|
||||
setState((current) => ({
|
||||
...current,
|
||||
isSubmitting: true,
|
||||
}));
|
||||
|
||||
try {
|
||||
const { successCount, errorCount } = await changeStatusSelectedProformas(
|
||||
proformas,
|
||||
newStatus
|
||||
);
|
||||
|
||||
notifyChangeResult(proformas, successCount, errorCount);
|
||||
|
||||
if (errorCount === 0) {
|
||||
closeDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
setState((current) => ({
|
||||
...current,
|
||||
isSubmitting: false,
|
||||
}));
|
||||
} catch {
|
||||
showErrorToast(
|
||||
t("pages.proformas.change_status.errorTitle"),
|
||||
t("pages.proformas.change_status.errorUnexpectedMessage")
|
||||
);
|
||||
|
||||
setState((current) => ({
|
||||
...current,
|
||||
isSubmitting: false,
|
||||
}));
|
||||
}
|
||||
},
|
||||
[closeDialog, changeStatusSelectedProformas, notifyChangeResult, proformas, t]
|
||||
);
|
||||
|
||||
const confirmChangeStatus = React.useCallback(
|
||||
async (newStatus: PROFORMA_STATUS) => {
|
||||
if (!canChangeStatus(isSubmitting, proformas)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await changeStatus(newStatus);
|
||||
},
|
||||
[changeStatus, isSubmitting, proformas]
|
||||
);
|
||||
} catch {
|
||||
showErrorToast(
|
||||
t("proformas.change_proforma_status_dialog.error_title"),
|
||||
t("proformas.change_proforma_status_dialog.error_unexpected_description")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
open: state.open,
|
||||
proformas: state.proformas,
|
||||
isSubmitting: state.isSubmitting,
|
||||
isBulkDelete: state.proformas.length > 1,
|
||||
|
||||
targets: state.targets,
|
||||
isSubmitting: changeStatusMutation.isPending,
|
||||
isBulkChange: state.targets.length > 1,
|
||||
openDialog,
|
||||
closeDialog,
|
||||
confirmChangeStatus,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import type { ProformaStatus } from "../../shared";
|
||||
|
||||
/**
|
||||
* Representa la proforma a la que se le va a cambiar de estado.
|
||||
*/
|
||||
|
||||
export interface ChangeProformaStatusTarget {
|
||||
id: string;
|
||||
reference: string;
|
||||
status: ProformaStatus;
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
export * from './change-proforma-status-target.entity';
|
||||
@ -1,3 +1,4 @@
|
||||
// proformas/change-status/helpers/proforma-status-ui.ts
|
||||
import {
|
||||
CheckCircle2Icon,
|
||||
FileCheckIcon,
|
||||
|
||||
@ -8,21 +8,22 @@ import {
|
||||
DialogTitle,
|
||||
Spinner,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { useTranslation } from "../../../i18n";
|
||||
import { PROFORMA_STATUS, PROFORMA_STATUS_TRANSITIONS, type ProformaListRow } from "../../shared";
|
||||
import { PROFORMA_STATUS, type ProformaStatus } from "../../shared";
|
||||
import type { ChangeProformaStatusTarget } from "../entities";
|
||||
import { getProformaStatusIcon } from "../helpers";
|
||||
import { getCommonAvailableProformaStatuses } from "../utils";
|
||||
|
||||
import { StatusNode, TimelineConnector } from "./components";
|
||||
import { StatusLegend, StatusNode, TimelineConnector } from "./components";
|
||||
|
||||
interface ChangeStatusDialogProps {
|
||||
interface ChangeProformaStatusDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
proformas: ProformaListRow[];
|
||||
targetStatus?: string;
|
||||
targets: ChangeProformaStatusTarget[];
|
||||
isSubmitting: boolean;
|
||||
onConfirm: (status: PROFORMA_STATUS) => void;
|
||||
onConfirm: (status: ProformaStatus) => void;
|
||||
}
|
||||
|
||||
const STATUS_FLOW = [
|
||||
@ -63,84 +64,86 @@ const STATUS_FLOW = [
|
||||
},
|
||||
] as const;
|
||||
|
||||
export function ChangeStatusDialog({
|
||||
export const ChangeProformaStatusDialog = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
proformas,
|
||||
targets,
|
||||
isSubmitting,
|
||||
onConfirm,
|
||||
}: ChangeStatusDialogProps) {
|
||||
}: ChangeProformaStatusDialogProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [selectedStatus, setSelectedStatus] = useState<PROFORMA_STATUS | null>(null);
|
||||
const [selectedStatus, setSelectedStatus] = useState<ProformaStatus | null>(null);
|
||||
|
||||
// Obtener estados disponibles comunes para todas las proformas seleccionadas
|
||||
const getAvailableStatuses = () => {
|
||||
if (!proformas || proformas.length === 0) return [];
|
||||
|
||||
// Intersección de estados disponibles para todas las proformas
|
||||
const firstProforma = proformas[0];
|
||||
let availableStatuses =
|
||||
PROFORMA_STATUS_TRANSITIONS[firstProforma.status as PROFORMA_STATUS] || [];
|
||||
|
||||
for (let i = 1; i < proformas.length; i++) {
|
||||
const currentStatuses =
|
||||
PROFORMA_STATUS_TRANSITIONS[proformas[i].status as PROFORMA_STATUS] || [];
|
||||
availableStatuses = availableStatuses.filter((status) => currentStatuses.includes(status));
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setSelectedStatus(null);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
return availableStatuses;
|
||||
};
|
||||
const availableStatuses = useMemo(() => getCommonAvailableProformaStatuses(targets), [targets]);
|
||||
|
||||
const availableStatuses = getAvailableStatuses();
|
||||
|
||||
const currentStatus = proformas.length === 1 ? proformas[0].status : null;
|
||||
const currentStatus = targets.length === 1 ? targets[0].status : null;
|
||||
|
||||
const getStatusType = (
|
||||
status: PROFORMA_STATUS
|
||||
status: ProformaStatus
|
||||
): "past" | "current" | "available" | "unavailable" => {
|
||||
if (currentStatus === status) return "current";
|
||||
if (currentStatus === status) {
|
||||
return "current";
|
||||
}
|
||||
|
||||
// Si no hay un estado actual único, solo mostrar disponibles
|
||||
if (!currentStatus) {
|
||||
return availableStatuses.includes(status) ? "available" : "unavailable";
|
||||
}
|
||||
|
||||
// Determinar si es pasado basado en el flujo lógico
|
||||
const currentIndex = STATUS_FLOW.findIndex((s) => s.status === currentStatus);
|
||||
const statusIndex = STATUS_FLOW.findIndex((s) => s.status === status);
|
||||
const currentIndex = STATUS_FLOW.findIndex((item) => item.status === currentStatus);
|
||||
const statusIndex = STATUS_FLOW.findIndex((item) => item.status === status);
|
||||
|
||||
if (statusIndex < currentIndex) {
|
||||
return "past";
|
||||
}
|
||||
|
||||
if (availableStatuses.includes(status)) {
|
||||
return "available";
|
||||
}
|
||||
|
||||
if (statusIndex < currentIndex) return "past";
|
||||
if (availableStatuses.includes(status)) return "available";
|
||||
return "unavailable";
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
onOpenChange={(nextOpen) => {
|
||||
if (isSubmitting) return;
|
||||
if (isSubmitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
onOpenChange(nextOpen);
|
||||
}}
|
||||
open={open}
|
||||
>
|
||||
<DialogContent className="sm:max-w-4xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Cambiar estado de la proforma</DialogTitle>
|
||||
<DialogTitle>{t("proformas.change_proforma_status_dialog.title")}</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
{proformas.length === 1
|
||||
? `Selecciona el nuevo estado para la proforma #${proformas[0].reference}`
|
||||
: `Selecciona el nuevo estado para ${proformas.length} proformas seleccionadas`}
|
||||
{targets.length === 1
|
||||
? t("proformas.change_proforma_status_dialog.single_description", {
|
||||
reference: targets[0].reference,
|
||||
})
|
||||
: t("proformas.change_proforma_status_dialog.multiple_description", {
|
||||
count: targets.length,
|
||||
})}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{availableStatuses.length === 0 ? (
|
||||
<div className="py-8 text-center text-sm text-muted-foreground">
|
||||
No hay transiciones de estado disponibles para las proformas seleccionadas.
|
||||
{t("proformas.change_proforma_status_dialog.empty_available_statuses")}
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-6">
|
||||
<div className="relative">
|
||||
<div className="relative grid grid-cols-5 gap-2 mb-8">
|
||||
{STATUS_FLOW.map((statusConfig, index) => {
|
||||
<div className="relative mb-8 grid grid-cols-5 gap-2">
|
||||
{STATUS_FLOW.map((statusConfig) => {
|
||||
const statusType = getStatusType(statusConfig.status);
|
||||
const isSelected = selectedStatus === statusConfig.status;
|
||||
const isClickable = statusType === "available";
|
||||
@ -176,31 +179,39 @@ export function ChangeStatusDialog({
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<StatusLegend hasCurrentStatus={currentStatus !== null} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DialogFooter className="sm:justify-between">
|
||||
<Button disabled={isSubmitting} onClick={() => onOpenChange(false)} variant="outline">
|
||||
Cancelar
|
||||
{t("proformas.change_proforma_status_dialog.cancel")}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabled={!selectedStatus || availableStatuses.length === 0 || isSubmitting}
|
||||
onClick={() => {
|
||||
if (!selectedStatus) return;
|
||||
onConfirm(selectedStatus); // ← enviamos el estado al controlador
|
||||
if (!selectedStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
onConfirm(selectedStatus);
|
||||
}}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Spinner className="mr-2 size-4" />
|
||||
Cambiando...
|
||||
<Spinner aria-hidden className="mr-2 size-4" />
|
||||
<span aria-live="polite">
|
||||
{t("proformas.change_proforma_status_dialog.submitting")}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
"Cambiar estado"
|
||||
t("proformas.change_proforma_status_dialog.confirm")
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
|
||||
interface StatusLegendProps {
|
||||
@ -8,22 +6,31 @@ interface StatusLegendProps {
|
||||
|
||||
export const StatusLegend = ({ hasCurrentStatus }: StatusLegendProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="mt-8 p-4 bg-muted/50 rounded-lg">
|
||||
<div className="mt-8 rounded-lg bg-muted/50 p-4">
|
||||
<div className="flex items-start gap-6 text-sm">
|
||||
{hasCurrentStatus && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="size-3 rounded-full bg-amber-500 ring-2 ring-amber-200" />
|
||||
<span className="text-muted-foreground">Estado actual</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t("proformas.change_proforma_status_dialog.legend.current")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="size-3 rounded-full bg-linear-to-r from-blue-400 to-green-400" />
|
||||
<span className="text-muted-foreground">Estados disponibles (colores originales)</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t("proformas.change_proforma_status_dialog.legend.available")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="size-3 rounded-full bg-gray-300 opacity-50" />
|
||||
<span className="text-muted-foreground">Estados no disponibles</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t("proformas.change_proforma_status_dialog.legend.unavailable")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
import { CheckCircle2, type LucideIcon } from "lucide-react";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
import type { PROFORMA_STATUS } from "../../../shared";
|
||||
|
||||
interface StatusNodeProps {
|
||||
status: PROFORMA_STATUS;
|
||||
label: string;
|
||||
description: string;
|
||||
icon: LucideIcon;
|
||||
@ -21,7 +15,6 @@ interface StatusNodeProps {
|
||||
}
|
||||
|
||||
export const StatusNode = ({
|
||||
status,
|
||||
label,
|
||||
description,
|
||||
icon: Icon,
|
||||
@ -33,7 +26,6 @@ export const StatusNode = ({
|
||||
isSelected,
|
||||
onClick,
|
||||
}: StatusNodeProps) => {
|
||||
const { t } = useTranslation();
|
||||
const isClickable = statusType === "available";
|
||||
|
||||
return (
|
||||
@ -42,7 +34,7 @@ export const StatusNode = ({
|
||||
className={cn(
|
||||
"relative z-10 flex size-24 flex-col items-center justify-center rounded-xl border-2 transition-all",
|
||||
statusType === "past" && ["border-gray-200 bg-gray-50", "opacity-50 saturate-50"],
|
||||
statusType === "current" && [activeBorder, activeBg, "border-4 scale-110 shadow-lg"],
|
||||
statusType === "current" && [activeBorder, activeBg, "scale-110 border-4 shadow-lg"],
|
||||
statusType === "available" && [
|
||||
activeBorder,
|
||||
activeBg,
|
||||
@ -52,7 +44,7 @@ export const StatusNode = ({
|
||||
],
|
||||
statusType === "unavailable" && [
|
||||
"border-gray-200 bg-gray-50",
|
||||
"opacity-30 saturate-0 cursor-not-allowed",
|
||||
"cursor-not-allowed opacity-30 saturate-0",
|
||||
]
|
||||
)}
|
||||
disabled={!isClickable}
|
||||
@ -61,7 +53,7 @@ export const StatusNode = ({
|
||||
>
|
||||
<Icon
|
||||
className={cn(
|
||||
"size-8 mb-1",
|
||||
"mb-1 size-8",
|
||||
statusType === "past" && "text-gray-400",
|
||||
statusType === "current" && activeColor,
|
||||
statusType === "available" && activeColor,
|
||||
@ -70,7 +62,7 @@ export const StatusNode = ({
|
||||
/>
|
||||
|
||||
{statusType === "current" && (
|
||||
<div className="absolute -top-3 left-1/2 -translate-x-1/2 px-2 py-0.5 rounded-full bg-primary text-primary-foreground text-[10px] font-bold tracking-wider shadow-md">
|
||||
<div className="absolute -top-3 left-1/2 rounded-full bg-primary px-2 py-0.5 text-[10px] font-bold tracking-wider text-primary-foreground shadow-md -translate-x-1/2">
|
||||
ACTUAL
|
||||
</div>
|
||||
)}
|
||||
@ -78,7 +70,7 @@ export const StatusNode = ({
|
||||
{isSelected && statusType !== "current" && (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute -top-2 -right-2 size-6 rounded-full border-2 border-white flex items-center justify-center",
|
||||
"absolute -top-2 -right-2 flex size-6 items-center justify-center rounded-full border-2 border-white",
|
||||
activeBg
|
||||
)}
|
||||
>
|
||||
@ -87,7 +79,7 @@ export const StatusNode = ({
|
||||
)}
|
||||
</button>
|
||||
|
||||
<div className="mt-3 text-center space-y-1 px-1">
|
||||
<div className="mt-3 space-y-1 px-1 text-center">
|
||||
<p
|
||||
className={cn(
|
||||
"text-sm font-medium",
|
||||
@ -99,7 +91,8 @@ export const StatusNode = ({
|
||||
>
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground line-clamp-2">{description}</p>
|
||||
|
||||
<p className="line-clamp-2 text-xs text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
import { PROFORMA_STATUS_TRANSITIONS, type ProformaStatus } from "../../shared";
|
||||
import type { ChangeProformaStatusTarget } from "../entities";
|
||||
|
||||
export const getCommonAvailableProformaStatuses = (
|
||||
targets: ChangeProformaStatusTarget[]
|
||||
): ProformaStatus[] => {
|
||||
if (targets.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let availableStatuses = PROFORMA_STATUS_TRANSITIONS[targets[0].status] ?? [];
|
||||
|
||||
for (let index = 1; index < targets.length; index += 1) {
|
||||
const currentStatuses = PROFORMA_STATUS_TRANSITIONS[targets[index].status] ?? [];
|
||||
|
||||
availableStatuses = availableStatuses.filter((status) => currentStatuses.includes(status));
|
||||
}
|
||||
|
||||
return availableStatuses;
|
||||
};
|
||||
@ -0,0 +1,2 @@
|
||||
export * from './get-common-available-proforma-statuses';
|
||||
export * from './prepare-change-proforma-status-targets';
|
||||
@ -0,0 +1,20 @@
|
||||
import type { Proforma, ProformaListRow } from "../../shared";
|
||||
import type { ChangeProformaStatusTarget } from "../entities";
|
||||
|
||||
type SupportedSource = Proforma | ProformaListRow;
|
||||
type SupportedInput = SupportedSource | SupportedSource[];
|
||||
|
||||
const mapToChangeProformaStatusTarget = (
|
||||
proforma: SupportedSource
|
||||
): ChangeProformaStatusTarget => ({
|
||||
id: proforma.id,
|
||||
reference: proforma.reference,
|
||||
status: proforma.status,
|
||||
});
|
||||
|
||||
export const prepareChangeProformaStatusTargets = (
|
||||
input: SupportedInput
|
||||
): ChangeProformaStatusTarget[] => {
|
||||
const items = Array.isArray(input) ? input : [input];
|
||||
return items.map(mapToChangeProformaStatusTarget);
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
import type { RightPanelMode } from "@repo/rdx-ui/hooks";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
import { useChangeProformaStatusDialogController } from "../../change-status";
|
||||
import { useDeleteProformaDialogController } from "../../delete";
|
||||
import { useIssueProformaDialogController } from "../../issue-proforma";
|
||||
|
||||
@ -11,6 +12,7 @@ export const useListProformasPageController = () => {
|
||||
const listCtrl = useListProformasController();
|
||||
const deleteDialogCtrl = useDeleteProformaDialogController();
|
||||
const issueDialogCtrl = useIssueProformaDialogController();
|
||||
const changeStatusDialogCtrl = useChangeProformaStatusDialogController();
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
@ -29,5 +31,6 @@ export const useListProformasPageController = () => {
|
||||
|
||||
deleteDialogCtrl,
|
||||
issueDialogCtrl,
|
||||
changeStatusDialogCtrl,
|
||||
};
|
||||
};
|
||||
|
||||
@ -15,6 +15,8 @@ import { FilterIcon, PlusIcon } from "lucide-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
import { ChangeProformaStatusDialog } from "../../../change-status";
|
||||
import { prepareChangeProformaStatusTargets } from "../../../change-status/utils";
|
||||
import { DeleteProformaDialog } from "../../../delete";
|
||||
import { prepareDeleteProformaTargets } from "../../../delete/utils";
|
||||
import { IssueProformaDialog } from "../../../issue-proforma";
|
||||
@ -27,7 +29,7 @@ export const ListProformasPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { listCtrl, panelCtrl, deleteDialogCtrl, issueDialogCtrl } =
|
||||
const { listCtrl, panelCtrl, deleteDialogCtrl, issueDialogCtrl, changeStatusDialogCtrl } =
|
||||
useListProformasPageController();
|
||||
|
||||
const columns = useProformasGridColumns({
|
||||
@ -36,7 +38,8 @@ export const ListProformasPage = () => {
|
||||
issueDialogCtrl.openDialog(prepareIssueProformaTarget(proformaRow)),
|
||||
onDeleteClick: (proformaRow: ProformaListRow) =>
|
||||
deleteDialogCtrl.openDialog(prepareDeleteProformaTargets(proformaRow)),
|
||||
//onChangeStatusClick: handleChangeStatusProforma,
|
||||
onChangeStatusClick: (proforma) =>
|
||||
changeStatusDialogCtrl.openDialog(prepareChangeProformaStatusTargets(proforma)),
|
||||
});
|
||||
|
||||
const isPanelOpen = panelCtrl.panelState.isOpen;
|
||||
@ -162,6 +165,18 @@ export const ListProformasPage = () => {
|
||||
target={issueDialogCtrl.target}
|
||||
/>
|
||||
|
||||
<ChangeProformaStatusDialog
|
||||
isSubmitting={changeStatusDialogCtrl.isSubmitting}
|
||||
onConfirm={changeStatusDialogCtrl.confirmChangeStatus}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
changeStatusDialogCtrl.closeDialog();
|
||||
}
|
||||
}}
|
||||
open={changeStatusDialogCtrl.open}
|
||||
targets={changeStatusDialogCtrl.targets}
|
||||
/>
|
||||
|
||||
{/* Eliminar */}
|
||||
<DeleteProformaDialog
|
||||
isSecondConfirmStep={deleteDialogCtrl.isSecondConfirmStep}
|
||||
|
||||
@ -20,7 +20,7 @@ export const useProformaChangeStatusMutation = () => {
|
||||
const dataSource = useDataSource();
|
||||
const schema = ChangeStatusProformaByIdRequestSchema;
|
||||
|
||||
useMutation<
|
||||
return useMutation<
|
||||
ChangeProformaStatusByIdResult,
|
||||
DefaultError,
|
||||
ChangeProformaStatusByIdParams,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user