Merge remote-tracking branch 'origin/iipbs/st/dev' into feature/review-skeleton-sein
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import useSWR from 'swr';
|
||||
|
||||
import {
|
||||
@@ -18,9 +18,9 @@ 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, Sido, Sgg } from "@/entities/statistics/model/statistics-interface";
|
||||
import type { ApiResponse, SearchCondition, Sgg } from "@/entities/statistics/model/statistics-interface";
|
||||
|
||||
const getSidoListFetcher = async (): Promise<ApiResponse<Sido[]>> => {
|
||||
const getSidoListFetcher = async (): Promise<ApiResponse<SearchCondition>> => {
|
||||
return statisticsAPi.getSidoList();
|
||||
};
|
||||
|
||||
@@ -34,20 +34,8 @@ const SearchForm = (props: SearchFormProps = {}) => {
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const defaultYear = currentYear - 1;
|
||||
const startYear = currentYear - 10;
|
||||
|
||||
const yearOptions = Array.from({ length: currentYear - startYear + 1 }, (_, i) => currentYear - i);
|
||||
|
||||
const [values, setValues] = useState<SearchFormValues>({
|
||||
sido: props.defaultValues?.sido || "",
|
||||
gugun: props.defaultValues?.gugun || '',
|
||||
year: props.defaultValues?.year || String(defaultYear),
|
||||
order: props.defaultValues?.order || '',
|
||||
fctName: props.defaultValues?.fctName || '',
|
||||
bsnmCd: props.defaultValues?.bsnmCd || '',
|
||||
});
|
||||
|
||||
const { data: sidoList } = useSWR<ApiResponse<Sido[]>>(
|
||||
const { data: sidoList } = useSWR<ApiResponse<SearchCondition>>(
|
||||
"sido-list",
|
||||
getSidoListFetcher,
|
||||
{
|
||||
@@ -69,6 +57,15 @@ const SearchForm = (props: SearchFormProps = {}) => {
|
||||
}
|
||||
);
|
||||
|
||||
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 { data: sggList } = useSWR<ApiResponse<Sgg[]>>(
|
||||
values.sido && values.sido !== "ALL" ? ["sgg-list", values.sido] : null,
|
||||
getSggListFetcher,
|
||||
@@ -111,7 +108,7 @@ const SearchForm = (props: SearchFormProps = {}) => {
|
||||
};
|
||||
|
||||
const handleSidoChange = (value: string) => {
|
||||
const selectedBsnm = sidoList?.data.find((v) => v.sido === value)?.bsnmCd || '';
|
||||
const selectedBsnm = sidoList?.data.sidoList.find((v) => v.sido === value)?.bsnmCd || '';
|
||||
|
||||
setValues((prev) => ({
|
||||
...prev,
|
||||
@@ -121,6 +118,10 @@ const SearchForm = (props: SearchFormProps = {}) => {
|
||||
}));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
props.onValuesChange?.(values);
|
||||
}, [values]);
|
||||
|
||||
return (
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit}>
|
||||
@@ -136,7 +137,7 @@ const SearchForm = (props: SearchFormProps = {}) => {
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ALL">선택</SelectItem>
|
||||
{sidoList?.data?.map((sido) => (
|
||||
{sidoList?.data?.sidoList?.map((sido) => (
|
||||
<SelectItem key={sido.adstDstrCd} value={sido.sido}>
|
||||
{sido.sido}
|
||||
</SelectItem>
|
||||
@@ -175,7 +176,7 @@ const SearchForm = (props: SearchFormProps = {}) => {
|
||||
<SelectValue placeholder="선택"></SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{yearOptions.map((year) => (
|
||||
{sidoList?.data?.yearList.map((year) => (
|
||||
<SelectItem key={year} value={String(year)}>
|
||||
{year}년
|
||||
</SelectItem>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
|
||||
import SearchForm from "@/components/module/SearchForm";
|
||||
// import { DataGrid } from "@rsone/components";
|
||||
@@ -12,47 +12,68 @@ import { LoadingWithAnimation } from "@common/components";
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { statisticsInputAPi } from "@/entities/statistics/api/statistics-input-service";
|
||||
import type { SearchFormValues } from "@/components/type/SearchForm.type";
|
||||
import type { SewerageSupplyRateItem } from "@/entities/statistics/model/statistics-input-interface";
|
||||
import type {
|
||||
SewerageSupplyRateItem,
|
||||
SewerageSupplyRateRequestParams,
|
||||
} from "@/entities/statistics/model/statistics-input-interface";
|
||||
import { SeweragePenetrationRateInpColumn } from "@/components/util/column/input/SeweragePenetrationRateInpColumn";
|
||||
import { toGridRows } from "@/components/util/grid/gridFunction";
|
||||
|
||||
function getSewerageSupplyRateParams(
|
||||
values: SearchFormValues,
|
||||
overrides?: { yr?: string; demoYr?: string; mode?: string }
|
||||
): SewerageSupplyRateRequestParams {
|
||||
return {
|
||||
sido: values.sido,
|
||||
gugun: values.gugun === 'ALL' ? '' : values.gugun,
|
||||
bsnmCd: values.bsnmCd,
|
||||
yr: overrides?.yr ?? values.year,
|
||||
demoYr: overrides?.demoYr ?? values.year,
|
||||
...(overrides?.mode && { mode: overrides.mode }),
|
||||
};
|
||||
}
|
||||
|
||||
// 하수도 통계 관리 > 01. 하수도 보급률
|
||||
const SeweragePenetrationRateInp = () => {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [searchValues, setSearchValues] = useState<SearchFormValues | null>(null);
|
||||
const [isEnterable, setIsEnterable] = useState<boolean>(true);
|
||||
const [lastSearchedYear, setLastSearchedYear] = useState<string | null>(null);
|
||||
|
||||
|
||||
// 최상위 관리자일땐 모든 상황에서 edit 가능하게 수정 필요.
|
||||
const columnInfo = useMemo(() => {
|
||||
const { columns, columnLayout } = SeweragePenetrationRateInpColumn;
|
||||
return {
|
||||
columnLayout,
|
||||
columns: isEnterable
|
||||
? columns
|
||||
: columns.map((col) => ({ ...col, editable: false as const })),
|
||||
};
|
||||
}, [isEnterable]);
|
||||
|
||||
const handleReset = () => {
|
||||
setRowData([]);
|
||||
setLastSearchedYear(null);
|
||||
};
|
||||
|
||||
const handleSearch = async (values: SearchFormValues) => {
|
||||
const fetchSewerageSupplyRate = async (params: SewerageSupplyRateRequestParams) => {
|
||||
try {
|
||||
if (values.sido !== '' && values.sido !== 'ALL') {
|
||||
setIsLoading(true);
|
||||
setIsLoading(true);
|
||||
const response = await statisticsInputAPi.getSewerageSupplyRateList(params);
|
||||
if (response.status === 'success' && response.data) {
|
||||
const convertedData: SewerageSupplyRateItem[] = response.data.sewerageSupplyRateList.map((d) => ({
|
||||
...d,
|
||||
speclCntrplnAr: d.speclCntrplnAr === '' || d.speclCntrplnAr === null ? '해당없음' : d.speclCntrplnAr,
|
||||
}));
|
||||
|
||||
const response = await statisticsInputAPi.getSewerageSupplyRateList({
|
||||
sido: values.sido,
|
||||
gugun: values.gugun === 'ALL' ? '' : values.gugun,
|
||||
bsnmCd: values.bsnmCd,
|
||||
yr: values.year,
|
||||
demoYr: values.year,
|
||||
});
|
||||
|
||||
if (response.status === 'success' && response.data) {
|
||||
const convertedData: SewerageSupplyRateItem[] = response.data.sewerageSupplyRateList.map((d) => ({
|
||||
...d,
|
||||
speclCntrplnAr: d.speclCntrplnAr === '' || d.speclCntrplnAr === null ? '해당없음' : d.speclCntrplnAr,
|
||||
}));
|
||||
|
||||
setRowData(toGridRows(convertedData));
|
||||
} else {
|
||||
alert(response.message);
|
||||
}
|
||||
setRowData(toGridRows(convertedData));
|
||||
} else {
|
||||
alert('시도를 선택해주세요.');
|
||||
alert(response.message);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
alert('데이터 조회 중 오류가 발생했습니다.');
|
||||
setRowData([]);
|
||||
} finally {
|
||||
@@ -60,12 +81,45 @@ const SeweragePenetrationRateInp = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = async (values: SearchFormValues) => {
|
||||
if (values.sido === '' || values.sido === 'ALL') {
|
||||
alert('시도를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await statisticsInputAPi.checkInputPeriod(values.year || '');
|
||||
|
||||
if (response.status === 'success') {
|
||||
setIsEnterable(response.data);
|
||||
setLastSearchedYear(values.year || null);
|
||||
|
||||
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values));
|
||||
} else {
|
||||
alert(response.message);
|
||||
}
|
||||
} catch {
|
||||
alert('데이터 조회 중 오류가 발생했습니다.');
|
||||
}
|
||||
};
|
||||
|
||||
const SubmitExceptionFunc = () => {
|
||||
// 예외 제출 로직
|
||||
};
|
||||
|
||||
const getLastYearDataFunc = () => {
|
||||
// 지난연도 데이터 가져오기
|
||||
const getLastYearDataFunc = async (values: SearchFormValues) => {
|
||||
if (values.sido === '' || values.sido === 'ALL') {
|
||||
alert('시도를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 실제 조회된 연도가 없으면 실행 불가
|
||||
if (!lastSearchedYear) {
|
||||
alert('먼저 데이터를 조회해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values, { mode: 'PRE' }));
|
||||
};
|
||||
|
||||
const submitFunc = () => {
|
||||
@@ -78,22 +132,29 @@ const SeweragePenetrationRateInp = () => {
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<SearchForm onSubmit={handleSearch} onReset={handleReset} />
|
||||
<div className="card !py-0">
|
||||
<div className="flex justify-end items-center gap-[0.6rem] mx-[0.6rem]">
|
||||
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
|
||||
<LastYearDataButton getLastYearDataFunc={getLastYearDataFunc} />
|
||||
<SubmitButton submitFunc={submitFunc} />
|
||||
<SaveButton saveFunc={saveFunc} />
|
||||
<SearchForm onSubmit={handleSearch} onReset={handleReset} onValuesChange={setSearchValues} />
|
||||
{rowData.length > 0 &&
|
||||
<div className="card !py-0">
|
||||
<div className="flex justify-end items-center gap-[0.6rem] mx-[0.6rem]">
|
||||
{isEnterable && <SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />}
|
||||
{isEnterable && <LastYearDataButton getLastYearDataFunc={() => getLastYearDataFunc(searchValues ?? {})} />}
|
||||
{isEnterable && <SubmitButton submitFunc={submitFunc} />}
|
||||
{
|
||||
// 최고 관리자일땐 저장 가능하게
|
||||
<SaveButton saveFunc={saveFunc} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{isLoading && <div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
|
||||
<div className="relative size-[5rem]">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>}
|
||||
<DataGrid
|
||||
columnInfo={SeweragePenetrationRateInpColumn}
|
||||
key={isEnterable ? 'enterable' : 'readonly'}
|
||||
columnInfo={columnInfo}
|
||||
isExcelImport={(rowData.length > 0) && isEnterable}
|
||||
rowStartNum={3}
|
||||
fixedColumns={1}
|
||||
rowData={rowData}
|
||||
|
||||
@@ -16,10 +16,11 @@ export type SearchFormProps = {
|
||||
userMenual?: boolean;
|
||||
onSubmit?: (values: SearchFormValues) => void;
|
||||
onReset?: () => void;
|
||||
onValuesChange?: (values: SearchFormValues) => void;
|
||||
defaultValues?: SearchFormValues;
|
||||
};
|
||||
|
||||
export const defaultSearchFormProps: Required<Omit<SearchFormProps, 'onSubmit' | 'onReset' | 'defaultValues'>> = {
|
||||
export const defaultSearchFormProps: Required<Omit<SearchFormProps, 'onSubmit' | 'onReset' | 'onValuesChange' | 'defaultValues'>> = {
|
||||
sido: true,
|
||||
gugun: true,
|
||||
year: true,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type StatisticsInputService,
|
||||
type SewerageSupplyRateRequestParams,
|
||||
type SewerageSupplyRateApiResponse,
|
||||
type CheckInputPeriodResponse,
|
||||
} from "../model/statistics-input-interface";
|
||||
|
||||
export class StatisticsInputAPiService implements StatisticsInputService {
|
||||
@@ -18,6 +19,14 @@ export class StatisticsInputAPiService implements StatisticsInputService {
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async checkInputPeriod(params: string): Promise<CheckInputPeriodResponse> {
|
||||
const response = await axios.get<CheckInputPeriodResponse> (
|
||||
`${ENDPOINT.ST_SERVICE.INPUTCOMMON}/check-input-period?year=${params}`,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const statisticsInputAPi = new StatisticsInputAPiService();
|
||||
@@ -3,16 +3,16 @@ import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import {
|
||||
type StatisticsService,
|
||||
type Sido,
|
||||
type SearchCondition,
|
||||
type ApiResponse,
|
||||
type Sgg
|
||||
} from "../model/statistics-interface";
|
||||
|
||||
export class StatisticsAPiService implements StatisticsService {
|
||||
|
||||
public async getSidoList(): Promise<ApiResponse<Sido[]>> {
|
||||
const response = await axios.get<ApiResponse<Sido[]>>(
|
||||
`${ENDPOINT.ST_SERVICE.COMMON}/sido`,
|
||||
public async getSidoList(): Promise<ApiResponse<SearchCondition>> {
|
||||
const response = await axios.get<ApiResponse<SearchCondition>>(
|
||||
`${ENDPOINT.ST_SERVICE.COMMON}/search-condition`,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ApiResponse } from "./statistics-interface";
|
||||
|
||||
// 하수도 보급률 API 응답의 상태 정보
|
||||
// 하수도 보급률 데이터 항목
|
||||
export interface SewerageSupplyRateStatusInfo {
|
||||
locgStatus: string;
|
||||
locgDt: string;
|
||||
@@ -13,7 +13,6 @@ export interface SewerageSupplyRateStatusInfo {
|
||||
returnOpinionSm: string | null;
|
||||
}
|
||||
|
||||
// 하수도 보급률 데이터 항목
|
||||
export interface SewerageSupplyRateItem {
|
||||
sido: string;
|
||||
gugun: string;
|
||||
@@ -103,7 +102,7 @@ export interface SewerageSupplyRateResponseData {
|
||||
|
||||
export type SewerageSupplyRateApiResponse = ApiResponse<SewerageSupplyRateResponseData> & {
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SewerageSupplyRateRequestParams {
|
||||
sido?: string;
|
||||
@@ -111,6 +110,14 @@ export interface SewerageSupplyRateRequestParams {
|
||||
bsnmCd?: string;
|
||||
yr?: string;
|
||||
demoYr?: string;
|
||||
mode?: string;
|
||||
}
|
||||
|
||||
// 하수도 통계 입력 기간 조회
|
||||
export interface CheckInputPeriodResponse {
|
||||
status: string;
|
||||
message: string;
|
||||
data: boolean;
|
||||
}
|
||||
|
||||
export interface StatisticsInputService {
|
||||
|
||||
@@ -68,6 +68,7 @@ export const ENDPOINT = {
|
||||
COMMON: "/st-management-service/api/v1/common",
|
||||
INPUT: "/st-input-service/api/v1/input",
|
||||
REVIEW: "/st-management-service/api/v1/review",
|
||||
INPUTCOMMON: "/st-input-service/api/v1/common",
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user