st: feat: 일반검증 컴포넌트 구현중
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useRef, useEffect } from "react"
|
||||
import { useRef, useEffect, useLayoutEffect } from "react"
|
||||
import { ValueType } from "realgrid"
|
||||
|
||||
import RealGrid, { GridFitStyle } from "realgrid"
|
||||
import { RealGridReact, RGDataColumn } from "realgrid-react"
|
||||
import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback } from "../type/DataGrid.type"
|
||||
import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback, CellStyleCallback } from "../type/DataGrid.type"
|
||||
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks"
|
||||
|
||||
// RealGrid 타입 확장
|
||||
@@ -62,15 +62,36 @@ function createNumberComparer(dp: RealGridDataProviderComparer): (field: string
|
||||
};
|
||||
}
|
||||
|
||||
const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false, rowData }: DataGridProps) => {
|
||||
const CompactDataGrid = ({
|
||||
columnInfo,
|
||||
fixedColumns,
|
||||
showHeaderCheckbox = false,
|
||||
rowData,
|
||||
setRowData,
|
||||
getGridDataRef,
|
||||
cellStyleCallback,
|
||||
onRowClick,
|
||||
}: DataGridProps) => {
|
||||
const { columns, columnLayout } = columnInfo;
|
||||
const gridRef = useRef<RealGridReact>(null);
|
||||
const rowDataRef = useRef<Record<string, string | number>[]>(rowData);
|
||||
const cellStyleCallbackRef = useRef<CellStyleCallback | undefined>(cellStyleCallback);
|
||||
rowDataRef.current = rowData;
|
||||
cellStyleCallbackRef.current = cellStyleCallback;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!cellStyleCallback || !gridRef.current?.gridView) return;
|
||||
gridRef.current.gridView.refresh();
|
||||
}, [cellStyleCallback, rowData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!gridRef.current?.gridView || !gridRef.current?.dataProvider) return;
|
||||
|
||||
const gridView = gridRef.current.gridView;
|
||||
const dataProvider = gridRef.current.dataProvider;
|
||||
if ((dataProvider as { setRows?: (r: unknown[]) => void })?.setRows && rowData) {
|
||||
(dataProvider as { setRows: (r: unknown[]) => void }).setRows(rowData);
|
||||
}
|
||||
|
||||
// setDataComparer로 숫자 정렬 (setFields 미사용 → 데이터 유실·필드 중복 오류 없음)
|
||||
const dp = dataProvider as RealGridDataProviderComparer;
|
||||
@@ -231,11 +252,11 @@ const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false,
|
||||
// footer가 단일 객체이고 valueCallback이 있는 경우
|
||||
columns.forEach(col => {
|
||||
if (!col.footer || Array.isArray(col.footer)) return;
|
||||
|
||||
|
||||
try {
|
||||
const column = currentGridView.columnByName(col.name);
|
||||
if (!column || !column.footer) return;
|
||||
|
||||
|
||||
if (col.footer.valueCallback) {
|
||||
(column.footer as { valueCallback?: typeof col.footer.valueCallback }).valueCallback = col.footer.valueCallback;
|
||||
}
|
||||
@@ -243,9 +264,75 @@ const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false,
|
||||
console.warn(`Error setting footer valueCallback for column ${col.name}:`, e);
|
||||
}
|
||||
});
|
||||
|
||||
// cellStyleCallback이 있으면 컬럼별 styleCallback 설정
|
||||
if (cellStyleCallbackRef.current) {
|
||||
columns.forEach((column) => {
|
||||
const baseStyleName = column.styleName;
|
||||
const gridCol = currentGridView.columnByName(column.name) as {
|
||||
styleCallback?: (g: unknown, dataCell: { itemIndex?: number; index?: { itemIndex?: number }; dataField?: string; fieldName?: string }) => string;
|
||||
} | null;
|
||||
if (gridCol) {
|
||||
gridCol.styleCallback = (_g, dataCell) => {
|
||||
const itemIndex = dataCell?.index?.itemIndex ?? dataCell?.itemIndex ?? -1;
|
||||
const fieldName = (dataCell?.dataField ?? dataCell?.fieldName ?? column.fieldName) ?? '';
|
||||
const data = rowDataRef.current;
|
||||
if (itemIndex < 0 || itemIndex >= data.length) return baseStyleName ?? '';
|
||||
const row = data[itemIndex] as Record<string, string | number>;
|
||||
const extra = cellStyleCallbackRef.current?.({ rowIndex: itemIndex, fieldName, row });
|
||||
if (!extra) return baseStyleName ?? '';
|
||||
return baseStyleName ? `${extra} ${baseStyleName}` : extra;
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
currentGridView.refresh?.();
|
||||
}, 200);
|
||||
|
||||
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox]);
|
||||
if (getGridDataRef) {
|
||||
const fieldNames = columns.map((c) => c.fieldName).filter(Boolean) as string[];
|
||||
getGridDataRef.current = () => {
|
||||
const gv = gridRef.current?.gridView as {
|
||||
getDataSource?: () => { getRowCount(): number; getValue(r: number, f: string): unknown };
|
||||
} | undefined;
|
||||
if (!gv?.getDataSource) return [];
|
||||
const ds = gv.getDataSource();
|
||||
if (!ds) return [];
|
||||
const cnt = ds.getRowCount();
|
||||
const rows: Record<string, string | number>[] = [];
|
||||
for (let i = 0; i < cnt; i++) {
|
||||
const r: Record<string, string | number> = {};
|
||||
for (const f of fieldNames) r[f] = ds.getValue(i, f) as string | number;
|
||||
rows.push(r);
|
||||
}
|
||||
return rows;
|
||||
};
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (getGridDataRef) getGridDataRef.current = null;
|
||||
};
|
||||
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox, getGridDataRef]);
|
||||
|
||||
// 행 클릭 이벤트
|
||||
useEffect(() => {
|
||||
const gridView = gridRef.current?.gridView;
|
||||
if (!gridView || !onRowClick) return;
|
||||
|
||||
gridView.onCellClicked = (grid, clickData) => {
|
||||
if (clickData.cellType !== "data" || clickData.itemIndex === undefined) return;
|
||||
try {
|
||||
const row = rowDataRef.current[clickData.itemIndex];
|
||||
onRowClick((row ?? {}) as Record<string, string | number>);
|
||||
} catch (error) {
|
||||
console.error("행 데이터 가져오기 오류:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return () => {
|
||||
gridView.onCellClicked = undefined;
|
||||
};
|
||||
}, [onRowClick]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem] w-1/2">
|
||||
@@ -256,10 +343,15 @@ const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false,
|
||||
layout={columnLayout}
|
||||
rows={rowData}
|
||||
onInitialized={() => {
|
||||
const dp = gridRef.current?.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined;
|
||||
const dp = gridRef.current?.dataProvider as
|
||||
| { setFields?(f: unknown[]): void; setRows?(r: Record<string, string | number>[]): void }
|
||||
| undefined;
|
||||
if (dp?.setFields) {
|
||||
dp.setFields(buildFieldsFromColumns(columns));
|
||||
}
|
||||
if (dp?.setRows && rowData?.length) {
|
||||
dp.setRows(rowData);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{columns.map((column) => (
|
||||
|
||||
@@ -1,61 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
// import useSWR from 'swr';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
// Input,
|
||||
Label,
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectValue
|
||||
// Select,
|
||||
// SelectTrigger,
|
||||
// SelectContent,
|
||||
// SelectItem,
|
||||
// SelectValue
|
||||
}
|
||||
from "@common/components";
|
||||
import DataGrid from '@/components/module/DataGrid';
|
||||
import { CodeSelect } from '@/entities/code';
|
||||
|
||||
import { LogHistoryColumn } from '@/components/util/column/management/LogHistoryColumn';
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import {
|
||||
LwpoCodeApi,
|
||||
} from "@/entities/code";
|
||||
// import { LwpoCodeApi } from "@/entities/code";
|
||||
|
||||
const getStateCdListFetcher = async () => {
|
||||
return LwpoCodeApi.getLwpoCodeList({
|
||||
sort: 'regDt,desc',
|
||||
upCd: 'STITEM',
|
||||
page: 0,
|
||||
size: 100,
|
||||
});
|
||||
}
|
||||
// const getStateCdListFetcher = async () => {
|
||||
// return LwpoCodeApi.getLwpoCodeList({
|
||||
// sort: 'regDt,desc',
|
||||
// upCd: 'STITEM',
|
||||
// page: 0,
|
||||
// size: 100,
|
||||
// });
|
||||
// }
|
||||
|
||||
const LogHistory = () => {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const [lwpoCd, setLwpoCd] = useState<string>('');
|
||||
// const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const [statsCd, setStatsCd] = useState('');
|
||||
|
||||
const { data: lwpoCdList } = useSWR(
|
||||
"lwpo-code-list",
|
||||
getStateCdListFetcher,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
revalidateIfStale: false,
|
||||
dedupingInterval: 2000,
|
||||
shouldRetryOnError: false,
|
||||
onSuccess: (data) => {
|
||||
if (!data.content || data.content.length == 0) {
|
||||
alert(`시도 목록을 불러오는데 실패했습니다.
|
||||
새로고침 후 다시 시도해주세요.`);
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
alert(`시도 목록을 불러오는데 실패했습니다.
|
||||
새로고침 후 다시 시도해주세요.`);
|
||||
},
|
||||
}
|
||||
);
|
||||
// const { data: lwpoCdList } = useSWR(
|
||||
// "lwpo-code-list",
|
||||
// getStateCdListFetcher,
|
||||
// {
|
||||
// revalidateOnFocus: false,
|
||||
// revalidateOnReconnect: false,
|
||||
// revalidateIfStale: false,
|
||||
// dedupingInterval: 2000,
|
||||
// shouldRetryOnError: false,
|
||||
// onSuccess: (data) => {
|
||||
// if (!data.content || data.content.length == 0) {
|
||||
// alert(`시도 목록을 불러오는데 실패했습니다.
|
||||
// 새로고침 후 다시 시도해주세요.`);
|
||||
// }
|
||||
// },
|
||||
// onError: () => {
|
||||
// alert(`시도 목록을 불러오는데 실패했습니다.
|
||||
// 새로고침 후 다시 시도해주세요.`);
|
||||
// },
|
||||
// }
|
||||
// );
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
@@ -64,9 +63,9 @@ const LogHistory = () => {
|
||||
<div className="flex items-center gap-8 p-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="text-label">
|
||||
메뉴명
|
||||
통계항목
|
||||
</Label>
|
||||
<Select value={lwpoCd} onValueChange={setLwpoCd}>
|
||||
{/* <Select value={lwpoCd} onValueChange={setLwpoCd}>
|
||||
<SelectTrigger className="w-[22rem]">
|
||||
<SelectValue placeholder="전체"></SelectValue>
|
||||
</SelectTrigger>
|
||||
@@ -78,7 +77,14 @@ const LogHistory = () => {
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Select> */}
|
||||
<CodeSelect
|
||||
upCd="STITEM"
|
||||
value={statsCd}
|
||||
onValueChange={setStatsCd}
|
||||
placeholder="선택"
|
||||
className="w-[16rem]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<Button color="green" className="h-[3.2rem] w-[96px]" type="button">
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { CodeSelect } from '@/entities/code';
|
||||
import {
|
||||
Button,
|
||||
Label,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
const GeneralVerificationManagement = () => {
|
||||
const [statsCd, setStatsCd] = useState('');
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="card card-border">
|
||||
<form>
|
||||
<div className="flex items-center gap-8 p-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="text-label">
|
||||
통계항목
|
||||
</Label>
|
||||
<CodeSelect
|
||||
upCd="STITEM"
|
||||
value={statsCd}
|
||||
onValueChange={setStatsCd}
|
||||
placeholder="선택"
|
||||
className="w-[16rem]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<Button color="green" className="h-[3.2rem] w-[96px]" type="button">
|
||||
초기화
|
||||
</Button>
|
||||
<Button className="h-[3.2rem] w-[96px]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className='card !mb-0 flex gap-5 !p-0'>
|
||||
<div className='card card-border w-1/2 !mx-0 flex flex-col'>
|
||||
<Table variant="default">
|
||||
<TableRow>
|
||||
<TableHead>No</TableHead>
|
||||
<TableHead>검증항목</TableHead>
|
||||
<TableHead>등록자</TableHead>
|
||||
<TableHead>등록일</TableHead>
|
||||
<TableHead>사용여부</TableHead>
|
||||
</TableRow>
|
||||
<TableBody className='text-center'>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
1
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
인구수검증
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
관리자
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
2026-02-09
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
Y
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className='w-1/2'>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralVerificationManagement;
|
||||
Reference in New Issue
Block a user