Merge branch 'iipbs/st/dev'
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Line } from "react-chartjs-2";
|
||||
|
||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
@@ -72,6 +73,7 @@ type DateRangeValue = {
|
||||
};
|
||||
|
||||
const EMPTY_DATA: never[] = [];
|
||||
const REGION_ALL_VALUE = "ALL";
|
||||
|
||||
function getImrDetailGridColumnInfo(statsCd: string): ColumnInfo {
|
||||
const visible =
|
||||
@@ -96,6 +98,21 @@ function getVerificationYearOptions(): string[] {
|
||||
);
|
||||
}
|
||||
|
||||
function applySingleOptionRegionDefault<T>(
|
||||
list: T[] | undefined,
|
||||
setValue: Dispatch<SetStateAction<string>>,
|
||||
resolveValue: (item: T) => string,
|
||||
) {
|
||||
if (!list || list.length !== 1) return;
|
||||
|
||||
const onlyItem = list[0];
|
||||
if (!onlyItem) return;
|
||||
|
||||
setValue((prev) =>
|
||||
prev === REGION_ALL_VALUE ? resolveValue(onlyItem) : prev,
|
||||
);
|
||||
}
|
||||
|
||||
const ImrVerificationHistory = () => {
|
||||
const yearOptions = useMemo(() => getVerificationYearOptions(), []);
|
||||
const defaultYear: string =
|
||||
@@ -104,8 +121,9 @@ const ImrVerificationHistory = () => {
|
||||
: "";
|
||||
|
||||
// Area A Search States
|
||||
const [searchSido, setSearchSido] = useState<string>("");
|
||||
const [searchSigungu, setSearchSigungu] = useState<string>("");
|
||||
const [searchSido, setSearchSido] = useState<string>(REGION_ALL_VALUE);
|
||||
const [searchSigungu, setSearchSigungu] =
|
||||
useState<string>(REGION_ALL_VALUE);
|
||||
const [searchStatsYr, setSearchStatsYr] = useState<string>(defaultYear);
|
||||
const [searchStatsCd, setSearchStatsCd] = useState<string>("");
|
||||
const [dateRange, setDateRange] = useState<DateRangeValue | undefined>({
|
||||
@@ -150,6 +168,28 @@ const ImrVerificationHistory = () => {
|
||||
gugunSkipYn: doList.includes(selectedSidoBsnmCd) ? "N" : "Y",
|
||||
});
|
||||
const sigunguOptions = sigunguData ?? [];
|
||||
const allowRegionAutoDefaultRef = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!allowRegionAutoDefaultRef.current) return;
|
||||
|
||||
applySingleOptionRegionDefault(
|
||||
sidoData?.sidoList,
|
||||
setSearchSido,
|
||||
(item) => item.sido,
|
||||
);
|
||||
}, [sidoData?.sidoList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!allowRegionAutoDefaultRef.current) return;
|
||||
if (searchSido === REGION_ALL_VALUE) return;
|
||||
|
||||
applySingleOptionRegionDefault(
|
||||
sigunguData,
|
||||
setSearchSigungu,
|
||||
(item) => item.gugun,
|
||||
);
|
||||
}, [sigunguData, searchSido]);
|
||||
|
||||
const imrDetailColumnInfo = useMemo(
|
||||
() => getImrDetailGridColumnInfo(selectedHistory?.statsCd ?? ""),
|
||||
@@ -248,12 +288,12 @@ const ImrVerificationHistory = () => {
|
||||
vrfcMthdCd: "IMR",
|
||||
sort: "anlsSn,desc",
|
||||
};
|
||||
if (searchSido !== "") {
|
||||
if (searchSido !== "" && searchSido !== REGION_ALL_VALUE) {
|
||||
const selectedSggBsnmCd = sigunguOptions.find(
|
||||
(item) => item.gugun === searchSigungu,
|
||||
)?.bsnmCd;
|
||||
params.sewerBzmnCd =
|
||||
searchSigungu !== ""
|
||||
searchSigungu !== "" && searchSigungu !== REGION_ALL_VALUE
|
||||
? (selectedSggBsnmCd ?? "")
|
||||
: selectedSidoBsnmCd;
|
||||
}
|
||||
@@ -271,8 +311,9 @@ const ImrVerificationHistory = () => {
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setSearchSido("");
|
||||
setSearchSigungu("");
|
||||
allowRegionAutoDefaultRef.current = false;
|
||||
setSearchSido(REGION_ALL_VALUE);
|
||||
setSearchSigungu(REGION_ALL_VALUE);
|
||||
setSearchStatsCd("");
|
||||
const defaultRange = {
|
||||
from: dayjs().subtract(1, "month").toDate(),
|
||||
@@ -410,13 +451,16 @@ const ImrVerificationHistory = () => {
|
||||
value={searchSido}
|
||||
onValueChange={(val) => {
|
||||
setSearchSido(val);
|
||||
setSearchSigungu("");
|
||||
setSearchSigungu(REGION_ALL_VALUE);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[14rem]">
|
||||
<SelectValue placeholder="시도 선택" />
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={REGION_ALL_VALUE}>
|
||||
전체
|
||||
</SelectItem>
|
||||
{sidoOptions.map((opt) => (
|
||||
<SelectItem
|
||||
key={opt.adstDstrCd}
|
||||
@@ -436,12 +480,18 @@ const ImrVerificationHistory = () => {
|
||||
<Select
|
||||
value={searchSigungu}
|
||||
onValueChange={setSearchSigungu}
|
||||
disabled={searchSido === ""}
|
||||
disabled={
|
||||
searchSido === "" ||
|
||||
searchSido === REGION_ALL_VALUE
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-[14rem]">
|
||||
<SelectValue placeholder="시군구 선택" />
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={REGION_ALL_VALUE}>
|
||||
전체
|
||||
</SelectItem>
|
||||
{sigunguOptions.map((opt) => (
|
||||
<SelectItem
|
||||
key={opt.adstDstrCd}
|
||||
|
||||
@@ -126,6 +126,8 @@ function useGridCheckboxSelection<T>(
|
||||
return { handleItemChecked, handleAllChecked };
|
||||
}
|
||||
|
||||
const REGION_ALL_OPTION = { label: "전체", value: "ALL" };
|
||||
|
||||
const DEFAULT_FILTERS: StatisticsVerificationFilterState = {
|
||||
ctpvNm: null,
|
||||
sggNm: null,
|
||||
@@ -133,6 +135,8 @@ const DEFAULT_FILTERS: StatisticsVerificationFilterState = {
|
||||
sewerBzmnCd: null,
|
||||
statsCd: null,
|
||||
pbcpSbmsnSttsCd: null,
|
||||
ctpvSewerBzmnCd: "ALL",
|
||||
sggSewerBzmnCd: "ALL",
|
||||
};
|
||||
|
||||
const DEFAULT_SUB_FILTERS: StatisticsVerificationSubFilterState = {
|
||||
@@ -422,14 +426,25 @@ function StatisticsVerificationReport() {
|
||||
|
||||
// 시도 변경 시
|
||||
if (key === "ctpvNm") {
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
sewerBzmnCd: value,
|
||||
ctpvNm: optionData?.label,
|
||||
ctpvSewerBzmnCd: value,
|
||||
sggNm: "",
|
||||
sggSewerBzmnCd: "",
|
||||
}));
|
||||
if (value === "ALL") {
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
ctpvNm: null,
|
||||
sggNm: null,
|
||||
sewerBzmnCd: null,
|
||||
ctpvSewerBzmnCd: "ALL",
|
||||
sggSewerBzmnCd: "ALL",
|
||||
}));
|
||||
} else {
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
sewerBzmnCd: value,
|
||||
ctpvNm: optionData?.label,
|
||||
ctpvSewerBzmnCd: value,
|
||||
sggNm: null,
|
||||
sggSewerBzmnCd: "ALL",
|
||||
}));
|
||||
}
|
||||
setSubFilters((prev) => ({
|
||||
...prev,
|
||||
ctpvNm: "",
|
||||
@@ -441,12 +456,25 @@ function StatisticsVerificationReport() {
|
||||
|
||||
// 시군구 변경 시
|
||||
if (key === "sggNm") {
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
sewerBzmnCd: value,
|
||||
sggNm: optionData?.label,
|
||||
sggSewerBzmnCd: value,
|
||||
}));
|
||||
if (value === "ALL") {
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
sggNm: null,
|
||||
sggSewerBzmnCd: "ALL",
|
||||
sewerBzmnCd:
|
||||
prev.ctpvSewerBzmnCd &&
|
||||
prev.ctpvSewerBzmnCd !== "ALL"
|
||||
? prev.ctpvSewerBzmnCd
|
||||
: null,
|
||||
}));
|
||||
} else {
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
sewerBzmnCd: value,
|
||||
sggNm: optionData?.label,
|
||||
sggSewerBzmnCd: value,
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -588,23 +616,31 @@ function StatisticsVerificationReport() {
|
||||
<div className="flex items-center gap-6">
|
||||
<FilterSelect
|
||||
label="시도"
|
||||
value={filters.ctpvSewerBzmnCd ?? ""}
|
||||
value={filters.ctpvSewerBzmnCd ?? "ALL"}
|
||||
onChange={handleFilterChange("ctpvNm")}
|
||||
options={sidoList.map((item) => ({
|
||||
label: item.sido,
|
||||
value: item.bsnmCd,
|
||||
}))}
|
||||
options={[
|
||||
REGION_ALL_OPTION,
|
||||
...sidoList.map((item) => ({
|
||||
label: item.sido,
|
||||
value: item.bsnmCd,
|
||||
})),
|
||||
]}
|
||||
/>
|
||||
<FilterSelect
|
||||
label="시군구"
|
||||
value={filters.sggSewerBzmnCd ?? ""}
|
||||
value={filters.sggSewerBzmnCd ?? "ALL"}
|
||||
onChange={handleFilterChange("sggNm")}
|
||||
options={
|
||||
sigunguData?.map((item) => ({
|
||||
disabled={
|
||||
!filters.ctpvSewerBzmnCd ||
|
||||
filters.ctpvSewerBzmnCd === "ALL"
|
||||
}
|
||||
options={[
|
||||
REGION_ALL_OPTION,
|
||||
...(sigunguData?.map((item) => ({
|
||||
label: item.gugun,
|
||||
value: item.bsnmCd,
|
||||
})) ?? []
|
||||
}
|
||||
})) ?? []),
|
||||
]}
|
||||
/>
|
||||
<FilterSelect
|
||||
label="년도"
|
||||
@@ -752,6 +788,7 @@ interface FilterSelectProps {
|
||||
) => void;
|
||||
options: Array<{ label: string; value: string }>;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
function FilterCombobox({
|
||||
@@ -818,12 +855,19 @@ function FilterCombobox({
|
||||
);
|
||||
}
|
||||
|
||||
function FilterSelect({ label, onChange, options, value }: FilterSelectProps) {
|
||||
function FilterSelect({
|
||||
label,
|
||||
onChange,
|
||||
options,
|
||||
value,
|
||||
disabled,
|
||||
}: FilterSelectProps) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="whitespace-nowrap">{label}</Label>
|
||||
<Select
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onValueChange={(value) =>
|
||||
onChange(
|
||||
value,
|
||||
|
||||
@@ -40,8 +40,8 @@ const HbVerificationDetail: React.FC<HbVerificationDetailProps> = ({
|
||||
);
|
||||
const [checkedSns, setCheckedSns] = useState<string[]>([]);
|
||||
const [appliedFilter, setAppliedFilter] = useState<AppliedFilter>({
|
||||
sido: "",
|
||||
gugun: "",
|
||||
sido: "ALL",
|
||||
gugun: "ALL",
|
||||
status: "",
|
||||
});
|
||||
const [selectedRowId, setSelectedRowId] = useState<number | null>(null);
|
||||
|
||||
@@ -26,6 +26,7 @@ import type { AppliedFilter } from "./HbVerificationDetail";
|
||||
import DataGrid from "@/components/module/DataGrid";
|
||||
|
||||
const EMPTY_ADST_LIST: AdstZoneResponseDto[] = [];
|
||||
const REGION_ALL_VALUE = "ALL";
|
||||
|
||||
interface RegionalGridProps {
|
||||
anlsSn: number;
|
||||
@@ -53,7 +54,10 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
|
||||
>([]);
|
||||
|
||||
const { data: sidoData } = useStAnlsSidoCombo();
|
||||
const { data: gugunData } = useStAnlsSigunguCombo(appliedFilter.sido ?? "");
|
||||
const sidoCd = appliedFilter.sido ?? "";
|
||||
const { data: gugunData } = useStAnlsSigunguCombo(
|
||||
sidoCd !== REGION_ALL_VALUE ? sidoCd : "",
|
||||
);
|
||||
const sidoOptions = sidoData ?? EMPTY_ADST_LIST;
|
||||
const gugunOptions = gugunData ?? EMPTY_ADST_LIST;
|
||||
|
||||
@@ -66,14 +70,14 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
|
||||
let data = (resultData?.content ?? []) as StAnlsRsltResponse[];
|
||||
{
|
||||
const sidoCd = appliedFilter.sido;
|
||||
if (sidoCd) {
|
||||
if (sidoCd && sidoCd !== REGION_ALL_VALUE) {
|
||||
data = data.filter((item) =>
|
||||
item.admdstCd.startsWith(sidoCd.slice(0, 2)),
|
||||
);
|
||||
}
|
||||
|
||||
const gugunCd = appliedFilter.gugun;
|
||||
if (gugunCd) {
|
||||
if (gugunCd && gugunCd !== REGION_ALL_VALUE) {
|
||||
data = data.filter((item) => item.admdstCd === gugunCd);
|
||||
}
|
||||
|
||||
@@ -107,13 +111,17 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
|
||||
}, [resultData, appliedFilter]);
|
||||
|
||||
const handleSidoChange = (val: string) => {
|
||||
onFilterChange({ ...appliedFilter, sido: val, gugun: "" });
|
||||
onFilterChange({
|
||||
...appliedFilter,
|
||||
sido: val,
|
||||
gugun: REGION_ALL_VALUE,
|
||||
});
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
onFilterChange({
|
||||
sido: "",
|
||||
gugun: "",
|
||||
sido: REGION_ALL_VALUE,
|
||||
gugun: REGION_ALL_VALUE,
|
||||
status: "",
|
||||
});
|
||||
};
|
||||
@@ -156,13 +164,16 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Select
|
||||
value={appliedFilter.sido || ""}
|
||||
value={appliedFilter.sido || REGION_ALL_VALUE}
|
||||
onValueChange={handleSidoChange}
|
||||
>
|
||||
<SelectTrigger className="w-[12rem] bg-white">
|
||||
<SelectValue placeholder="시도 선택" />
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={REGION_ALL_VALUE}>
|
||||
전체
|
||||
</SelectItem>
|
||||
{sidoOptions.map((opt) => (
|
||||
<SelectItem key={opt.adstCd} value={opt.adstCd}>
|
||||
{opt.adstNm}
|
||||
@@ -171,16 +182,22 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select
|
||||
value={appliedFilter.gugun || ""}
|
||||
value={appliedFilter.gugun || REGION_ALL_VALUE}
|
||||
onValueChange={(val) =>
|
||||
onFilterChange({ ...appliedFilter, gugun: val })
|
||||
}
|
||||
disabled={!appliedFilter.sido}
|
||||
disabled={
|
||||
!appliedFilter.sido ||
|
||||
appliedFilter.sido === REGION_ALL_VALUE
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-[12rem] bg-white">
|
||||
<SelectValue placeholder="시군구 선택" />
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={REGION_ALL_VALUE}>
|
||||
전체
|
||||
</SelectItem>
|
||||
{gugunOptions.map((opt) => (
|
||||
<SelectItem key={opt.adstCd} value={opt.adstCd}>
|
||||
{opt.adstNm}
|
||||
|
||||
@@ -73,7 +73,7 @@ export const useStAnlsSigunguCombo = (sidoCd: string) => {
|
||||
return useQuery({
|
||||
queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST, "sigungu", sidoCd],
|
||||
queryFn: () => statisticsAnalysisMasterService.getSigunguCombo(sidoCd),
|
||||
enabled: Boolean(sidoCd),
|
||||
enabled: Boolean(sidoCd) && sidoCd !== "ALL",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user