mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-31 21:17:38 +00:00
410 lines
13 KiB
TypeScript
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>
|
|
);
|
|
};
|