Files
edr-platform/apps/edr-freight-web/backoffice/src/pages/AttachmentSignature.tsx
yaschalew 697799b00d fix
2026-07-10 14:24:58 +03:00

231 lines
9.2 KiB
TypeScript

import React, { useEffect, useId, useState } from "react";
import { Building2, PenLine, Settings, ShieldCheck } from "lucide-react";
import { useAuth } from "@/shared/context/AuthContext";
import { useLocalizedName } from "@/shared/common/localizedName";
import { useUnit } from "@/user-management/hooks/useUnit";
import { useUnitConfiguration } from "@/shared/hooks/useUnitConfiguration";
import { prefixSuffixService } from "@/user-management/services/api/prefixSuffixService";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/shared/common/ui/select";
import { Switch } from "@/shared/common/ui/switch";
import { Label } from "@/shared/common/ui/label";
import { t } from "i18next";
type UnitOption = {
id: string;
name?: { am: string; en: string };
};
type UnitConfigurationItem = {
id: string;
unitId: string;
attachSignatureOnAttachment?: boolean;
};
const AttachmentSignature = () => {
const { user } = useAuth();
const queryClient = useQueryClient();
const organizationId = user?.employee?.[0]?.organizationId || "";
const localizedName = useLocalizedName();
const switchId = useId();
const { getList: getUnitList } = useUnit();
const { data: units, isLoading: isLoadingUnits } = getUnitList(
organizationId,
{ take: 1000 },
);
const [selectedUnitId, setSelectedUnitId] = useState("");
const [localAttachSignatureValue, setLocalAttachSignatureValue] = useState<
boolean | null
>(null);
useEffect(() => {
if (!selectedUnitId && units?.data?.items?.length) {
setSelectedUnitId(units.data.items[0].id);
}
}, [selectedUnitId, units]);
const unitItems = (units?.data?.items ?? []) as UnitOption[];
const hasUnits = unitItems.length > 0;
const selectedUnitName = unitItems.find(
(unit) => unit.id === selectedUnitId,
)?.name;
const { data: unitConfigResponse, isLoading: isLoadingConfig } =
useUnitConfiguration(selectedUnitId);
const unitConfigurations =
(unitConfigResponse?.data?.items as UnitConfigurationItem[] | undefined) ||
[];
const filteredConfig =
unitConfigurations.find((item) => item.unitId === selectedUnitId) ||
unitConfigurations[0];
const configValue = Boolean(filteredConfig?.attachSignatureOnAttachment);
const isSwitchEnabled = Boolean(filteredConfig?.id);
const switchValue =
localAttachSignatureValue === null ? configValue : localAttachSignatureValue;
useEffect(() => {
setLocalAttachSignatureValue(null);
}, [selectedUnitId, configValue]);
const { mutate: updateEscalationField, isPending: isUpdatingSignature } =
useMutation({
mutationFn: ({ id, value }: { id: string; value: boolean }) =>
prefixSuffixService.updateInternalPrefixSuffix(
{
attachSignatureOnAttachment: value,
unitId: selectedUnitId,
},
id,
),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["unitConfig", selectedUnitId],
});
},
onError: () => {
setLocalAttachSignatureValue(null);
},
});
const handleToggleSignature = (checked: boolean) => {
if (!filteredConfig?.id || isUpdatingSignature) return;
setLocalAttachSignatureValue(checked);
updateEscalationField({
id: filteredConfig.id,
value: checked,
});
};
return (
<section className="w-full">
<div className="relative overflow-hidden rounded-2xl border border-slate-200/80 bg-gradient-to-br from-white via-slate-50 to-slate-100/60 p-4 shadow-sm sm:p-6 dark:border-slate-700/80 dark:bg-gradient-to-br dark:from-slate-950 dark:via-slate-900 dark:to-slate-900">
<div className="relative space-y-5">
<header className="flex flex-col gap-2 border-b border-slate-200 pb-4 dark:border-slate-700 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1">
<p className="inline-flex w-fit items-center gap-2 rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-slate-700 ring-1 ring-slate-200 dark:bg-slate-800 dark:text-slate-300 dark:ring-slate-700">
<ShieldCheck className="h-3.5 w-3.5" />
{t("setting.attachmentSignature.badge")}
</p>
<h2 className="text-xl font-semibold tracking-tight text-slate-900 dark:text-slate-100 sm:text-2xl flex gap-2">
<Settings className="h-8 w-8 text-primary" />
{t("setting.attachmentSignature.title")}
</h2>
<p className="max-w-2xl text-sm text-slate-600 dark:text-slate-400">
{t("setting.attachmentSignature.description")}
</p>
</div>
</header>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<div className="space-y-2 rounded-xl border border-slate-200/80 bg-white/80 p-4 backdrop-blur-sm dark:border-slate-700 dark:bg-slate-900/70">
<Label className="text-sm font-medium text-slate-800 dark:text-slate-200">
{t("setting.attachmentSignature.unit")}
</Label>
<Select
value={selectedUnitId}
onValueChange={(value) => setSelectedUnitId(value)}
>
<SelectTrigger
className="h-11 w-full bg-white/90 dark:border-slate-700 dark:bg-slate-900"
aria-label={t("setting.attachmentSignature.unit")}
>
<SelectValue
placeholder={t("setting.attachmentSignature.selectUnit")}
/>
</SelectTrigger>
<SelectContent>
{isLoadingUnits ? (
<SelectItem value="loading" disabled>
{t("setting.attachmentSignature.loadingUnits")}
</SelectItem>
) : hasUnits ? (
unitItems.map((unit) => (
<SelectItem key={unit.id} value={unit.id}>
{localizedName(unit.name)}
</SelectItem>
))
) : (
<SelectItem value="no-units" disabled>
{t("setting.attachmentSignature.noUnitsFound")}
</SelectItem>
)}
</SelectContent>
</Select>
<p className="text-xs text-slate-500 dark:text-slate-400">
{t("setting.attachmentSignature.unitHint")}
</p>
</div>
<div className="space-y-3 rounded-xl border border-slate-200/80 bg-white/80 p-4 backdrop-blur-sm dark:border-slate-700 dark:bg-slate-900/70">
<div className="flex items-start justify-between gap-3">
<div className="space-y-1">
<Label
htmlFor={switchId}
className="text-sm font-medium text-slate-800 dark:text-slate-200"
>
{t("setting.attachmentSignature.toggleLabel")}
</Label>
<p
id={`${switchId}-description`}
className="text-xs text-slate-500 dark:text-slate-400"
>
{t("setting.attachmentSignature.toggleDescription")}
</p>
</div>
<Switch
id={switchId}
checked={switchValue}
onCheckedChange={handleToggleSignature}
aria-describedby={`${switchId}-description`}
disabled={!isSwitchEnabled || isLoadingConfig || isUpdatingSignature}
/>
</div>
<div className="flex items-center gap-2 rounded-lg border border-dashed border-slate-300/80 bg-slate-50/80 px-3 py-2 text-sm text-slate-600 dark:border-slate-700 dark:bg-slate-800/80 dark:text-slate-300">
<PenLine className="h-4 w-4 text-indigo-600 dark:text-indigo-400" />
<span>
{t("setting.attachmentSignature.status")}{" "}
<span className="font-semibold">
{switchValue
? t("profile.enabled")
: t("profile.disabled")}
</span>
</span>
</div>
{!isSwitchEnabled && !isLoadingConfig && (
<p className="text-xs text-amber-600 dark:text-amber-400">
{t("setting.noConfigFound")}
</p>
)}
</div>
</div>
<footer className="flex flex-wrap items-center gap-2 rounded-lg border border-slate-200/80 bg-white/70 px-3 py-2 text-xs text-slate-600 dark:border-slate-700 dark:bg-slate-900/70 dark:text-slate-300">
<Building2 className="h-3.5 w-3.5 text-slate-500 dark:text-slate-400" />
<span>
{t("setting.attachmentSignature.activeUnit")}{" "}
<strong className="font-semibold">
{selectedUnitName
? localizedName(selectedUnitName)
: t("common.None")}
</strong>
</span>
</footer>
</div>
</div>
</section>
);
};
export default AttachmentSignature;