259 lines
11 KiB
TypeScript
259 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import useSWR from 'swr';
|
|
|
|
import {
|
|
Button,
|
|
Input,
|
|
Label,
|
|
Select,
|
|
SelectTrigger,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectValue
|
|
}
|
|
from "@common/components";
|
|
|
|
import { useAlertStore } from "@/shared/lib";
|
|
import { type SearchFormProps, type SearchFormValues, defaultSearchFormProps } from "../type/SearchForm.type";
|
|
import { statisticsAPi } from "@/entities/statistics/api/statistics-service";
|
|
import type { ApiResponse, SearchCondition, Sgg } from "@/entities/statistics/model/statistics-interface";
|
|
|
|
const getSidoListFetcher = async (): Promise<ApiResponse<SearchCondition>> => {
|
|
return statisticsAPi.getSidoList();
|
|
};
|
|
|
|
const getSggListFetcher = async (key: [string, string]): Promise<ApiResponse<Sgg[]>> => {
|
|
return statisticsAPi.getSggList(key[1]);
|
|
};
|
|
|
|
const SearchForm = (props: SearchFormProps = {}) => {
|
|
const { setMessage: alert } = useAlertStore((state) => state);
|
|
const mergedProps = { ...defaultSearchFormProps, ...props };
|
|
|
|
const currentYear = new Date().getFullYear();
|
|
const defaultYear = currentYear - 1;
|
|
|
|
const { data: sidoList } = useSWR<ApiResponse<SearchCondition>>(
|
|
"sido-list",
|
|
getSidoListFetcher,
|
|
{
|
|
revalidateOnFocus: false,
|
|
revalidateOnReconnect: false,
|
|
revalidateIfStale: false,
|
|
dedupingInterval: 2000,
|
|
shouldRetryOnError: false,
|
|
onSuccess: (data) => {
|
|
if (data.status !== 'success') {
|
|
alert(`시도 목록을 불러오는데 실패했습니다.
|
|
새로고침 후 다시 시도해주세요.`);
|
|
}
|
|
},
|
|
onError: () => {
|
|
alert(`시도 목록을 불러오는데 실패했습니다.
|
|
새로고침 후 다시 시도해주세요.`);
|
|
},
|
|
}
|
|
);
|
|
|
|
const [values, setValues] = useState<SearchFormValues>({
|
|
sido: props.defaultValues?.sido || '',
|
|
gugun: props.defaultValues?.gugun || '',
|
|
year: props.defaultValues?.year || defaultYear.toString(),
|
|
order: props.defaultValues?.order || '',
|
|
fctName: props.defaultValues?.fctName || '',
|
|
bsnmCd: props.defaultValues?.bsnmCd || '',
|
|
});
|
|
|
|
const sggKey: [string, string] | null =
|
|
values.sido && values.sido !== "ALL"
|
|
? ["sgg-list", values.sido]
|
|
: null;
|
|
const { data: sggList } = useSWR<ApiResponse<Sgg[]>>(
|
|
sggKey,
|
|
getSggListFetcher,
|
|
{
|
|
revalidateOnFocus: false,
|
|
revalidateOnReconnect: false,
|
|
revalidateIfStale: false,
|
|
dedupingInterval: 2000,
|
|
shouldRetryOnError: false,
|
|
onSuccess: (data) => {
|
|
if (data.status !== 'success') {
|
|
alert(`시군구 목록을 불러오는데 실패했습니다.
|
|
새로고침 후 다시 시도해주세요.`);
|
|
}
|
|
},
|
|
onError: () => {
|
|
alert(`시군구 목록을 불러오는데 실패했습니다.
|
|
새로고침 후 다시 시도해주세요.`);
|
|
},
|
|
}
|
|
);
|
|
const sggOptions = sggList?.data ?? [];
|
|
|
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
props.onSubmit?.(values);
|
|
};
|
|
|
|
const handleReset = () => {
|
|
const resetValues: SearchFormValues = {
|
|
sido: '',
|
|
gugun: '',
|
|
year: String(defaultYear),
|
|
order: '',
|
|
fctName: '',
|
|
bsnmCd: '',
|
|
};
|
|
setValues(resetValues);
|
|
props.onReset?.();
|
|
};
|
|
|
|
const handleSidoChange = (value: string) => {
|
|
const selectedBsnm = sidoList?.data.sidoList.find((v) => v.sido === value)?.bsnmCd || '';
|
|
setValues((prev) => ({
|
|
...prev,
|
|
sido: value,
|
|
gugun: '',
|
|
bsnmCd: selectedBsnm,
|
|
}));
|
|
};
|
|
|
|
const handleSggChange = (value: string) => {
|
|
const listForBsnm = sggList?.data ?? [];
|
|
const selectedBsnm = value === 'ALL'
|
|
? (sidoList?.data.sidoList.find((v) => v.sido === values.sido)?.bsnmCd || '')
|
|
: (listForBsnm.find((v) => v.gugun === value)?.bsnmCd || '');
|
|
|
|
setValues((prev) => ({
|
|
...prev,
|
|
gugun: value,
|
|
bsnmCd: selectedBsnm,
|
|
}));
|
|
};
|
|
|
|
useEffect(() => {
|
|
props.onValuesChange?.(values);
|
|
}, [values]);
|
|
|
|
return (
|
|
<div className="card card-border">
|
|
<form onSubmit={handleSubmit}>
|
|
<div className="flex items-center gap-8 p-2">
|
|
{mergedProps.sido &&
|
|
<div className="flex items-center gap-2">
|
|
<Label className="text-label">
|
|
시도
|
|
</Label>
|
|
<Select value={values.sido} onValueChange={handleSidoChange}>
|
|
<SelectTrigger className="w-[12rem]">
|
|
<SelectValue placeholder="선택"></SelectValue>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="ALL">선택</SelectItem>
|
|
{sidoList?.data?.sidoList?.map((sido) => (
|
|
<SelectItem key={sido.adstDstrCd} value={sido.sido}>
|
|
{sido.sido}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
}
|
|
{mergedProps.gugun &&
|
|
<div className="flex items-center gap-2">
|
|
<Label className="text-label">
|
|
시군구
|
|
</Label>
|
|
<Select value={values.gugun} onValueChange={handleSggChange}>
|
|
<SelectTrigger className="w-[12rem]">
|
|
<SelectValue placeholder="전체"></SelectValue>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="ALL">전체</SelectItem>
|
|
{sggOptions?.map((sgg) => (
|
|
<SelectItem key={sgg.adstDstrCd} value={sgg.gugun}>
|
|
{sgg.gugun}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
}
|
|
{mergedProps.year &&
|
|
<div className="flex items-center gap-2">
|
|
<Label className="text-label">
|
|
년도
|
|
</Label>
|
|
<Select value={values.year} onValueChange={(value) => setValues({ ...values, year: value })}>
|
|
<SelectTrigger className="w-[10rem]">
|
|
<SelectValue placeholder="선택"></SelectValue>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{sidoList?.data?.yearList.map((year) => (
|
|
<SelectItem key={year} value={String(year)}>
|
|
{year}년
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
}
|
|
{mergedProps.order &&
|
|
<div className="flex items-center gap-2">
|
|
<Label className="text-label">
|
|
차수
|
|
</Label>
|
|
<Select value={values.order} onValueChange={(value) => setValues({ ...values, order: value })}>
|
|
<SelectTrigger className="w-[10rem]">
|
|
<SelectValue placeholder="전체"></SelectValue>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="ALL">전체</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
}
|
|
<div className="flex items-center gap-2 ml-auto">
|
|
<Button color="green" className="h-[3.2rem] w-[96px]" type="button" onClick={handleReset}>
|
|
초기화
|
|
</Button>
|
|
<Button className="h-[3.2rem] w-[96px]" type="submit">
|
|
조회
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
{(mergedProps.fctName || mergedProps.userMenual) &&
|
|
<div className="flex items-center p-2">
|
|
{mergedProps.fctName &&
|
|
<div className="flex items-center gap-2">
|
|
<Label htmlFor="fctNmSearchInput" className="mr-2 text-label">
|
|
시설명
|
|
</Label>
|
|
<Input
|
|
className="h-[3.2rem] w-[14rem] rounded-sm !text-[1.3rem] mr-4"
|
|
id="fctNmSearchInput"
|
|
value={values.fctName}
|
|
onChange={(e) => setValues({ ...values, fctName: e.target.value })}
|
|
autoComplete="off"
|
|
/>
|
|
</div>
|
|
}
|
|
{mergedProps.userMenual &&
|
|
<div className="flex items-center gap-2 ml-auto">
|
|
<Button className="h-[3.2rem] w-[124px]" type="button">
|
|
<i className="diveicon di-person-fill" />
|
|
사용자 메뉴얼
|
|
</Button>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</form>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SearchForm; |