[as] feat: 체크리스트 전체선택 기능 추가

This commit is contained in:
2026-07-13 16:43:01 +09:00
parent 399645a80a
commit 8d29f25101
3 changed files with 163 additions and 77 deletions
@@ -2,6 +2,7 @@ import type { Dispatch, SetStateAction } from "react";
import type { CklstRvwRsltTreeDto } from "@/entities/fnrs-cnsltn";
import {
Button,
cn,
Table,
TableBody,
@@ -36,6 +37,24 @@ function ChecklistTaskPanel({
const currentUserReviewStep = userRoleCode ? ROLE_TO_REVIEW_STEP[userRoleCode] : undefined;
const isStepEditable = currentUserReviewStep === selectedReviewStep;
function handleSelectAll(value: "Y" | "N") {
if (!isStepEditable) return;
setFormState((prev) => {
const nextResults = { ...prev.results };
selectedRows
.filter((row) => row.bchckUseYn === "Y")
.forEach((row) => {
nextResults[buildChecklistRowKey(row, selectedReviewStep)] = value;
});
return patchChecklistFormState(
prev,
{ results: nextResults },
allRows,
selectedReviewStep,
);
});
}
return (
<>
<div className="overflow-x-auto">
@@ -54,8 +73,27 @@ function ChecklistTaskPanel({
) : null}
</TableRow>
<TableRow>
<TableHead className="w-[9rem]">예</TableHead>
<TableHead className="w-[9rem]">아니오</TableHead>
<TableHead className="w-[12rem]">
<Button
type="button"
size="sm"
disabled={!isStepEditable}
onClick={() => handleSelectAll("Y")}
>
전체 예
</Button>
</TableHead>
<TableHead className="w-[12rem]">
<Button
type="button"
size="sm"
disabled={!isStepEditable}
onClick={() => handleSelectAll("N")}
className="!bg-destructive"
>
전체 아니오
</Button>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@@ -131,7 +169,7 @@ function ChecklistTaskPanel({
<TableCell className="text-left">
<Textarea
className={cn(
"min-h-[6rem] !text-xl",
"min-h-[6rem] w-[32rem] !text-xl",
isStepEditable ? "cursor-pointer" : "cursor-not-allowed",
)}
value={formState.comments[key] ?? ""}
@@ -2,6 +2,7 @@ import type { Dispatch, SetStateAction } from "react";
import type { CklstRvwRsltTreeDto, ReviewStepCode } from "@/entities/instl-sncn";
import {
Button,
cn,
Table,
TableBody,
@@ -35,6 +36,24 @@ function ChecklistTaskPanel({
const currentUserReviewStep = userRoleCode ? ROLE_TO_REVIEW_STEP[userRoleCode] : undefined;
const isStepEditable = currentUserReviewStep === selectedReviewStep;
function handleSelectAll(value: "Y" | "N") {
if (!isStepEditable) return;
setFormState((prev) => {
const nextResults = { ...prev.results };
selectedRows
.filter((row) => row.bchckUseYn === "Y")
.forEach((row) => {
nextResults[buildChecklistRowKey(row, selectedReviewStep)] = value;
});
return patchChecklistFormState(
prev,
{ results: nextResults },
allRows,
selectedReviewStep,
);
});
}
return (
<>
<div className="overflow-x-auto">
@@ -53,8 +72,27 @@ function ChecklistTaskPanel({
) : null}
</TableRow>
<TableRow>
<TableHead className="w-[9rem]">예</TableHead>
<TableHead className="w-[9rem]">아니오</TableHead>
<TableHead className="w-[12rem]">
<Button
type="button"
size="sm"
disabled={!isStepEditable}
onClick={() => handleSelectAll("Y")}
>
전체 예
</Button>
</TableHead>
<TableHead className="w-[12rem]">
<Button
type="button"
size="sm"
disabled={!isStepEditable}
onClick={() => handleSelectAll("N")}
className="!bg-destructive"
>
전체 아니오
</Button>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@@ -130,7 +168,7 @@ function ChecklistTaskPanel({
<TableCell className="text-left">
<Textarea
className={cn(
"min-h-[6rem] !text-xl",
"min-h-[6rem] w-[32rem] !text-xl",
isStepEditable ? "cursor-pointer" : "cursor-not-allowed",
)}
value={formState.comments[key] ?? ""}
@@ -5,7 +5,7 @@ import { Controller, useFormContext, useWatch } from "react-hook-form";
import { useQuery } from "@tanstack/react-query";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { Field, FieldLabel } from "@/shared/ui/field";
import { cklstMngService, type CklstTreeNode } from "@/entities/cklst";
import { useCodeList } from "@/entities/code";
import {
@@ -196,31 +196,33 @@ function ChecklistFormSection({ docSeComCd, fieldPrefix }: ChecklistFormSectionP
검토항목
</TableHead>
<TableHead rowSpan={2}>주요 검토내용</TableHead>
<TableHead className="w-[16rem]">검토</TableHead>
<TableHead colSpan={2}>검토 결과</TableHead>
</TableRow>
<TableRow>
<TableHead>
<div className="flex gap-2">
<Button
size="sm"
onClick={(e) => {
e.preventDefault();
handleSelectAll(tab.code, "Y");
}}
>
전체 예
</Button>
<Button
size="sm"
onClick={(e) => {
e.preventDefault();
handleSelectAll(tab.code, "N");
}}
className="!bg-destructive"
>
전체 아니오
</Button>
</div>
<TableHead className="w-[12rem]">
<Button
type="button"
size="sm"
onClick={(e) => {
e.preventDefault();
handleSelectAll(tab.code, "Y");
}}
>
전체 예
</Button>
</TableHead>
<TableHead className="w-[12rem]">
<Button
type="button"
size="sm"
onClick={(e) => {
e.preventDefault();
handleSelectAll(tab.code, "N");
}}
className="!bg-destructive"
>
전체 아니오
</Button>
</TableHead>
</TableRow>
</TableHeader>
@@ -236,64 +238,72 @@ function ChecklistFormSection({ docSeComCd, fieldPrefix }: ChecklistFormSectionP
style={{
paddingLeft: `${Math.max(0, item.treeLevel - 1) * 16 + 8}px`,
}}
colSpan={showRadios ? 0 : 2}
colSpan={showRadios ? 0 : 3}
>
{item.treeLevel > 1 ? "└ " : ""}
{item.content}
</TableCell>
{showRadios && resultIdx >= 0 ? (
<TableCell>
<Controller
name={`cklstResultList.${resultIdx}.selected`}
control={control}
render={({ field, fieldState }) => (
<FieldSet>
<Controller
name={`cklstResultList.${resultIdx}.selected`}
control={control}
render={({ field, fieldState }) => (
<>
<TableCell className="text-center">
<RadioGroup
name={field.name}
value={field.value || undefined}
value={field.value === "Y" ? "Y" : undefined}
onValueChange={field.onChange}
className="flex justify-center"
>
<div className="flex w-full justify-center gap-6">
<FieldLabel
htmlFor={`check-${item.cklstRvwSn}-Y`}
className="!w-auto border-none"
<FieldLabel
htmlFor={`check-${item.cklstRvwSn}-Y`}
className="!w-auto justify-center border-none"
>
<Field
orientation="horizontal"
data-invalid={fieldState.invalid}
className="w-auto"
>
<Field
orientation="horizontal"
data-invalid={fieldState.invalid}
className="w-auto"
>
<RadioGroupItem
value="Y"
id={`check-${item.cklstRvwSn}-Y`}
aria-invalid={fieldState.invalid}
/>
<span className="font-normal">예</span>
</Field>
</FieldLabel>
<FieldLabel
htmlFor={`check-${item.cklstRvwSn}-N`}
className="!w-auto border-none"
>
<Field
orientation="horizontal"
data-invalid={fieldState.invalid}
className="w-auto"
>
<RadioGroupItem
value="N"
id={`check-${item.cklstRvwSn}-N`}
aria-invalid={fieldState.invalid}
/>
<span className="font-normal">아니오</span>
</Field>
</FieldLabel>
</div>
<RadioGroupItem
value="Y"
id={`check-${item.cklstRvwSn}-Y`}
aria-invalid={fieldState.invalid}
aria-label="예"
/>
</Field>
</FieldLabel>
</RadioGroup>
</FieldSet>
)}
/>
</TableCell>
</TableCell>
<TableCell className="text-center">
<RadioGroup
name={field.name}
value={field.value === "N" ? "N" : undefined}
onValueChange={field.onChange}
className="flex justify-center"
>
<FieldLabel
htmlFor={`check-${item.cklstRvwSn}-N`}
className="!w-auto justify-center border-none"
>
<Field
orientation="horizontal"
data-invalid={fieldState.invalid}
className="w-auto"
>
<RadioGroupItem
value="N"
id={`check-${item.cklstRvwSn}-N`}
aria-invalid={fieldState.invalid}
aria-label="아니오"
/>
</Field>
</FieldLabel>
</RadioGroup>
</TableCell>
</>
)}
/>
) : null}
</TableRow>
);