mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 07:38:10 +00:00
269 lines
9.4 KiB
TypeScript
269 lines
9.4 KiB
TypeScript
import React, { useState } from "react";
|
|
import { Button } from "@/shared/common/ui/button";
|
|
import { Card } from "@/shared/common/ui/card";
|
|
import { Input } from "@/shared/common/ui/input";
|
|
import { Label } from "@radix-ui/react-label";
|
|
import { Plus, Trash2, Pencil } from "lucide-react";
|
|
import {
|
|
prefixSuffixService,
|
|
RemarkPayload,
|
|
} from "@/user-management/services/api/prefixSuffixService";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
} from "@/shared/common/ui/dialog";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from "@/shared/common/ui/alert-dialog";
|
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
import { useRemarkByUnitId } from "@/super-admin/hooks/useRemark";
|
|
import { t } from "i18next";
|
|
|
|
interface Props {
|
|
unitId: string;
|
|
}
|
|
|
|
interface RemarkDto {
|
|
id: string;
|
|
remark: string;
|
|
description?: string;
|
|
}
|
|
|
|
export const CommonRemarks = ({ unitId }: Props) => {
|
|
const [remark, setRemark] = useState("");
|
|
const [description, setDescription] = useState("");
|
|
const [editingId, setEditingId] = useState<string | null>(null);
|
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
|
const [deleteRemarkId, setDeleteRemarkId] = useState<string | null>(null);
|
|
const [error, setError] = useState("");
|
|
|
|
const queryClient = useQueryClient();
|
|
const { data: remarksData, isLoading: isRemarkLoading } =
|
|
useRemarkByUnitId(unitId,{
|
|
skip: 0,
|
|
take: 300,
|
|
});
|
|
|
|
// Save/Edit Remark
|
|
const saveRemarkMutation = useMutation({
|
|
mutationFn: async () => {
|
|
if (!remark) throw new Error(t("contentManagement.remarkRequired"));
|
|
|
|
const payload: RemarkPayload = { unitId, remark, description };
|
|
|
|
if (editingId) {
|
|
return prefixSuffixService.editRemark(editingId, payload);
|
|
}
|
|
return prefixSuffixService.createRemark(payload);
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["remark", unitId] });
|
|
resetForm();
|
|
setIsDialogOpen(false);
|
|
},
|
|
onError: (err: any) =>
|
|
setError(err.message || t("contentManagement.failed")),
|
|
});
|
|
|
|
// Delete Remark
|
|
const deleteRemarkMutation = useMutation({
|
|
mutationFn: (id: string) => prefixSuffixService.deleteRemarks(id),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["remark", unitId] });
|
|
setIsDeleteDialogOpen(false);
|
|
setDeleteRemarkId(null);
|
|
},
|
|
onError: () => setError(t("contentManagement.failedToDelete")),
|
|
});
|
|
|
|
const resetForm = () => {
|
|
setRemark("");
|
|
setDescription("");
|
|
setEditingId(null);
|
|
setError("");
|
|
};
|
|
|
|
const handleEdit = (item: RemarkDto) => {
|
|
setEditingId(item.id);
|
|
setRemark(item.remark);
|
|
setDescription(item.description || "");
|
|
setIsDialogOpen(true);
|
|
};
|
|
|
|
const handleSubmit = () => saveRemarkMutation.mutate();
|
|
|
|
return (
|
|
<Card className="p-4 border shadow-sm space-y-4 dark:border-gray-700 dark:bg-gray-800">
|
|
{/* Add/Edit Dialog */}
|
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button className="w-[180px] bg-primary hover:bg-primary/90 text-primary-foreground flex items-center">
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
{t("contentManagement.addRemark")}
|
|
</Button>
|
|
</DialogTrigger>
|
|
|
|
<DialogContent className="sm:max-w-xl w-full dark:bg-gray-800">
|
|
<DialogHeader>
|
|
<DialogTitle className="dark:text-white">
|
|
{editingId
|
|
? t("contentManagement.editRemark")
|
|
: t("contentManagement.addRemark")}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="grid gap-4 py-4">
|
|
<div className="flex flex-col">
|
|
<Label htmlFor="remark" className="mb-1 dark:text-gray-200">
|
|
{t("header.Remark")}
|
|
</Label>
|
|
<Input
|
|
id="remark"
|
|
value={remark}
|
|
onChange={(e) => setRemark(e.target.value)}
|
|
placeholder={t("header.Remark")}
|
|
disabled={isRemarkLoading}
|
|
className="w-full dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
|
/>
|
|
<Label htmlFor="description" className="mb-1 dark:text-gray-200">
|
|
{t("header.Description")}
|
|
</Label>
|
|
<Input
|
|
id="description"
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
placeholder={t("header.Description")}
|
|
disabled={isRemarkLoading}
|
|
className="w-full mb-2 dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
|
/>
|
|
</div>
|
|
{error && <p className="text-sm text-red-500">{error}</p>}
|
|
</div>
|
|
|
|
<div className="flex justify-end gap-2 pt-4 border-t border-gray-200 dark:border-gray-700">
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => {
|
|
resetForm();
|
|
setIsDialogOpen(false);
|
|
}}>
|
|
{t("common.Cancel")}
|
|
</Button>
|
|
<Button
|
|
onClick={handleSubmit}
|
|
disabled={isRemarkLoading} // ✅ access via mutation object
|
|
className="bg-primary hover:bg-primary/90 text-primary-foreground">
|
|
{isRemarkLoading
|
|
? t("contentManagement.saving")
|
|
: editingId
|
|
? t("delegation.update")
|
|
: t("delegation.save")}
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
{/* Remarks Table */}
|
|
<h3 className="font-semibold text-md dark:text-white">
|
|
{t("contentManagement.commonRemarks")}
|
|
</h3>
|
|
{isRemarkLoading ? (
|
|
<div className="text-center py-4 dark:text-gray-400">{t("contentManagement.loading")}</div>
|
|
) : (
|
|
<div className="overflow-x-auto max-h-[380px] overflow-y-auto border rounded dark:border-gray-600">
|
|
<table className="min-w-full text-sm text-left">
|
|
<thead>
|
|
<tr className="border-b bg-gray-100 dark:bg-gray-700">
|
|
<th className="px-3 py-2 dark:text-white">#</th>
|
|
<th className="px-3 py-2 dark:text-white">{t("header.Remark")}</th>
|
|
<th className="px-3 py-2 dark:text-white">{t("header.Description")}</th>
|
|
<th className="px-3 py-2 dark:text-white">{t("userRecord.Actions")}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{remarksData?.items.length ? (
|
|
remarksData.items.map((item: any, idx: any) => (
|
|
<tr key={item.id} className="border-b hover:bg-gray-50 dark:hover:bg-gray-700 dark:border-gray-600">
|
|
<td className="px-3 py-2 dark:text-gray-300">{idx + 1}</td>
|
|
<td className="px-3 py-2 dark:text-gray-300">{item.remark}</td>
|
|
<td className="px-3 py-2 dark:text-gray-300">{item.description}</td>
|
|
<td className="px-3 py-2 flex gap-2">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => handleEdit(item)}>
|
|
<Pencil className="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
variant="destructive"
|
|
size="sm"
|
|
onClick={() => {
|
|
setDeleteRemarkId(item.id);
|
|
setIsDeleteDialogOpen(true);
|
|
}}>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</td>
|
|
</tr>
|
|
))
|
|
) : (
|
|
<tr>
|
|
<td colSpan={4} className="text-center py-4 text-gray-500 dark:text-gray-400">
|
|
{t("contentManagement.noRec")}
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
|
|
<AlertDialog
|
|
open={isDeleteDialogOpen}
|
|
onOpenChange={(open) => {
|
|
if (deleteRemarkMutation.isPending) return;
|
|
setIsDeleteDialogOpen(open);
|
|
if (!open) setDeleteRemarkId(null);
|
|
}}
|
|
>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>{t("common.delete")}</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
{t("contentManagement.deleteMsg")}
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel disabled={deleteRemarkMutation.isPending}>
|
|
{t("common.cancel")}
|
|
</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
className="bg-red-600 hover:bg-red-700"
|
|
disabled={deleteRemarkMutation.isPending || !deleteRemarkId}
|
|
onClick={() => {
|
|
if (!deleteRemarkId) return;
|
|
deleteRemarkMutation.mutate(deleteRemarkId);
|
|
}}
|
|
>
|
|
{deleteRemarkMutation.isPending
|
|
? t("common.deleting", "Deleting...")
|
|
: t("common.delete")}
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</Card>
|
|
);
|
|
};
|