Files
frontend/apps/as/src/pages/rvw-mnpw-mng/ui/rvw-mnpw-block.tsx
T
jcchoi 48999f5699
Deploy Frontend / deploy (push) Successful in 9m6s
웹 접근성 조치
2026-10-11 15:32:56 +09:00

121 lines
4.0 KiB
TypeScript

"use client";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { AxiosError } from "axios";
import { useAlertStore } from "@/shared/lib";
import type { RvwMnpwMngResponseDto } from "@/entities/rvw-mnpw";
import { useDeleteRvwMnpwMng, useSaveRvwMnpwMng } from "@/entities/rvw-mnpw";
import type { SelectedOgnzUser } from "@/widgets/ognz-user-tree";
import { Badge, Button } from "@common/components";
import type { RvwMnpwBlockConfig } from "../model/rvw-mnpw-block-config";
/** 서버 오류 응답의 message 를 우선 표시하고, 없으면 기본 문구를 사용한다 */
function getErrorMessage(error: unknown, fallback: string) {
if (error instanceof AxiosError) {
return error.response?.data?.message || fallback;
}
return fallback;
}
interface RvwMnpwBlockProps {
block: RvwMnpwBlockConfig;
items: RvwMnpwMngResponseDto[];
selectedPerson: SelectedOgnzUser | null;
}
function RvwMnpwBlock({ block, items, selectedPerson }: RvwMnpwBlockProps) {
const { confirm } = useConfirm();
const alert = useAlertStore((state) => state.setMessage);
const saveMutation = useSaveRvwMnpwMng();
const deleteMutation = useDeleteRvwMnpwMng();
const handleAssign = async () => {
if (!selectedPerson) return;
try {
await saveMutation.mutateAsync({
atrzSeComCd: block.atrzSeComCd,
ognzSn: selectedPerson.ognzSn,
userUuid: selectedPerson.userUuid,
});
alert("배치되었습니다.");
} catch (error) {
alert(getErrorMessage(error, "배치에 실패했습니다."));
}
};
const handleRemove = async (item: RvwMnpwMngResponseDto) => {
if (!(await confirm("해당 인원을 배치에서 제거하시겠습니까?"))) return;
try {
await deleteMutation.mutateAsync({
atrzSeComCd: item.atrzSeComCd,
ognzSn: item.ognzSn,
userUuid: item.userUuid,
});
} catch (error) {
alert(getErrorMessage(error, "제거에 실패했습니다."));
}
};
const isAssigning = saveMutation.isPending;
const isRemoving = deleteMutation.isPending;
return (
<section className="rounded-md border border-border bg-background p-4">
<div className="mb-3 flex flex-wrap items-center justify-between gap-2">
<div className="flex items-center gap-2">
<h4 className="text-xl font-semibold">{block.title}</h4>
<Badge color="gray">{items.length}</Badge>
</div>
<Button
type="button"
size="sm"
color="green"
disabled={!selectedPerson || isAssigning}
onClick={handleAssign}
>
선택 인원 배치
</Button>
</div>
{items.length === 0 ? (
<p className="py-4 text-center text-lg text-muted-foreground">배치된 인원이 없습니다.</p>
) : (
<ul className="grid grid-cols-4 gap-2">
{items.map((item) => (
<li
key={`${item.atrzSeComCd}-${item.ognzSn}-${item.userUuid}`}
className="flex items-center justify-between gap-3 rounded-sm border border-border px-3 py-2"
>
<div className="flex min-w-0 flex-1 items-center gap-2">
<p className="text-[1.4rem] font-medium text-label">
{item.userUuidNm || item.userUuid}
</p>
{item.ognzNm && item.jbpsSeNm && (
<span className="text-lg text-muted-foreground">
({item.ognzNm} &middot; {item.jbpsSeNm})
</span>
)}
</div>
<Button
type="button"
size="sm"
color="white"
disabled={isRemoving}
onClick={() => handleRemove(item)}
>
제거
</Button>
</li>
))}
</ul>
)}
</section>
);
}
export { RvwMnpwBlock };