Files
edr-platform/apps/edr-freight-web/backoffice/src/user-management/components/content/PrefixModal.tsx
natib21 e6e44e773b fix ui
2026-07-10 11:25:59 +00:00

410 lines
13 KiB
TypeScript

// PrefixModal.tsx
import React, { useState, useEffect } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/shared/common/ui/dialog";
import { Button } from "@/shared/common/ui/button";
import { Input } from "@/shared/common/ui/input";
import { Label } from "@radix-ui/react-label";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import {
CreatePrefixPayload,
CreateSuffixPayload,
prefixSuffixService,
} from "@/user-management/services/api/prefixSuffixService";
import { t } from "i18next";
import { PrefixTabType } from "./PrefixAndSuffixCard";
import { toast } from "sonner";
import {
useAddReferenceNumber,
useGetReferenceNumbers,
useUpdateExternalReferencePrefix,
useUpdateInternalMemoReferencePrefix,
} from "@/shared/hooks/useReferenceNumberPrefixes";
import { useErrorHandler } from "@/shared/hooks/useErrorHandler";
import { SingleSelect } from "@/shared/common/ui/single-select";
interface PrefixModalProps {
open: boolean;
onClose: () => void;
onSuccess: () => void;
unitId: string;
cardType: string; // e.g., "prefix", "suffix", "prefixCC", "reference", "internalPrefix", etc.
recordType: PrefixTabType;
editingItem?: any;
positions?: any[];
}
export const PrefixModal = ({
open,
onClose,
onSuccess,
unitId,
cardType,
recordType,
editingItem,
positions = [],
}: PrefixModalProps) => {
const queryClient = useQueryClient();
const isEditing = !!editingItem;
// Form state
const [nameAm, setNameAm] = useState("");
const [nameEn, setNameEn] = useState("");
const [positionId, setPositionId] = useState("");
// Determine if this is a reference type
const isReference = [
"reference",
"externalReference",
"internalMemoReference",
].includes(cardType);
const isPositionPrefix = recordType === "positionPrefix";
const isCC = cardType.includes("CC");
const isReferenceCard = cardType === "reference";
const isExternalReferenceCard = cardType === "externalReference";
const isInternalMemoReferenceCard = cardType === "internalMemoReference";
const positionOptions = positions
.filter((pos) => Boolean(pos?.id))
.map((pos) => ({
value: pos.id,
label: pos.name?.en || pos.name?.am || pos.id,
}));
const positionRecordTypeKey =
cardType === "internalPrefix"
? "internal"
: cardType === "externalPrefix"
? "external"
: cardType === "internalMemoPrefix"
? "internal_memo"
: editingItem?.recordTypeKey;
const { data: referenceNumbersData } = useGetReferenceNumbers(
isReference ? unitId : "",
);
const addReferenceNumberMutation = useAddReferenceNumber();
const updateInternalMemoReferenceMutation =
useUpdateInternalMemoReferencePrefix();
const updateExternalReferenceMutation = useUpdateExternalReferencePrefix();
const { handleError } = useErrorHandler(t);
const referenceNumbers =
((referenceNumbersData?.data as any)?.items?.[0] as any) ||
referenceNumbersData?.data ||
{};
const isArray = Array.isArray(referenceNumbers);
const referenceNumberPrefix =
isArray &&
referenceNumbers?.find((rn: any) => rn.name === "referenceNumberPrefix");
const externalReferenceNumberPrefix =
isArray &&
referenceNumbers?.find(
(rn: any) => rn.name === "externalReferenceNumberPrefix",
);
const internalMemoReferenceNumberPrefix =
isArray &&
referenceNumbers?.find(
(rn: any) => rn.name === "internalMemoReferenceNumberPrefix",
);
// Populate form when editing
useEffect(() => {
if (editingItem) {
if (isReference) {
setNameAm(editingItem.name?.am || "");
setNameEn(editingItem.name?.en || "");
} else {
setNameAm(editingItem.name?.am || "");
setNameEn(editingItem.name?.en || "");
}
setPositionId(editingItem.positionId || "");
} else {
// Reset
setNameAm("");
setNameEn("");
setPositionId("");
}
}, [editingItem, isReference]);
useEffect(() => {
if (!isReference || editingItem) return;
if (isReferenceCard) {
setNameAm(referenceNumbers.referenceNumberPrefix || "");
setNameEn(referenceNumbers.referenceNumberPrefix || "");
return;
}
if (isExternalReferenceCard) {
setNameAm(referenceNumbers.externalReferenceNumberPrefix || "");
setNameEn(referenceNumbers.externalReferenceNumberPrefix || "");
return;
}
if (isInternalMemoReferenceCard) {
setNameAm(referenceNumbers.internalMemoReferenceNumberPrefix || "");
setNameEn(referenceNumbers.internalMemoReferenceNumberPrefix || "");
}
}, [
editingItem,
isReference,
isReferenceCard,
isExternalReferenceCard,
isInternalMemoReferenceCard,
referenceNumbers.referenceNumberPrefix,
referenceNumbers.externalReferenceNumberPrefix,
referenceNumbers.internalMemoReferenceNumberPrefix,
]);
// Mutation for saving
const { mutate: save, isPending } = useMutation({
mutationFn: async () => {
const baseRecordTypeKey = recordType;
const isPrefixOperation =
isReference ||
isPositionPrefix ||
cardType === "prefix" ||
cardType === "prefixCC" ||
cardType === "internalPrefix" ||
cardType === "externalPrefix" ||
cardType === "internalMemoPrefix";
let prefixPayload: CreatePrefixPayload | undefined;
let suffixPayload: CreateSuffixPayload | undefined;
if (isReference) {
if (isReferenceCard) {
await addReferenceNumberMutation.mutateAsync({
unitId,
payload: {
referenceNumberPrefix: {
am: nameAm,
en: nameEn,
},
},
});
return;
}
if (isExternalReferenceCard) {
await updateExternalReferenceMutation.mutateAsync({
unitId,
payload: {
externalReferenceNumberPrefix: {
am: nameAm,
en: nameEn,
},
},
});
return;
}
if (isInternalMemoReferenceCard) {
await updateInternalMemoReferenceMutation.mutateAsync({
unitId,
payload: {
internalMemoReferenceNumberPrefix: {
am: nameAm,
en: nameEn,
},
},
});
return;
}
} else if (isPositionPrefix) {
if (
positionRecordTypeKey !== "internal" &&
positionRecordTypeKey !== "external" &&
positionRecordTypeKey !== "internal_memo"
) {
throw new Error(
"Position prefix requires internal, external, or internal_memo recordTypeKey",
);
}
prefixPayload = {
unitId,
recordTypeKey: positionRecordTypeKey,
isForCC: false,
positionId,
name: { am: nameAm, en: nameEn },
};
} else {
const commonPayload = {
unitId,
recordTypeKey: baseRecordTypeKey,
isForCC: isCC,
name: { am: nameAm, en: nameEn },
};
if (cardType === "suffix" || cardType === "suffixCC") {
suffixPayload = commonPayload;
} else {
prefixPayload = commonPayload;
}
}
if (isEditing) {
if (isPrefixOperation && prefixPayload) {
return prefixSuffixService.editPrefix(editingItem.id, prefixPayload);
}
if (suffixPayload) {
return prefixSuffixService.editSuffix(editingItem.id, suffixPayload);
}
throw new Error("Invalid prefix/suffix payload for update");
}
if (isPrefixOperation && prefixPayload) {
return prefixSuffixService.createPrefix(prefixPayload);
}
if (suffixPayload) {
return prefixSuffixService.createSuffix(suffixPayload);
}
throw new Error("Invalid prefix/suffix payload for create");
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["prefixSuffix"] });
toast.success(t("msg.successfullyCompleted"));
onSuccess();
},
onError: (error) => {
handleError(error);
},
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
save();
};
const isValid = () => {
if (isReference) return nameAm.trim() !== "" && nameEn.trim() !== "";
if (isPositionPrefix)
return nameAm.trim() !== "" && nameEn.trim() !== "" && positionId;
return nameAm.trim() !== "" && nameEn.trim() !== "";
};
return (
<Dialog open={open} onOpenChange={onClose}>
<DialogContent className="sm:max-w-[500px] dark:bg-gray-800">
<DialogHeader>
<DialogTitle className="dark:text-white">
{isEditing
? t("contentManagement.edit")
: t("contentManagement.add")}{" "}
{t(`contentManagement.${cardType}`)}
</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
{isReference ? (
<div className="space-y-2">
<div className="space-y-2">
<Label htmlFor="nameAm" className="dark:text-gray-200">
{t("contentManagement.amharicName")} *
</Label>
<Input
id="nameAm"
value={nameAm}
onChange={(e) => setNameAm(e.target.value)}
placeholder={t("contentManagement.amharicName")}
disabled={isPending}
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
/>
</div>
<div className="space-y-2">
<Label htmlFor="nameEn" className="dark:text-gray-200">
{t("contentManagement.englishName")} *
</Label>
<Input
id="nameEn"
value={nameEn}
onChange={(e) => setNameEn(e.target.value)}
placeholder={t("contentManagement.englishName")}
disabled={isPending}
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
/>
</div>
</div>
) : (
<>
<div className="space-y-2">
<Label htmlFor="nameAm" className="dark:text-gray-200">
{t("contentManagement.amharicName")} *
</Label>
<Input
id="nameAm"
value={nameAm}
onChange={(e) => setNameAm(e.target.value)}
placeholder={t("contentManagement.amharicName")}
disabled={isPending}
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
/>
</div>
<div className="space-y-2">
<Label htmlFor="nameEn" className="dark:text-gray-200">
{t("contentManagement.englishName")} *
</Label>
<Input
id="nameEn"
value={nameEn}
onChange={(e) => setNameEn(e.target.value)}
placeholder={t("contentManagement.englishName")}
disabled={isPending}
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
/>
</div>
</>
)}
{isPositionPrefix && (
<div className="space-y-2">
<Label htmlFor="position" className="dark:text-gray-200">
{t("contentManagement.position")} *
</Label>
<SingleSelect
options={positionOptions}
value={positionId}
onValueChange={setPositionId}
placeholder={t("contentManagement.selectPosition")}
className={isPending ? "pointer-events-none opacity-60" : ""}
/>
</div>
)}
<div className="flex justify-end gap-2 pt-4">
<Button
type="button"
variant="outline"
onClick={onClose}
disabled={isPending}
>
{t("common.Cancel")}
</Button>
<Button
type="submit"
disabled={!isValid() || isPending}
className="bg-purple-600 hover:bg-purple-700"
>
{isPending
? t("contentManagement.saving")
: (isReferenceCard &&
(referenceNumbers.referenceNumberPrefix ||
referenceNumbers.externalReferenceNumberPrefix ||
referenceNumbers.internalMemoReferenceNumberPrefix)) ||
isEditing
? t("common.update")
: t("common.save")}
</Button>
</div>
</form>
</DialogContent>
</Dialog>
);
};