[st] refactor: 시도/시군구 셀렉트 API 변경

This commit is contained in:
2026-03-23 11:45:34 +09:00
parent 1728d1879c
commit a46b185c16
8 changed files with 122 additions and 80 deletions
@@ -24,7 +24,7 @@ import type {
} from "@/entities/statistics";
import { verificationHistoryColumns } from "@/entities/statistics";
import { analysisApi } from "@/entities/statistics/api/analysis-api";
import { analysisHistoryApi } from "@/entities/statistics/api/analysis-history-api";
import { stAnlsMstApi } from "@/entities/statistics/api/statistics-analysis-master-api";
import { verificationTargetManagementApi } from "@/entities/statistics/api/verification-target-management-service";
import type { VerificationTargetCodeResponse } from "@/entities/statistics/model/verification-target-management-interface";
import {
@@ -202,7 +202,7 @@ const HbStatisticsVerification = () => {
params.verifyDtEnd = dayjs(to).format("YYYY-MM-DD");
}
return analysisHistoryApi.getAnalysisHistoryList(params);
return stAnlsMstApi.getAnalysisHistoryList(params);
},
refetchInterval: (query) => {
const data = query.state.data;
@@ -242,7 +242,7 @@ const HbStatisticsVerification = () => {
],
queryFn: async () => {
if (!outlierDialog.anlsSn) return [];
const response = await analysisHistoryApi.getOutlierList(
const response = await stAnlsMstApi.getOutlierList(
outlierDialog.anlsSn,
);
return response;
@@ -33,7 +33,7 @@ import {
imrDetailGridColumnInfo,
imrVerificationHistoryColumns,
} from "@/entities/statistics";
import { analysisHistoryApi } from "@/entities/statistics/api/analysis-history-api";
import { stAnlsMstApi } from "@/entities/statistics/api/statistics-analysis-master-api";
import { imrApi } from "@/entities/statistics/api/imr-api";
import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api";
import {
@@ -130,7 +130,7 @@ const ImrVerificationHistory = () => {
historyPagination,
],
queryFn: () =>
analysisHistoryApi.getAnalysisHistoryList({
stAnlsMstApi.getAnalysisHistoryList({
...appliedSearchParams,
page: historyPagination.pageIndex,
size: historyPagination.pageSize,
@@ -1,11 +1,14 @@
import React, { useEffect, useMemo, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { ENDPOINT } from "@/shared/config";
import { CodeApi } from "@/entities/code/api/code-service";
import type { StAnlsRsltResponse } from "@/entities/statistics";
import { useStAnlsRsltList } from "@/entities/statistics/api/queries";
import type {
AdstZoneResponseDto,
StAnlsRsltResponse,
} from "@/entities/statistics";
import {
useStAnlsRsltList,
useStAnlsSidoCombo,
useStAnlsSigunguCombo,
} from "@/entities/statistics/api/queries";
import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api";
import { regionalGridColumnInfo as columnInfo } from "@/entities/statistics/model/regional-grid-columns";
import {
@@ -22,6 +25,8 @@ import type { AppliedFilter } from "./HbVerificationDetail";
import DataGrid from "@/components/module/DataGrid";
const EMPTY_ADST_LIST: AdstZoneResponseDto[] = [];
interface RegionalGridProps {
anlsSn: number;
vrfcTrgtSn: string | null;
@@ -52,27 +57,10 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
Record<string, string | number>[]
>([]);
const { data: regionCodes } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.CODE, "LAESECD"],
queryFn: () => CodeApi.getCode("LAESECD"),
});
const sidoOptions = useMemo(() => {
if (!regionCodes) return [];
return regionCodes.filter(
(code) => code.lwpoCd.startsWith("G3") && code.lwpoCd.length === 4,
);
}, [regionCodes]);
const gugunOptions = useMemo(() => {
if (!regionCodes || !selectedSido) return [];
const sidoPrefix = selectedSido;
return regionCodes.filter(
(code) =>
code.lwpoCd.startsWith(sidoPrefix) && code.lwpoCd.length === 7,
);
}, [regionCodes, selectedSido]);
const { data: sidoData } = useStAnlsSidoCombo();
const { data: gugunData } = useStAnlsSigunguCombo(selectedSido);
const sidoOptions = sidoData ?? EMPTY_ADST_LIST;
const gugunOptions = gugunData ?? EMPTY_ADST_LIST;
const { data: resultData, isLoading } = useStAnlsRsltList(
anlsSn,
@@ -90,22 +78,22 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
data = showAllOnEmptyFilter ? data : [];
} else {
if (appliedFilter.sido) {
const sidoName = sidoOptions.find(
(opt) => opt.lwpoCd === appliedFilter.sido,
)?.lwpoCdNm;
if (sidoName) {
data = data.filter((item) => item.sido === sidoName);
const sidoCd = sidoOptions.find(
(opt) => opt.adstCd === appliedFilter.sido,
)?.adstCd;
if (sidoCd) {
data = data.filter((item) =>
item.admdstCd.startsWith(sidoCd.slice(0, 2)),
);
}
}
if (appliedFilter.gugun) {
const gugunName = gugunOptions.find(
(opt) => opt.lwpoCd === appliedFilter.gugun,
)?.lwpoCdNm;
if (gugunName) {
data = data.filter((item) =>
gugunName.includes(item.gugun),
);
const gugunCd = gugunOptions.find(
(opt) => opt.adstCd === appliedFilter.gugun,
)?.adstCd;
if (gugunCd) {
data = data.filter((item) => item.admdstCd === gugunCd);
}
}
@@ -135,9 +123,6 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
otlrYn: mapOtlrToLabel(item.otlrYnCd),
}));
setGridRowData(
mappedData as unknown as Record<string, string | number>[],
);
return mappedData;
}, [
resultData,
@@ -198,6 +183,10 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
}
};
useEffect(() => {
setGridRowData(rowData as Record<string, string | number>[]);
}, [rowData]);
useEffect(() => {
handleReset();
}, [vrfcTrgtSn]);
@@ -215,8 +204,8 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
</SelectTrigger>
<SelectContent>
{sidoOptions.map((opt) => (
<SelectItem key={opt.lwpoCd} value={opt.lwpoCd}>
{opt.lwpoCdNm}
<SelectItem key={opt.adstCd} value={opt.adstCd}>
{opt.adstNm}
</SelectItem>
))}
</SelectContent>
@@ -231,8 +220,8 @@ const RegionalGrid: React.FC<RegionalGridProps> = ({
</SelectTrigger>
<SelectContent>
{gugunOptions.map((opt) => (
<SelectItem key={opt.lwpoCd} value={opt.lwpoCd}>
{opt.lwpoCdNm}
<SelectItem key={opt.adstCd} value={opt.adstCd}>
{opt.adstNm}
</SelectItem>
))}
</SelectContent>
@@ -1,7 +1,6 @@
import React, { useMemo } from "react";
import { Scatter } from "react-chartjs-2";
import { useQuery } from "@tanstack/react-query";
import {
Chart as ChartJS,
type ChartData,
@@ -17,8 +16,6 @@ import {
} from "chart.js";
import zoomPlugin from "chartjs-plugin-zoom";
import { ENDPOINT } from "@/shared/config";
import { CodeApi } from "@/entities/code/api/code-service";
import type { StAnlsRsltResponse } from "@/entities/statistics";
import {
useStAnlsChartData,
@@ -55,29 +52,28 @@ interface DataPoint extends LinePoint {
item: StAnlsRsltResponse;
}
function matchesAdmdstFilter(
admdstCd: string,
sidoCd: string | null | undefined,
gugunCd: string | undefined,
): boolean {
const gugun = (gugunCd ?? "").trim();
if (gugun) {
return admdstCd === gugun;
}
const sido = (sidoCd ?? "").trim();
if (!sido) {
return true;
}
return admdstCd.startsWith(sido.slice(0, 2));
}
const ScatterChart: React.FC<ScatterChartProps> = ({
anlsSn,
vrfcTrgtSn,
appliedFilter,
onPointClick,
}) => {
const { data: regionCodes } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.CODE, "LAESECD"],
queryFn: () => CodeApi.getCode("LAESECD"),
});
const filterNames = useMemo(() => {
if (!regionCodes) return { sido: null, gugun: null };
return {
sido:
regionCodes.find((c) => c.lwpoCd === appliedFilter.sido)
?.lwpoCdNm || null,
gugun:
regionCodes.find((c) => c.lwpoCd === appliedFilter.gugun)
?.lwpoCdNm || null,
};
}, [regionCodes, appliedFilter.sido, appliedFilter.gugun]);
const { data: resultData, isLoading: isLoadingGrid } = useStAnlsRsltList(
anlsSn,
vrfcTrgtSn,
@@ -113,12 +109,13 @@ const ScatterChart: React.FC<ScatterChartProps> = ({
const outlierPoints: DataPoint[] = [];
rawPoints.forEach((item) => {
// Apply Filters (Sido, Gugun, Status)
if (filterNames.sido && item.sido !== filterNames.sido) {
return;
}
if (filterNames.gugun && !filterNames.gugun.includes(item.gugun)) {
if (
!matchesAdmdstFilter(
item.admdstCd,
appliedFilter.sido,
appliedFilter.gugun,
)
) {
return;
}
@@ -249,7 +246,13 @@ const ScatterChart: React.FC<ScatterChartProps> = ({
};
return { data, options };
}, [resultData, chartData, filterNames, appliedFilter.status]);
}, [
resultData,
chartData,
appliedFilter.sido,
appliedFilter.gugun,
appliedFilter.status,
]);
return (
<div className="relative flex-1 p-4">
@@ -2,8 +2,28 @@ import { useQuery } from "@tanstack/react-query";
import { ENDPOINT } from "@/shared/config";
import { stAnlsMstApi } from "./statistics-analysis-master-api";
import { stAnlsRsltApi } from "./statistics-analysis-result-api";
export const useStAnlsSidoCombo = () => {
return useQuery({
queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST, "sido"],
queryFn: () => stAnlsMstApi.getSidoCombo(),
});
};
export const useStAnlsSigunguCombo = (sidoCd: string) => {
return useQuery({
queryKey: [
ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST,
"sigungu",
sidoCd,
],
queryFn: () => stAnlsMstApi.getSigunguCombo(sidoCd),
enabled: Boolean(sidoCd),
});
};
export const useStAnlsRsltList = (
anlsSn: number,
vrfcTrgtSn: string | null,
@@ -21,6 +41,7 @@ export const useStAnlsRsltList = (
vrfcTrgtSn: Number(vrfcTrgtSn),
page: 0,
size: 1000,
sort: "admdstCd,asc",
}),
enabled: !!anlsSn && !!vrfcTrgtSn,
});
@@ -2,12 +2,13 @@ import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
AdstZoneResponseDto,
StAnlsMstListApiResponse,
StAnlsMstSearchParams,
StAnlsOutlierListResponse,
} from "../model/statistics-analysis/statistics-analysis-master-interface";
export class AnalysisHistoryApiService {
export class StAnlsMstApiService {
public async getAnalysisHistoryList(
params: StAnlsMstSearchParams,
): Promise<StAnlsMstListApiResponse> {
@@ -30,6 +31,23 @@ export class AnalysisHistoryApiService {
return response.data;
}
public async getSidoCombo(): Promise<AdstZoneResponseDto[]> {
const response = await client.get<AdstZoneResponseDto[]>(
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/sido`,
);
return response.data;
}
public async getSigunguCombo(sidoCd: string): Promise<AdstZoneResponseDto[]> {
const response = await client.get<AdstZoneResponseDto[]>(
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/sigungu`,
{ params: { sidoCd } },
);
return response.data;
}
}
export const analysisHistoryApi = new AnalysisHistoryApiService();
export const stAnlsMstApi = new StAnlsMstApiService();
+1
View File
@@ -28,6 +28,7 @@ export {
type StAnlsDtlSearchParams,
} from "./model/statistics-analysis/statistics-analysis-detail-interface";
export {
type AdstZoneResponseDto,
type StAnlsMstListApiResponse,
type StAnlsMstResponse,
type StAnlsMstResponseDto,
@@ -1,5 +1,15 @@
import type { PageableList, PageableParams } from "@/shared/model";
/**
* 행정구역(시도·시군구) 콤보 응답 DTO
*/
export interface AdstZoneResponseDto {
/** 행정구역 코드 */
adstCd: string;
/** 행정구역 명 */
adstNm: string;
}
/**
* 분석 현황 마스터 응답 DTO
*/