Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev
This commit is contained in:
@@ -8,14 +8,15 @@ import {
|
||||
type RowSelectionState,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import type { AxiosError } from "axios";
|
||||
|
||||
import { client } from "@/shared/api/client";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { useAlertStore, useMutationWrapper } from "@/shared/lib";
|
||||
import type { ApiErrorResponse } from "@/shared/model";
|
||||
import {
|
||||
type AdstZoneResponseDto,
|
||||
type BusinessSendRequest,
|
||||
type BusinessSendResponse,
|
||||
noticeTalkSendingApi,
|
||||
type SelectedRecipient,
|
||||
type SewerStUserItem,
|
||||
useNtftalkTemplateList,
|
||||
@@ -122,10 +123,26 @@ function NtitlkSndng() {
|
||||
const [previewRecipient, setPreviewRecipient] =
|
||||
useState<SelectedRecipient | null>(null);
|
||||
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
const [userListRowSelection, setUserListRowSelection] =
|
||||
useState<RowSelectionState>({});
|
||||
|
||||
const sendMutation = useMutationWrapper<
|
||||
BusinessSendResponse,
|
||||
AxiosError<ApiErrorResponse>,
|
||||
BusinessSendRequest
|
||||
>({
|
||||
mutationFn: (payload) => noticeTalkSendingApi.sendBusiness(payload),
|
||||
onSuccess: (data) => {
|
||||
if (!data.success) {
|
||||
alert(data.message || "알림톡 발송 요청에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
alert(data.message || "알림톡 발송이 요청되었습니다.");
|
||||
setSelectedRecipients([]);
|
||||
setCommonVariableValues({});
|
||||
},
|
||||
});
|
||||
|
||||
const { data: templatePageData } = useNtftalkTemplateList();
|
||||
const templates = templatePageData?.content ?? [];
|
||||
|
||||
@@ -321,7 +338,7 @@ function NtitlkSndng() {
|
||||
}),
|
||||
);
|
||||
if (missingVariable) {
|
||||
alert("필수 변수값이 입력되지 않은 발송 대상이 있습니다.");
|
||||
alert("변수값이 입력되지 않은 발송 대상이 있습니다.");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
@@ -331,34 +348,13 @@ function NtitlkSndng() {
|
||||
)
|
||||
return;
|
||||
|
||||
const payload: BusinessSendRequest = {
|
||||
sendMutation.mutate({
|
||||
templateCode: selectedTemplateCode,
|
||||
callback: "032-590-4000",
|
||||
recipients: selectedRecipients.map((recipient) =>
|
||||
getMergedVariables(recipient, commonVariableValues),
|
||||
),
|
||||
};
|
||||
|
||||
try {
|
||||
setIsSending(true);
|
||||
const response = await client.post<BusinessSendResponse>(
|
||||
`${ENDPOINT.NTFTALK_SERVICE.ATA_SEND}/business`,
|
||||
payload,
|
||||
);
|
||||
const data = response.data;
|
||||
if (!data.success) {
|
||||
alert(data.message || "알림톡 발송 요청에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
alert(data.message || `알림톡 발송이 요청되었습니다.`);
|
||||
setSelectedRecipients([]);
|
||||
setCommonVariableValues({});
|
||||
} catch {
|
||||
alert("알림톡 발송 중 오류가 발생했습니다.");
|
||||
} finally {
|
||||
setIsSending(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -567,7 +563,8 @@ function NtitlkSndng() {
|
||||
color="green"
|
||||
onClick={handleSend}
|
||||
disabled={
|
||||
isSending || selectedRecipients.length === 0
|
||||
sendMutation.isPending ||
|
||||
selectedRecipients.length === 0
|
||||
}
|
||||
>
|
||||
발송
|
||||
|
||||
@@ -20,18 +20,18 @@ import { ENDPOINT } from "@/shared/config";
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import type { ApiErrorResponse } from "@/shared/model";
|
||||
import { CodeSelect } from "@/entities/code";
|
||||
import type {
|
||||
AnalysisRequestPayload,
|
||||
AnalysisResponseDto,
|
||||
StAnlsMstResponse,
|
||||
StAnlsMstSearchParams,
|
||||
StAnlsOutlierListResponse,
|
||||
import {
|
||||
analysisApi,
|
||||
type AnalysisRequestPayload,
|
||||
type AnalysisResponseDto,
|
||||
type StAnlsMstResponse,
|
||||
type StAnlsMstSearchParams,
|
||||
type StAnlsOutlierListResponse,
|
||||
statisticsAnalysisMasterService,
|
||||
verificationHistoryColumns,
|
||||
type VerificationTargetCodeResponse,
|
||||
verificationTargetManagementApi,
|
||||
} from "@/entities/statistics";
|
||||
import { verificationHistoryColumns } from "@/entities/statistics";
|
||||
import { analysisApi } from "@/entities/statistics/api/analysis-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 {
|
||||
Button,
|
||||
Checkbox,
|
||||
@@ -102,7 +102,7 @@ function toNumber(value: string): number {
|
||||
return Number.isNaN(parsed) ? 0 : parsed;
|
||||
}
|
||||
|
||||
const HbStatisticsVerification = () => {
|
||||
export default function HbStatisticsVerification() {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
@@ -114,10 +114,10 @@ const HbStatisticsVerification = () => {
|
||||
|
||||
const [verifyType, setVerifyType] = useState<VerifyType>("ALL");
|
||||
const [formState, setFormState] = useState<VerificationFormState>({
|
||||
mu: "0",
|
||||
percentile: "0",
|
||||
a: "0",
|
||||
c: "0",
|
||||
mu: "",
|
||||
percentile: "",
|
||||
a: "",
|
||||
c: "",
|
||||
verificationYear: defaultYear,
|
||||
});
|
||||
const [selectedTargets, setSelectedTargets] = useState<string[]>([]);
|
||||
@@ -207,7 +207,9 @@ const HbStatisticsVerification = () => {
|
||||
params.verifyDtEnd = dayjs(to).format("YYYY-MM-DD");
|
||||
}
|
||||
|
||||
return stAnlsMstApi.getAnalysisHistoryList(params);
|
||||
return statisticsAnalysisMasterService.getAnalysisHistoryList(
|
||||
params,
|
||||
);
|
||||
},
|
||||
refetchInterval: (query) => {
|
||||
const data = query.state.data;
|
||||
@@ -248,9 +250,10 @@ const HbStatisticsVerification = () => {
|
||||
],
|
||||
queryFn: async () => {
|
||||
if (!outlierDialog.anlsSn) return [];
|
||||
const response = await stAnlsMstApi.getOutlierList(
|
||||
outlierDialog.anlsSn,
|
||||
);
|
||||
const response =
|
||||
await statisticsAnalysisMasterService.getOutlierList(
|
||||
outlierDialog.anlsSn,
|
||||
);
|
||||
return response;
|
||||
},
|
||||
enabled: outlierDialog.open && !!outlierDialog.anlsSn,
|
||||
@@ -556,11 +559,14 @@ const HbStatisticsVerification = () => {
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-4">
|
||||
<Label className="w-32 text-label">
|
||||
μ
|
||||
<Label className="w-48 whitespace-nowrap text-label">
|
||||
중요도 계수(μ)
|
||||
</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.1"
|
||||
value={formState.mu}
|
||||
onChange={(event) =>
|
||||
handleFormChange(
|
||||
@@ -569,15 +575,19 @@ const HbStatisticsVerification = () => {
|
||||
)
|
||||
}
|
||||
className="flex-1"
|
||||
placeholder="(0 ~ 1, 0.1 단위)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<Label className="w-32 text-label">
|
||||
Percentile
|
||||
<Label className="w-48 text-label">
|
||||
백분위수 기준값(pct)
|
||||
</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="0.5"
|
||||
step="0.01"
|
||||
value={formState.percentile}
|
||||
onChange={(event) =>
|
||||
handleFormChange(
|
||||
@@ -586,15 +596,19 @@ const HbStatisticsVerification = () => {
|
||||
)
|
||||
}
|
||||
className="flex-1"
|
||||
placeholder="(0 ~ 0.5, 0.01 단위)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<Label className="w-32 text-label">
|
||||
A
|
||||
<Label className="w-48 text-label">
|
||||
스케일 조정 계수(A)
|
||||
</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={formState.a}
|
||||
onChange={(event) =>
|
||||
handleFormChange(
|
||||
@@ -603,15 +617,19 @@ const HbStatisticsVerification = () => {
|
||||
)
|
||||
}
|
||||
className="flex-1"
|
||||
placeholder="(0 ~ 1, 0.01 단위)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<Label className="w-32 text-label">
|
||||
C
|
||||
<Label className="w-48 text-label">
|
||||
임계 계수(c)
|
||||
</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
value={formState.c}
|
||||
onChange={(event) =>
|
||||
handleFormChange(
|
||||
@@ -620,6 +638,7 @@ const HbStatisticsVerification = () => {
|
||||
)
|
||||
}
|
||||
className="flex-1"
|
||||
placeholder="(0 ~ 100, 1 단위)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -771,6 +790,4 @@ const HbStatisticsVerification = () => {
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HbStatisticsVerification;
|
||||
}
|
||||
|
||||
@@ -24,25 +24,21 @@ import dayjs from "dayjs";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { CodeSelect } from "@/entities/code";
|
||||
import type {
|
||||
AdstZoneResponseDto,
|
||||
StAnlsMstResponse,
|
||||
StAnlsMstSearchParams,
|
||||
StAnlsRsltResponse,
|
||||
} from "@/entities/statistics";
|
||||
import {
|
||||
type AdstZoneResponseDto,
|
||||
IMR_DETAIL_VISIBLE_COLUMNS_BY_STATS_CD,
|
||||
IMR_DETAIL_VISIBLE_COLUMNS_DEFAULT,
|
||||
imrApi,
|
||||
imrDetailGridColumnInfo,
|
||||
imrVerificationHistoryColumns,
|
||||
} from "@/entities/statistics";
|
||||
import { imrApi } from "@/entities/statistics/api/imr-api";
|
||||
import {
|
||||
type StAnlsMstResponse,
|
||||
type StAnlsMstSearchParams,
|
||||
stAnlsRsltApi,
|
||||
type StAnlsRsltResponse,
|
||||
statisticsAnalysisMasterService,
|
||||
useStAnlsSidoCombo,
|
||||
useStAnlsSigunguCombo,
|
||||
} from "@/entities/statistics/api/queries";
|
||||
import { stAnlsMstApi } from "@/entities/statistics/api/statistics-analysis-master-api";
|
||||
import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api";
|
||||
} from "@/entities/statistics";
|
||||
import {
|
||||
Button,
|
||||
DataTable,
|
||||
@@ -144,7 +140,7 @@ const ImrVerificationHistory = () => {
|
||||
historyPagination,
|
||||
],
|
||||
queryFn: () =>
|
||||
stAnlsMstApi.getImrAnalysisMasterList({
|
||||
statisticsAnalysisMasterService.getImrAnalysisMasterList({
|
||||
...appliedSearchParams,
|
||||
page: historyPagination.pageIndex,
|
||||
size: historyPagination.pageSize,
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Search } from "lucide-react";
|
||||
|
||||
import { CodeSelect } from "@/entities/code/ui/code-select";
|
||||
import { verificationTargetManagementApi } from "@/entities/statistics/api/verification-target-management-service";
|
||||
@@ -368,7 +367,7 @@ export function VerificationTargetDialog({
|
||||
type="number"
|
||||
min="0"
|
||||
max="0.5"
|
||||
step="0.1"
|
||||
step="0.01"
|
||||
value={
|
||||
field.value ??
|
||||
""
|
||||
@@ -385,7 +384,7 @@ export function VerificationTargetDialog({
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 0.5, 0.1 단위)"
|
||||
placeholder="(0 ~ 0.5, 0.01 단위)"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import type { StAnlsDtlResponse } from "@/entities/statistics";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import type { AxiosError } from "axios";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useAlertStore, useMutationWrapper } from "@/shared/lib";
|
||||
import type { ApiErrorResponse } from "@/shared/model";
|
||||
import type {
|
||||
StAnlsDtlConfirmRequest,
|
||||
StAnlsDtlConfirmResponse,
|
||||
StAnlsDtlResponse,
|
||||
} from "@/entities/statistics";
|
||||
import { statisticsAnalysisDetailService } from "@/entities/statistics";
|
||||
import { Button } from "@common/components";
|
||||
|
||||
import Indicators from "./Indicators";
|
||||
@@ -38,6 +50,47 @@ const HbVerificationDetail: React.FC<HbVerificationDetailProps> = ({
|
||||
vrfcTrgtNm: string;
|
||||
} | null>(null);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const { confirm } = useConfirm();
|
||||
|
||||
const confirmMutation = useMutationWrapper<
|
||||
StAnlsDtlConfirmResponse,
|
||||
AxiosError<ApiErrorResponse>,
|
||||
StAnlsDtlConfirmRequest
|
||||
>({
|
||||
mutationFn: (payload) =>
|
||||
statisticsAnalysisDetailService.confirmDetail(payload),
|
||||
onSuccess: async () => {
|
||||
setCheckedSns([]);
|
||||
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [
|
||||
ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_DTL,
|
||||
"list",
|
||||
anlsSn,
|
||||
],
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST],
|
||||
}),
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
const handleConfirmClick = async () => {
|
||||
if (checkedSns.length === 0) return;
|
||||
|
||||
if (!(await confirm("검증확정 처리하시겠습니까?"))) return;
|
||||
|
||||
const vrfcTrgtSnList = checkedSns
|
||||
.map((value) => Number(value))
|
||||
.filter((value) => !Number.isNaN(value));
|
||||
|
||||
confirmMutation.mutate({ anlsSn, vrfcTrgtSnList });
|
||||
};
|
||||
|
||||
const handleTargetSelect = (row: StAnlsDtlResponse) => {
|
||||
setSelectedVrfcTrgtSn(String(row.vrfcTrgtSn));
|
||||
setSelectedTargetInfo({
|
||||
@@ -70,7 +123,14 @@ const HbVerificationDetail: React.FC<HbVerificationDetailProps> = ({
|
||||
검증 상세
|
||||
</h2>
|
||||
</div>
|
||||
<Button disabled={checkedSns.length === 0}>검증확정</Button>
|
||||
<Button
|
||||
disabled={
|
||||
checkedSns.length === 0 || confirmMutation.isPending
|
||||
}
|
||||
onClick={handleConfirmClick}
|
||||
>
|
||||
검증확정
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { StAnlsDtlResponse } from "@/entities/statistics";
|
||||
import { getVerificationListColumns } from "@/entities/statistics";
|
||||
import { stAnlsDtlApi } from "@/entities/statistics/api/statistics-analysis-detail-api";
|
||||
import { statisticsAnalysisDetailService } from "@/entities/statistics/api/statistics-analysis-detail-service";
|
||||
import { DataTable } from "@common/components";
|
||||
|
||||
interface VerificationListProps {
|
||||
@@ -27,7 +27,11 @@ const VerificationList: React.FC<VerificationListProps> = ({
|
||||
const { data: detailData, isLoading } = useQuery({
|
||||
queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_DTL, "list", anlsSn],
|
||||
queryFn: () =>
|
||||
stAnlsDtlApi.getDetailList({ anlsSn, page: 0, size: 100 }),
|
||||
statisticsAnalysisDetailService.getDetailList({
|
||||
anlsSn,
|
||||
page: 0,
|
||||
size: 100,
|
||||
}),
|
||||
enabled: !!anlsSn,
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import { ENDPOINT } from "@/shared/config";
|
||||
import type { PageableList } from "@/shared/model";
|
||||
|
||||
import type {
|
||||
BusinessSendRequest,
|
||||
BusinessSendResponse,
|
||||
NtftalkTemplate,
|
||||
TemplateVariablesResponse,
|
||||
} from "../../model/notice-talk/notice-talk-sending-interface";
|
||||
@@ -38,6 +40,16 @@ export class NoticeTalkSendingApiService {
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async sendBusiness(
|
||||
payload: BusinessSendRequest,
|
||||
): Promise<BusinessSendResponse> {
|
||||
const response = await client.post<BusinessSendResponse>(
|
||||
`${ENDPOINT.NTFTALK_SERVICE.ATA_SEND}/business`,
|
||||
payload,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const noticeTalkSendingApi = new NoticeTalkSendingApiService();
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { generalverificationApiService } from "@/entities/statistics/api/general-verification-service";
|
||||
import { StatisticsInputStatusApi } from "@/entities/statistics/api/input/statistics-input-status-service";
|
||||
import type { VerifyResultPageData } from "@/entities/statistics/model/general-verification-interface";
|
||||
|
||||
import type { VerifyResultPageData } from "../model/general-verification-interface";
|
||||
import type { SewerStUserSearchParams } from "../model/sewer-st-user-interface";
|
||||
import type {
|
||||
StatisticsVerificationFilterState,
|
||||
StatisticsVerificationSubFilterState,
|
||||
} from "@/entities/statistics/model/statistics-verification-report/statistics-verification-report-types";
|
||||
|
||||
import type { SewerStUserSearchParams } from "../model/sewer-st-user-interface";
|
||||
} from "../model/statistics-verification-report/statistics-verification-report-types";
|
||||
|
||||
import { StatisticsInputStatusApi } from "./input/statistics-input-status-service";
|
||||
import { generalverificationApiService } from "./general-verification-service";
|
||||
import { sewerStUserApi } from "./sewer-st-user-service";
|
||||
import { stAnlsMstApi } from "./statistics-analysis-master-api";
|
||||
import { statisticsAnalysisMasterService } from "./statistics-analysis-master-service";
|
||||
import { stAnlsRsltApi } from "./statistics-analysis-result-api";
|
||||
|
||||
const DETAIL_PAGE_SIZE = 100;
|
||||
@@ -20,14 +20,14 @@ const DETAIL_PAGE_SIZE = 100;
|
||||
export const useStAnlsSidoCombo = () => {
|
||||
return useQuery({
|
||||
queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST, "sido"],
|
||||
queryFn: () => stAnlsMstApi.getSidoCombo(),
|
||||
queryFn: () => statisticsAnalysisMasterService.getSidoCombo(),
|
||||
});
|
||||
};
|
||||
|
||||
export const useStAnlsSigunguCombo = (sidoCd: string) => {
|
||||
return useQuery({
|
||||
queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST, "sigungu", sidoCd],
|
||||
queryFn: () => stAnlsMstApi.getSigunguCombo(sidoCd),
|
||||
queryFn: () => statisticsAnalysisMasterService.getSigunguCombo(sidoCd),
|
||||
enabled: Boolean(sidoCd),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
StAnlsDtlListApiResponse,
|
||||
StAnlsDtlResponse,
|
||||
StAnlsDtlSearchParams,
|
||||
} from "../model/statistics-analysis/statistics-analysis-detail-interface";
|
||||
|
||||
export class StAnlsDtlApiService {
|
||||
public async getDetailList(
|
||||
params: StAnlsDtlSearchParams,
|
||||
): Promise<StAnlsDtlListApiResponse> {
|
||||
const response = await client.get<StAnlsDtlListApiResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_DTL}`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getDetail(
|
||||
anlsSn: number,
|
||||
vrfcTrgtSn: number,
|
||||
): Promise<StAnlsDtlResponse> {
|
||||
const response = await client.get<StAnlsDtlResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_DTL}/${anlsSn}/${vrfcTrgtSn}`,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const stAnlsDtlApi = new StAnlsDtlApiService();
|
||||
@@ -0,0 +1,51 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
StAnlsDtlConfirmRequest,
|
||||
StAnlsDtlConfirmResponse,
|
||||
StAnlsDtlListApiResponse,
|
||||
StAnlsDtlResponse,
|
||||
StAnlsDtlSearchParams,
|
||||
} from "../model/statistics-analysis/statistics-analysis-detail-interface";
|
||||
|
||||
async function getDetailList(
|
||||
params: StAnlsDtlSearchParams,
|
||||
): Promise<StAnlsDtlListApiResponse> {
|
||||
const response = await client.get<StAnlsDtlListApiResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_DTL}`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getDetail(
|
||||
anlsSn: number,
|
||||
vrfcTrgtSn: number,
|
||||
): Promise<StAnlsDtlResponse> {
|
||||
const response = await client.get<StAnlsDtlResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_DTL}/${anlsSn}/${vrfcTrgtSn}`,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function confirmDetail(
|
||||
request: StAnlsDtlConfirmRequest,
|
||||
): Promise<StAnlsDtlConfirmResponse> {
|
||||
const response = await client.post<StAnlsDtlConfirmResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_DTL}/confirm`,
|
||||
request,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const statisticsAnalysisDetailService = {
|
||||
getDetailList,
|
||||
getDetail,
|
||||
confirmDetail,
|
||||
};
|
||||
@@ -1,69 +0,0 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
AdstZoneResponseDto,
|
||||
StAnlsMstImrListApiResponse,
|
||||
StAnlsMstListApiResponse,
|
||||
StAnlsMstSearchParams,
|
||||
StAnlsOutlierListResponse,
|
||||
} from "../model/statistics-analysis/statistics-analysis-master-interface";
|
||||
|
||||
export class StAnlsMstApiService {
|
||||
public async getAnalysisHistoryList(
|
||||
params: StAnlsMstSearchParams,
|
||||
): Promise<StAnlsMstListApiResponse> {
|
||||
const response = await client.get<StAnlsMstListApiResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getImrAnalysisMasterList(
|
||||
params: StAnlsMstSearchParams,
|
||||
): Promise<StAnlsMstImrListApiResponse> {
|
||||
const response = await client.get<StAnlsMstImrListApiResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/imr`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getOutlierList(
|
||||
anlsSn: number,
|
||||
): Promise<StAnlsOutlierListResponse[]> {
|
||||
const response = await client.get<StAnlsOutlierListResponse[]>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/outlierList/${anlsSn}`,
|
||||
);
|
||||
|
||||
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 stAnlsMstApi = new StAnlsMstApiService();
|
||||
@@ -0,0 +1,71 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
AdstZoneResponseDto,
|
||||
StAnlsMstImrListApiResponse,
|
||||
StAnlsMstListApiResponse,
|
||||
StAnlsMstSearchParams,
|
||||
StAnlsOutlierListResponse,
|
||||
} from "../model/statistics-analysis/statistics-analysis-master-interface";
|
||||
|
||||
async function getAnalysisHistoryList(
|
||||
params: StAnlsMstSearchParams,
|
||||
): Promise<StAnlsMstListApiResponse> {
|
||||
const response = await client.get<StAnlsMstListApiResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getImrAnalysisMasterList(
|
||||
params: StAnlsMstSearchParams,
|
||||
): Promise<StAnlsMstImrListApiResponse> {
|
||||
const response = await client.get<StAnlsMstImrListApiResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/imr`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getOutlierList(
|
||||
anlsSn: number,
|
||||
): Promise<StAnlsOutlierListResponse[]> {
|
||||
const response = await client.get<StAnlsOutlierListResponse[]>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/outlierList/${anlsSn}`,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getSidoCombo(): Promise<AdstZoneResponseDto[]> {
|
||||
const response = await client.get<AdstZoneResponseDto[]>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/sido`,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function 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 statisticsAnalysisMasterService = {
|
||||
getAnalysisHistoryList,
|
||||
getImrAnalysisMasterList,
|
||||
getOutlierList,
|
||||
getSidoCombo,
|
||||
getSigunguCombo,
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
export { analysisApi } from "./api/analysis-api";
|
||||
export {
|
||||
useDashboardEcptReqCount,
|
||||
useDashboardStatsSubmissionRate,
|
||||
@@ -8,6 +9,7 @@ export {
|
||||
export { dashboardApi } from "./api/dashboard/dashboard-service";
|
||||
export { useScheduleInfo } from "./api/dashboard/schedule-info-queries";
|
||||
export { scheduleInfoApi } from "./api/dashboard/schedule-info-service";
|
||||
export { imrApi } from "./api/imr-api";
|
||||
export { noticeTalkSendingApi } from "./api/notice-talk/notice-talk-sending-service";
|
||||
export {
|
||||
useNtftalkTemplateList,
|
||||
@@ -21,7 +23,11 @@ export {
|
||||
useStatsVrfcRsltData,
|
||||
} from "./api/queries";
|
||||
export { sewerStUserApi } from "./api/sewer-st-user-service";
|
||||
export { statisticsAnalysisDetailService } from "./api/statistics-analysis-detail-service";
|
||||
export { statisticsAnalysisMasterService } from "./api/statistics-analysis-master-service";
|
||||
export { stAnlsRsltApi } from "./api/statistics-analysis-result-api";
|
||||
export { statisticsApprovalApi } from "./api/statistics-approval-service";
|
||||
export { verificationTargetManagementApi } from "./api/verification-target-management-service";
|
||||
export {
|
||||
type AnalysisHistoryListApiResponse,
|
||||
type AnalysisRequestPayload,
|
||||
@@ -83,6 +89,8 @@ export {
|
||||
type SewerStUserSearchParams,
|
||||
} from "./model/sewer-st-user-interface";
|
||||
export {
|
||||
type StAnlsDtlConfirmRequest,
|
||||
type StAnlsDtlConfirmResponse,
|
||||
type StAnlsDtlListApiResponse,
|
||||
type StAnlsDtlResponse,
|
||||
type StAnlsDtlSearchParams,
|
||||
@@ -134,3 +142,4 @@ export {
|
||||
type IMRArticleItem,
|
||||
type ValidationArticleItem,
|
||||
} from "./model/verification-article/verification-article-interface";
|
||||
export { type VerificationTargetCodeResponse } from "./model/verification-target-management-interface";
|
||||
|
||||
+12
@@ -18,6 +18,8 @@ export interface StAnlsDtlResponse {
|
||||
nmlDataNocs: number; // 정상 데이터 건수
|
||||
exclDataNocs: number; // 제외 데이터 건수
|
||||
otlrDataNocs: number; // 이상치 데이터 건수
|
||||
cfmtnYn: string; // 확정여부 (Y/N)
|
||||
cfmtnDt: string | null; // 확정일자 (yyyy-MM-dd HH:mm:ss)
|
||||
statsCd: string; // 통계 코드
|
||||
statsNm: string; // 통계 항목 명
|
||||
vrfcTrgtNm: string; // 검증대상명
|
||||
@@ -30,3 +32,13 @@ export interface StAnlsDtlSearchParams extends PageableParams {
|
||||
}
|
||||
|
||||
export type StAnlsDtlListApiResponse = PageableList<StAnlsDtlResponse>;
|
||||
|
||||
export interface StAnlsDtlConfirmRequest {
|
||||
anlsSn: number; // 분석 일련번호
|
||||
vrfcTrgtSnList: number[]; // 검증 대상 일련번호 목록
|
||||
}
|
||||
|
||||
export interface StAnlsDtlConfirmResponse {
|
||||
anlsSn: number; // 분석 일련번호
|
||||
processedCount: number; // 처리된 검증 대상 건수
|
||||
}
|
||||
|
||||
+3
-2
@@ -45,13 +45,15 @@ export interface StAnlsMstResponseDto {
|
||||
/** 검증대상명 */
|
||||
vrfcTrgtNm: string;
|
||||
/** 분석진행비율 */
|
||||
anlsPrgrsRt: number;
|
||||
anlsPrgrsRt: number | null;
|
||||
/** 하수도사업자코드 */
|
||||
sewerBzmnCd: string;
|
||||
/** 통계코드 */
|
||||
statsCd: string;
|
||||
/** 검증방법코드 */
|
||||
vrfcMthdCd: string;
|
||||
/** 확정건수/총건수 (예: 3/10) */
|
||||
cfmtnYnCntTotalCnt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +134,6 @@ export interface StAnlsOutlierListResponseDto {
|
||||
outlierDataList: StAnlsOutlierDataResponseDto[];
|
||||
}
|
||||
|
||||
/** 기존 인터페이스 호환을 위한 별칭 (필요 시 유지) */
|
||||
export type StAnlsMstResponse = StAnlsMstResponseDto;
|
||||
export type StAnlsMstImrResponse = StAnlsMstImrResponseDto;
|
||||
export type StAnlsMstSearchParams = StAnlsMstSearchDto;
|
||||
|
||||
+6
@@ -77,6 +77,12 @@ export const verificationHistoryColumns = [
|
||||
<div className="px-2 py-4">{info.getValue() ?? "-"}</div>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("cfmtnYnCntTotalCnt", {
|
||||
header: "확정건수/총",
|
||||
cell: (info) => (
|
||||
<div className="px-2 py-4">{info.getValue() ?? "-"}</div>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("nmlDataNocs", {
|
||||
header: "정상건수",
|
||||
cell: (info) => (
|
||||
|
||||
+117
-87
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
import type { ColumnDef } from "@tanstack/react-table";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { Checkbox } from "@common/components";
|
||||
|
||||
@@ -16,94 +17,123 @@ export const getVerificationListColumns = ({
|
||||
rowData,
|
||||
checkedSns,
|
||||
onCheck,
|
||||
}: GetVerificationListColumnsParams): ColumnDef<StAnlsDtlResponse>[] => [
|
||||
{
|
||||
id: "checkbox",
|
||||
header: () => {
|
||||
const isAllChecked =
|
||||
rowData.length > 0 && checkedSns.length === rowData.length;
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<Checkbox
|
||||
checked={isAllChecked}
|
||||
onCheckedChange={(value: boolean) => {
|
||||
if (value) {
|
||||
onCheck(
|
||||
rowData.map((r) => String(r.vrfcTrgtSn)),
|
||||
rowData,
|
||||
}: GetVerificationListColumnsParams): ColumnDef<StAnlsDtlResponse>[] => {
|
||||
const selectableRows = rowData.filter((r) => r.cfmtnYn !== "Y");
|
||||
|
||||
return [
|
||||
{
|
||||
id: "checkbox",
|
||||
header: () => {
|
||||
const isAllChecked =
|
||||
selectableRows.length > 0 &&
|
||||
checkedSns.length === selectableRows.length;
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<Checkbox
|
||||
checked={isAllChecked}
|
||||
disabled={selectableRows.length === 0}
|
||||
onCheckedChange={(value: boolean) => {
|
||||
if (value) {
|
||||
onCheck(
|
||||
selectableRows.map((r) =>
|
||||
String(r.vrfcTrgtSn),
|
||||
),
|
||||
selectableRows,
|
||||
);
|
||||
} else {
|
||||
onCheck([], []);
|
||||
}
|
||||
}}
|
||||
aria-label="Select all rows"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
if (row.original.cfmtnYn === "Y") {
|
||||
return <div className="flex items-center justify-center" />;
|
||||
}
|
||||
const rowId = String(row.original.vrfcTrgtSn);
|
||||
const isChecked = checkedSns.includes(rowId);
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<Checkbox
|
||||
checked={isChecked}
|
||||
onCheckedChange={(value: boolean) => {
|
||||
const newCheckedSns = value
|
||||
? [...checkedSns, rowId]
|
||||
: checkedSns.filter((id) => id !== rowId);
|
||||
const newCheckedRows = selectableRows.filter(
|
||||
(r) =>
|
||||
newCheckedSns.includes(
|
||||
String(r.vrfcTrgtSn),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
onCheck([], []);
|
||||
onCheck(newCheckedSns, newCheckedRows);
|
||||
}}
|
||||
onClick={(e: React.MouseEvent) =>
|
||||
e.stopPropagation()
|
||||
}
|
||||
}}
|
||||
aria-label="Select all rows"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
aria-label={`Select row ${rowId}`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const rowId = String(row.original.vrfcTrgtSn);
|
||||
const isChecked = checkedSns.includes(rowId);
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<Checkbox
|
||||
checked={isChecked}
|
||||
onCheckedChange={(value: boolean) => {
|
||||
const newCheckedSns = value
|
||||
? [...checkedSns, rowId]
|
||||
: checkedSns.filter((id) => id !== rowId);
|
||||
const newCheckedRows = rowData.filter((r) =>
|
||||
newCheckedSns.includes(String(r.vrfcTrgtSn)),
|
||||
);
|
||||
onCheck(newCheckedSns, newCheckedRows);
|
||||
}}
|
||||
onClick={(e: React.MouseEvent) => e.stopPropagation()}
|
||||
aria-label={`Select row ${rowId}`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
{
|
||||
header: "통계 년도",
|
||||
accessorKey: "statsYr",
|
||||
},
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
header: "통계 년도",
|
||||
accessorKey: "statsYr",
|
||||
},
|
||||
{
|
||||
header: "통계 항목",
|
||||
accessorKey: "statsNm",
|
||||
},
|
||||
{
|
||||
header: "검증 항목",
|
||||
accessorKey: "vrfcTrgtNm",
|
||||
},
|
||||
{
|
||||
header: "μ",
|
||||
accessorKey: "ipcrCntVl",
|
||||
},
|
||||
{
|
||||
header: "Percentile",
|
||||
accessorKey: "pctlCrtrVl",
|
||||
},
|
||||
{
|
||||
header: "A",
|
||||
accessorKey: "scopAjmtCntVl",
|
||||
},
|
||||
{
|
||||
header: "C",
|
||||
accessorKey: "thrsCntVl",
|
||||
},
|
||||
{
|
||||
header: "정상건수",
|
||||
accessorKey: "nmlDataNocs",
|
||||
},
|
||||
{
|
||||
header: "제외건수",
|
||||
accessorKey: "exclDataNocs",
|
||||
},
|
||||
{
|
||||
header: "이상건수",
|
||||
accessorKey: "otlrDataNocs",
|
||||
},
|
||||
];
|
||||
{
|
||||
header: "통계 항목",
|
||||
accessorKey: "statsNm",
|
||||
},
|
||||
{
|
||||
header: "검증 항목",
|
||||
accessorKey: "vrfcTrgtNm",
|
||||
},
|
||||
{
|
||||
header: "확정일자",
|
||||
accessorKey: "cfmtnDt",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="text-center">
|
||||
{row.original.cfmtnDt
|
||||
? dayjs(row.original.cfmtnDt).format("YYYY-MM-DD")
|
||||
: "-"}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "μ",
|
||||
accessorKey: "ipcrCntVl",
|
||||
},
|
||||
{
|
||||
header: "Percentile",
|
||||
accessorKey: "pctlCrtrVl",
|
||||
},
|
||||
{
|
||||
header: "A",
|
||||
accessorKey: "scopAjmtCntVl",
|
||||
},
|
||||
{
|
||||
header: "C",
|
||||
accessorKey: "thrsCntVl",
|
||||
},
|
||||
{
|
||||
header: "정상건수",
|
||||
accessorKey: "nmlDataNocs",
|
||||
},
|
||||
{
|
||||
header: "제외건수",
|
||||
accessorKey: "exclDataNocs",
|
||||
},
|
||||
{
|
||||
header: "이상건수",
|
||||
accessorKey: "otlrDataNocs",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
import { AxiosError } from "axios";
|
||||
import { useMutation, type UseMutationOptions } from "@tanstack/react-query";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import { useAlertStore } from "./use-alert-store";
|
||||
|
||||
export const useMutationWrapper = <
|
||||
TData = unknown,
|
||||
TError = unknown,
|
||||
TVariables = void,
|
||||
TContext = unknown,
|
||||
TData = unknown,
|
||||
TError = unknown,
|
||||
TVariables = void,
|
||||
TContext = unknown,
|
||||
>(
|
||||
mutationOptions: UseMutationOptions<TData, TError, TVariables, TContext>,
|
||||
mutationOptions: UseMutationOptions<TData, TError, TVariables, TContext>,
|
||||
) => {
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
return useMutation({
|
||||
...mutationOptions,
|
||||
onError: (error) => {
|
||||
let errorMessage = "";
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
return useMutation({
|
||||
...mutationOptions,
|
||||
onError: (error) => {
|
||||
let errorMessage = "";
|
||||
|
||||
if (error instanceof AxiosError) {
|
||||
if (error.response?.data?.message)
|
||||
errorMessage += `${error.response.data.message}\n`;
|
||||
if (error.response?.data?.errors[0]?.message)
|
||||
errorMessage += `${error.response.data.errors[0].message}\n`;
|
||||
}
|
||||
if (error instanceof AxiosError) {
|
||||
if (error.response?.data?.message)
|
||||
errorMessage += `${error.response.data.message}\n`;
|
||||
if (error.response?.data?.errors[0]?.message)
|
||||
errorMessage += `${error.response.data.errors[0].message}\n`;
|
||||
}
|
||||
|
||||
alert(`${errorMessage}`);
|
||||
},
|
||||
});
|
||||
alert(`${errorMessage}`);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user