304 lines
12 KiB
TypeScript
304 lines
12 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Controller, useFormContext } from "react-hook-form";
|
|
|
|
import { NON_NEGATIVE_INPUT_PROPS } from "@/shared/lib";
|
|
import { SectionCollapsible } from "@/shared/ui";
|
|
import { Field, FieldError, FieldGroup, FieldLabel, FieldSet } from "@/shared/ui/field";
|
|
import { findCodeLabel, useCodeList } from "@/entities/code";
|
|
import { type BnsApplyData, BnsPickerDialog } from "@/widgets/bns-picker";
|
|
import {
|
|
Button,
|
|
Checkbox,
|
|
Input,
|
|
RadioGroup,
|
|
RadioGroupItem,
|
|
Table,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@common/components";
|
|
|
|
import type { InstlSncnRegistFormData } from "../../lib/instl-sncn-regist-schema";
|
|
|
|
function BasicInfoFormSection() {
|
|
const { control, watch, setValue } = useFormContext<InstlSncnRegistFormData>();
|
|
const [isPickerOpen, setIsPickerOpen] = useState(false);
|
|
const taskSeComCd = watch("instlSncnMng.taskSeComCd");
|
|
const localGovLabel = [watch("instlSncnMng.sido"), watch("instlSncnMng.gugun")]
|
|
.filter(Boolean)
|
|
.join(" ");
|
|
|
|
function handleBnsSelect(data: BnsApplyData) {
|
|
setValue("instlSncnMng.bizNm", data.bizNm);
|
|
setValue("instlSncnMng.bizBgngYr", data.bizBgngYr);
|
|
setValue("instlSncnMng.bizEndYr", data.bizEndYr);
|
|
setValue("instlSncnMng.bizCn", data.bizCn);
|
|
setValue("instlSncnMng.bgtBizCd", data.bgtBizCd);
|
|
setValue("instlSncnMng.bgtAplyYr", data.bgtAplyYr);
|
|
}
|
|
|
|
const { data: taskOptions } = useCodeList("INSTL");
|
|
const { data: sncnOptions } = useCodeList("SNCNSE");
|
|
const { data: rtlztnFcltOptions } = useCodeList("RTLZTNFCLTSE");
|
|
const { data: rtlztnUsgOptions } = useCodeList("RTLZTNUSGSE");
|
|
|
|
const taskSeComCdLabel =
|
|
taskSeComCd
|
|
.map((code) => findCodeLabel(taskOptions, code))
|
|
.filter(Boolean)
|
|
.join(", ") || "-";
|
|
|
|
return (
|
|
<SectionCollapsible title="기본 정보">
|
|
<Table caption="기본 정보 입력으로 사업명, 사업기간, 사업내용, 구분, 신청 지자체, 사업구분, 시설종류, 재이용용도 정보 제공" variant="secondary">
|
|
<colgroup>
|
|
<col width="15%" />
|
|
<col width="35%" />
|
|
<col width="15%" />
|
|
<col width="35%" />
|
|
</colgroup>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead scope="row" className="w-[20rem]" data-required>
|
|
사업명
|
|
</TableHead>
|
|
<TableCell colSpan={3}>
|
|
<Controller
|
|
name="instlSncnMng.bizNm"
|
|
control={control}
|
|
render={({ field, fieldState }) => (
|
|
<Field data-invalid={fieldState.invalid}>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
{...field}
|
|
id={field.name}
|
|
placeholder="사업명 입력"
|
|
aria-invalid={fieldState.invalid}
|
|
className="w-[40rem]"
|
|
/>
|
|
<Button type="button" onClick={() => setIsPickerOpen(true)}>
|
|
사업찾기
|
|
</Button>
|
|
</div>
|
|
<FieldError errors={[fieldState.error]} />
|
|
</Field>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableHead scope="row" data-required>사업기간</TableHead>
|
|
<TableCell colSpan={3}>
|
|
<FieldGroup className="flex-row items-center gap-2">
|
|
<Controller
|
|
name="instlSncnMng.bizBgngYr"
|
|
control={control}
|
|
render={({ field, fieldState }) => (
|
|
<Field data-invalid={fieldState.invalid} className="w-auto !gap-1">
|
|
<Input
|
|
{...field}
|
|
{...NON_NEGATIVE_INPUT_PROPS}
|
|
type="number"
|
|
id={field.name}
|
|
placeholder="시작연도"
|
|
className="max-w-[12rem]"
|
|
aria-invalid={fieldState.invalid}
|
|
/>
|
|
<FieldError errors={[fieldState.error]} />
|
|
</Field>
|
|
)}
|
|
/>
|
|
<span>~</span>
|
|
<Controller
|
|
name="instlSncnMng.bizEndYr"
|
|
control={control}
|
|
render={({ field, fieldState }) => (
|
|
<Field data-invalid={fieldState.invalid} className="w-auto !gap-1">
|
|
<Input
|
|
{...field}
|
|
{...NON_NEGATIVE_INPUT_PROPS}
|
|
type="number"
|
|
id={field.name}
|
|
placeholder="종료연도"
|
|
className="max-w-[12rem]"
|
|
aria-invalid={fieldState.invalid}
|
|
/>
|
|
<FieldError errors={[fieldState.error]} />
|
|
</Field>
|
|
)}
|
|
/>
|
|
</FieldGroup>
|
|
</TableCell>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableHead scope="row" data-required>사업내용</TableHead>
|
|
<TableCell colSpan={3}>
|
|
<Controller
|
|
name="instlSncnMng.bizCn"
|
|
control={control}
|
|
render={({ field, fieldState }) => (
|
|
<Field data-invalid={fieldState.invalid}>
|
|
<Input
|
|
{...field}
|
|
id={field.name}
|
|
placeholder="사업내용 입력"
|
|
aria-invalid={fieldState.invalid}
|
|
/>
|
|
<FieldError errors={[fieldState.error]} />
|
|
</Field>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableHead scope="row" data-required>구분</TableHead>
|
|
<TableCell>{taskSeComCdLabel}</TableCell>
|
|
<TableHead scope="row">신청 지자체</TableHead>
|
|
<TableCell>{localGovLabel}</TableCell>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableHead scope="row" data-required>사업구분</TableHead>
|
|
<TableCell colSpan={3}>
|
|
<Controller
|
|
name="instlSncnMng.sncnSeComCd"
|
|
control={control}
|
|
render={({ field, fieldState }) => (
|
|
<FieldSet>
|
|
<RadioGroup
|
|
name={field.name}
|
|
value={field.value}
|
|
onValueChange={field.onChange}
|
|
>
|
|
{sncnOptions?.map((option) => (
|
|
<FieldLabel
|
|
key={option.lwpoCd}
|
|
htmlFor={`sncn-${option.lwpoCd}`}
|
|
className="!w-auto border-none"
|
|
>
|
|
<Field
|
|
orientation="horizontal"
|
|
data-invalid={fieldState.invalid}
|
|
className="w-auto"
|
|
>
|
|
<RadioGroupItem
|
|
value={option.lwpoCd}
|
|
id={`sncn-${option.lwpoCd}`}
|
|
aria-invalid={fieldState.invalid}
|
|
/>
|
|
<span className="font-normal">{option.lwpoCdNm}</span>
|
|
</Field>
|
|
</FieldLabel>
|
|
))}
|
|
</RadioGroup>
|
|
<FieldError errors={[fieldState.error]} />
|
|
</FieldSet>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
{taskSeComCd.includes("RE") && (
|
|
<>
|
|
<TableRow>
|
|
<TableHead scope="row">시설종류</TableHead>
|
|
<TableCell colSpan={3}>
|
|
<Controller
|
|
name="instlSncnMng.rtlztnFcltSeComCd"
|
|
control={control}
|
|
render={({ field, fieldState }) => (
|
|
<FieldSet>
|
|
<RadioGroup
|
|
name={field.name}
|
|
value={field.value || ""}
|
|
onValueChange={field.onChange}
|
|
>
|
|
{rtlztnFcltOptions?.map((option) => (
|
|
<FieldLabel
|
|
key={option.lwpoCd}
|
|
htmlFor={`rtlztn-fclt-${option.lwpoCd}`}
|
|
className="!w-auto border-none"
|
|
>
|
|
<Field
|
|
orientation="horizontal"
|
|
data-invalid={fieldState.invalid}
|
|
className="w-auto"
|
|
>
|
|
<RadioGroupItem
|
|
value={option.lwpoCd}
|
|
id={`rtlztn-fclt-${option.lwpoCd}`}
|
|
aria-invalid={fieldState.invalid}
|
|
/>
|
|
<span className="font-normal">{option.lwpoCdNm}</span>
|
|
</Field>
|
|
</FieldLabel>
|
|
))}
|
|
</RadioGroup>
|
|
<FieldError errors={[fieldState.error]} />
|
|
</FieldSet>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableHead scope="row">재이용용도</TableHead>
|
|
<TableCell colSpan={3}>
|
|
<Controller
|
|
name="instlSncnMng.rtlztnUsgSeComCd"
|
|
control={control}
|
|
render={({ field, fieldState }) => (
|
|
<FieldSet>
|
|
<FieldGroup data-slot="checkbox-group" className="flex-row">
|
|
{rtlztnUsgOptions?.map((option) => (
|
|
<Field
|
|
key={option.lwpoCd}
|
|
orientation="horizontal"
|
|
data-invalid={fieldState.invalid}
|
|
className="w-auto"
|
|
>
|
|
<Checkbox
|
|
id={`rtlztn-usg-${option.lwpoCd}`}
|
|
name={field.name}
|
|
aria-invalid={fieldState.invalid}
|
|
checked={field.value?.includes(option.lwpoCd) ?? false}
|
|
onCheckedChange={(checked) => {
|
|
const currentValue = field.value || "";
|
|
const values = currentValue ? currentValue.split(",") : [];
|
|
const newValue = checked
|
|
? [...values, option.lwpoCd]
|
|
: values.filter((value) => value !== option.lwpoCd);
|
|
field.onChange(newValue.join(","));
|
|
}}
|
|
/>
|
|
<FieldLabel
|
|
htmlFor={`rtlztn-usg-${option.lwpoCd}`}
|
|
className="font-normal"
|
|
>
|
|
{option.lwpoCdNm}
|
|
</FieldLabel>
|
|
</Field>
|
|
))}
|
|
</FieldGroup>
|
|
<FieldError errors={[fieldState.error]} />
|
|
</FieldSet>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
</>
|
|
)}
|
|
</TableHeader>
|
|
</Table>
|
|
|
|
<BnsPickerDialog
|
|
open={isPickerOpen}
|
|
onClose={() => setIsPickerOpen(false)}
|
|
onSelect={handleBnsSelect}
|
|
/>
|
|
</SectionCollapsible>
|
|
);
|
|
}
|
|
|
|
export { BasicInfoFormSection };
|