[st] refactor: 시도/시군구 셀렉트 API 변경
This commit is contained in:
@@ -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,
|
||||
});
|
||||
|
||||
+20
-2
@@ -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();
|
||||
@@ -28,6 +28,7 @@ export {
|
||||
type StAnlsDtlSearchParams,
|
||||
} from "./model/statistics-analysis/statistics-analysis-detail-interface";
|
||||
export {
|
||||
type AdstZoneResponseDto,
|
||||
type StAnlsMstListApiResponse,
|
||||
type StAnlsMstResponse,
|
||||
type StAnlsMstResponseDto,
|
||||
|
||||
+10
@@ -1,5 +1,15 @@
|
||||
import type { PageableList, PageableParams } from "@/shared/model";
|
||||
|
||||
/**
|
||||
* 행정구역(시도·시군구) 콤보 응답 DTO
|
||||
*/
|
||||
export interface AdstZoneResponseDto {
|
||||
/** 행정구역 코드 */
|
||||
adstCd: string;
|
||||
/** 행정구역 명 */
|
||||
adstNm: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 분석 현황 마스터 응답 DTO
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user