Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev

This commit is contained in:
2026-02-23 10:03:44 +09:00
11 changed files with 662 additions and 383 deletions
+1 -1
View File
@@ -25,4 +25,4 @@ apps/fo/.next/**/*
pnpm-lock.yaml
settings.json
.npmrc
.cursor
.cursor
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
BRANCH=$(git rev-parse --abbrev-ref HEAD)
pnpm turbo run build --filter="...[origin/$BRANCH]"
+16 -6
View File
@@ -6,16 +6,26 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{
rules: {
"react-hooks/exhaustive-deps": "off",
...compat.extends("next/core-web-vitals", "next/typescript"),
{
rules: {
"react-hooks/exhaustive-deps": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-wrapper-object-types": "warn",
},
},
},
];
export default eslintConfig;
@@ -5,35 +5,81 @@ import SearchForm from "@/components/module/SearchForm";
import type { ColumnConfig } from "@/components/type/DataGrid.type";
const StatisticsInputManagement = () => {
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
const [rowData, setRowData] = useState<Record<string, string | number>[]>(
[],
);
const columns: ColumnConfig[] = [
{ name: 'sido', fieldName: '시도', header: { text: '시도' }, mergeCells: true, editable: false, sortdisable: true },
{ name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true },
{ name: '구분', fieldName: '구분', header: { text: '구분' }, editable: false, sortdisable: true },
{ name: '광역자치단체', fieldName: '광역자치단체', header: { text: '광역자치단체' }, editable: false, sortdisable: true },
{ name: '공단', fieldName: '공단', header: { text: '공단' }, editable: false, sortdisable: true },
{ name: '처리내역', fieldName: '처리내역', header: { text: '처리내역' }, editable: false, sortdisable: true },
{
name: "sido",
fieldName: "시도",
header: { text: "시도" },
mergeCells: true,
editable: false,
sortdisable: true,
},
{
name: "sgg",
fieldName: "시군구",
header: { text: "시군구" },
mergeCells: true,
editable: false,
sortdisable: true,
},
{
name: "구분",
fieldName: "구분",
header: { text: "구분" },
editable: false,
sortdisable: true,
},
{
name: "광역자치단체",
fieldName: "광역자치단체",
header: { text: "광역자치단체" },
editable: false,
sortdisable: true,
},
{
name: "공단",
fieldName: "공단",
header: { text: "공단" },
editable: false,
sortdisable: true,
},
{
name: "처리내역",
fieldName: "처리내역",
header: { text: "처리내역" },
editable: false,
sortdisable: true,
},
];
const columnLayout = [
'sido',
'sgg',
'구분',
"sido",
"sgg",
"구분",
{
name: 'groupA',
name: "groupA",
hideChildHeaders: false,
header: { text: '검토내역' },
items: ['광역자치단체', '공단', '처리내역'],
header: { text: "검토내역" },
items: ["광역자치단체", "공단", "처리내역"],
},
];
return (
<>
<SearchForm sgg={false} sido={false} />
<DataGrid columnInfo={{ columns, columnLayout }} rowData={rowData} setRowData={setRowData} isExcelImport={false} isExcelExport={false} />
<SearchForm sido={false} />
<DataGrid
columnInfo={{ columns, columnLayout }}
rowData={rowData}
setRowData={setRowData}
isExcelImport={false}
isExcelExport={false}
/>
</>
);
};
export default StatisticsInputManagement;
export default StatisticsInputManagement;
@@ -3,9 +3,9 @@ import SearchForm from "@/components/module/SearchForm";
const StatisticsPopulationManagement = () => {
return (
<>
<SearchForm sgg={false} sido={false} />
<SearchForm sido={false} />
</>
);
};
export default StatisticsPopulationManagement;
export default StatisticsPopulationManagement;
@@ -37,7 +37,7 @@ const TAB_CONFIG = {
},
} as const;
type TabValue = keyof typeof TAB_CONFIG;
export type TabValue = keyof typeof TAB_CONFIG;
function VerificationArticleManagement() {
const queryClient = useQueryClient();
@@ -248,6 +248,7 @@ function VerificationArticleManagement() {
))}
<VerificationTargetDialog
tabValue={activeTab}
open={dialogOpen}
onOpenChange={setDialogOpen}
data={selectedData}
@@ -23,8 +23,10 @@ import {
import { useEffect } from "react";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import type { TabValue } from "./VerificationArticleManagement";
interface VerificationTargetDialogProps {
tabValue: TabValue;
open: boolean;
onOpenChange: (open: boolean) => void;
data: VerificationTargetManagementResponse | null;
@@ -33,6 +35,7 @@ interface VerificationTargetDialogProps {
}
export function VerificationTargetDialog({
tabValue,
open,
onOpenChange,
data,
@@ -51,7 +54,7 @@ export function VerificationTargetDialog({
useEffect(() => {
if (data && open) reset(data);
}, [data, open]);
}, [data, open, reset]);
function onSubmit(formData: VerificationTargetManagementFormData) {
if (!data) return;
@@ -100,11 +103,6 @@ export function VerificationTargetDialog({
}
placeholder="선택"
/>
{errors.statsCd && (
<p className="text-xs text-red-500">
{errors.statsCd.message}
</p>
)}
</div>
)}
/>
@@ -147,11 +145,6 @@ export function VerificationTargetDialog({
{...field}
placeholder="스키마명"
/>
{errors.schmNm && (
<p className="text-xs text-red-500">
{errors.schmNm.message}
</p>
)}
</div>
)}
/>
@@ -172,11 +165,6 @@ export function VerificationTargetDialog({
{...field}
placeholder="테이블명"
/>
{errors.tblNm && (
<p className="text-xs text-red-500">
{errors.tblNm.message}
</p>
)}
</div>
)}
/>
@@ -200,11 +188,6 @@ export function VerificationTargetDialog({
{...field}
placeholder="컬럼명"
/>
{errors.colNm && (
<p className="text-xs text-red-500">
{errors.colNm.message}
</p>
)}
</div>
)}
/>
@@ -225,132 +208,145 @@ export function VerificationTargetDialog({
{...field}
placeholder="PK 키"
/>
{errors.pkColNm && (
<p className="text-xs text-red-500">
{errors.pkColNm.message}
</p>
)}
</div>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead className="bg-muted font-medium">
중요도 계수(μ)
</TableHead>
<TableCell>
<Controller
name="ipcrCntVl"
control={control}
render={({ field }) => (
<div className="space-y-1">
<Input
type="number"
step="0.1"
value={field.value ?? ""}
onChange={(e) =>
field.onChange(
e.target.value
? Number(
e.target
.value,
)
: null,
)
}
placeholder="(0 ~ 1)"
/>
</div>
)}
/>
</TableCell>
<TableHead className="bg-muted font-medium">
백분위수 기준값(pct)
</TableHead>
<TableCell>
<Controller
name="pctlCrtrVl"
control={control}
render={({ field }) => (
<div className="space-y-1">
<Input
type="number"
step="0.1"
value={field.value ?? ""}
onChange={(e) =>
field.onChange(
e.target.value
? Number(
e.target
.value,
)
: null,
)
}
placeholder="(0 ~ 0.5)"
/>
</div>
)}
/>
</TableCell>
</TableRow>
{tabValue !== "IMR" && (
<TableRow>
<TableHead className="bg-muted font-medium">
중요도 계수(μ)
</TableHead>
<TableCell>
<Controller
name="ipcrCntVl"
control={control}
render={({ field }) => (
<div className="space-y-1">
<Input
type="number"
min="0"
max="1"
step="0.1"
value={field.value ?? ""}
onChange={(e) =>
field.onChange(
e.target.value
? Number(
e.target
.value,
)
: null,
)
}
placeholder="(0 ~ 1, 0.1 단위)"
/>
</div>
)}
/>
</TableCell>
<TableHead className="bg-muted font-medium">
백분위수 기준값(pct)
</TableHead>
<TableCell>
<Controller
name="pctlCrtrVl"
control={control}
render={({ field }) => (
<div className="space-y-1">
<Input
type="number"
min="0"
max="0.5"
step="0.1"
value={field.value ?? ""}
onChange={(e) =>
field.onChange(
e.target.value
? Number(
e.target
.value,
)
: null,
)
}
placeholder="(0 ~ 0.5, 0.1 단위)"
/>
</div>
)}
/>
</TableCell>
</TableRow>
)}
<TableRow>
<TableHead className="bg-muted font-medium">
스케일 조정 계수(A)
</TableHead>
<TableCell>
<Controller
name="scopCntCntVl"
control={control}
render={({ field }) => (
<Input
type="number"
step="0.05"
value={field.value ?? ""}
onChange={(e) =>
field.onChange(
e.target.value
? Number(
e.target
.value,
)
: null,
)
}
/>
)}
/>
</TableCell>
<TableHead className="bg-muted font-medium">
임계 계수(c)
</TableHead>
<TableCell>
<Controller
name="thrsCntVl"
control={control}
render={({ field }) => (
<Input
type="number"
step="1"
value={field.value ?? ""}
onChange={(e) =>
field.onChange(
e.target.value
? Number(
e.target
.value,
)
: null,
)
}
/>
)}
/>
</TableCell>
</TableRow>
{tabValue !== "IMR" && (
<TableRow>
<TableHead className="bg-muted font-medium">
스케일 조정 계수(A)
</TableHead>
<TableCell>
<Controller
name="scopCntCntVl"
control={control}
render={({ field }) => (
<div className="space-y-1">
<Input
type="number"
min="0"
max="1"
step="0.01"
value={field.value ?? ""}
onChange={(e) =>
field.onChange(
e.target.value
? Number(
e.target
.value,
)
: null,
)
}
placeholder="(0 ~ 1, 0.01 단위)"
/>
</div>
)}
/>
</TableCell>
<TableHead className="bg-muted font-medium">
임계 계수(c)
</TableHead>
<TableCell>
<Controller
name="thrsCntVl"
control={control}
render={({ field }) => (
<div className="space-y-1">
<Input
type="number"
min="0"
max="100"
step="1"
value={field.value ?? ""}
onChange={(e) =>
field.onChange(
e.target.value
? Number(
e.target
.value,
)
: null,
)
}
placeholder="(0 ~ 100, 1 단위)"
/>
</div>
)}
/>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
@@ -116,15 +116,6 @@ export const imrArticleColumnInfo: ColumnInfo = {
editable: false,
sortdisable: false,
},
{
name: "ipcrCntVl",
fieldName: "ipcrCntVl",
header: { text: "μ" },
editable: false,
sortdisable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.###",
},
{
name: "rgtrNm",
fieldName: "rgtrNm",
@@ -145,7 +136,6 @@ export const imrArticleColumnInfo: ColumnInfo = {
"vrfcTrgtNm",
"tblNm",
"colNm",
"ipcrCntVl",
"rgtrNm",
"regDt",
],
@@ -196,5 +186,12 @@ export const validationArticleColumnInfo: ColumnInfo = {
sortdisable: false,
},
],
columnLayout: ["statsNm", "vrfcTrgtNm", "tblNm", "colNm", "rgtrNm", "regDt"],
columnLayout: [
"statsNm",
"vrfcTrgtNm",
"tblNm",
"colNm",
"rgtrNm",
"regDt",
],
};
@@ -10,10 +10,31 @@ export const verificationTargetManagementSchema = z.object({
tblNm: z.string().min(1, "테이블명을 입력해주세요"),
colNm: z.string().min(1, "컬럼명을 입력해주세요"),
pkColNm: z.string().min(1, "PK 키를 입력해주세요"),
ipcrCntVl: z.number().optional().nullable(),
pctlCrtrVl: z.number().optional().nullable(),
scopCntCntVl: z.number().optional().nullable(),
thrsCntVl: z.number().optional().nullable(),
ipcrCntVl: z
.number()
.min(0, "중요도 계수는 0 이상이어야 합니다")
.max(1, "중요도 계수는 1 이하여야 합니다")
.nullable()
.optional(),
pctlCrtrVl: z
.number()
.min(0, "백분위수 기준값은 0 이상이어야 합니다")
.max(0.5, "백분위수 기준값은 0.5 이하여야 합니다")
.nullable()
.optional(),
scopCntCntVl: z
.number()
.min(0, "스케일 조정 계수는 0 이상이어야 합니다")
.max(1, "스케일 조정 계수는 1 이하여야 합니다")
.nullable()
.optional(),
thrsCntVl: z
.number()
.int("임계 계수는 정수여야 합니다")
.min(0, "임계 계수는 0 이상이어야 합니다")
.max(100, "임계 계수는 100 이하여야 합니다")
.nullable()
.optional(),
useYn: z.string().optional(),
vrfcTrgtNm: z.string().optional().nullable(),
rgtr: z.string().optional().nullable(),
+4 -2
View File
@@ -25,7 +25,8 @@
"dev:fo": "turbo run dev --filter=fo",
"dev:dm": "turbo run dev --filter=dm",
"preview:components": "turbo run storybook --filter=@common/components",
"preinstall": "npx only-allow pnpm"
"preinstall": "npx only-allow pnpm",
"prepare": "husky"
},
"dependencies": {
"chart.js": "^4.4.8",
@@ -38,6 +39,8 @@
"@types/node": "^22.13.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"husky": "^9.1.7",
"lint-staged": "^16.2.7",
"turbo": "2.4.2"
},
"pnpm": {
@@ -46,7 +49,6 @@
"@types/react-dom": "^19.2.3",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"lodash.isequal": "^4.5.0",
"source-map": "^0.7.4"
},
+403 -200
View File
File diff suppressed because it is too large Load Diff