st: fix: 하수관로 검토 컬럼 수정 & 관리자 계정 판별 로직 추가
This commit is contained in:
@@ -708,7 +708,7 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
|
||||
valueType: col.valueType,
|
||||
dataFormat: col.dataFormat,
|
||||
...(col.styleName != null && { styleName: col.styleName }),
|
||||
...(col.mergeCells && { mergeRule: { criteria: 'value' as const } }),
|
||||
...(col.mergeCells && { mergeRule: { criteria: col.mergeGroupField ? `values['${col.mergeGroupField}'] + value` : 'value' as const } }),
|
||||
};
|
||||
if (col.footer) {
|
||||
if (Array.isArray(col.footer)) c.footer = col.footer;
|
||||
@@ -993,7 +993,7 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
|
||||
valueType: col.valueType,
|
||||
dataFormat: col.dataFormat,
|
||||
...(col.styleName != null && { styleName: col.styleName }),
|
||||
...(col.mergeCells && { mergeRule: { criteria: 'value' as const } }),
|
||||
...(col.mergeCells && { mergeRule: { criteria: col.mergeGroupField ? `values['${col.mergeGroupField}'] + value` : 'value' as const } }),
|
||||
};
|
||||
|
||||
if (col.footer) {
|
||||
@@ -1151,7 +1151,7 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
|
||||
expression: (column.footer as { expression?: 'sum' | 'avg' | 'count' | 'max' | 'min' }).expression,
|
||||
numberFormat: (column.footer as { numberFormat?: string }).numberFormat,
|
||||
} : undefined}
|
||||
mergeRule={column.mergeCells ? { criteria: 'value' } : undefined}
|
||||
mergeRule={column.mergeCells ? { criteria: column.mergeGroupField ? `values['${column.mergeGroupField}'] + value` : 'value' } : undefined}
|
||||
sortable={!column.sortdisable}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
|
||||
import SubmitDailog from "./SubmitDailog";
|
||||
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { useAdminStore } from "@/store/admin-store";
|
||||
import { statisticsInputAPi } from "@/entities/statistics/api/statistics-input-service";
|
||||
import type { SearchFormValues } from "@/components/type/SearchForm.type";
|
||||
import type {
|
||||
@@ -60,6 +61,8 @@ const SeweragePenetrationRateInp = () => {
|
||||
const [showDailog, setShowDailog] = useState<boolean>(false);
|
||||
const { confirm } = useConfirm();
|
||||
|
||||
const { isAdminInfo } = useAdminStore();
|
||||
|
||||
const [footerFromApi, setFooterFromApi] = useState<{ totalPopl?: number; poplSm?: number; frgnrPoplSm?: number; refArea?: number } | null>(null);
|
||||
const getGridDataRef = useRef<(() => Record<string, string | number>[]) | null>(null);
|
||||
|
||||
@@ -74,7 +77,7 @@ const SeweragePenetrationRateInp = () => {
|
||||
// 최상위 관리자일땐 모든 상황에서 edit 가능하게 수정 필요.
|
||||
const columnInfo = useMemo(() => {
|
||||
const { columns, columnLayout } = SeweragePenetrationRateInpColumn;
|
||||
const base = isEnterable ? columns : columns.map((col) => ({ ...col, editable: false as const }));
|
||||
const base = (isEnterable || isAdminInfo) ? columns : columns.map((col) => ({ ...col, editable: false as const }));
|
||||
return {
|
||||
columnLayout,
|
||||
columns: base.map((col) => {
|
||||
@@ -85,7 +88,7 @@ const SeweragePenetrationRateInp = () => {
|
||||
return col;
|
||||
}),
|
||||
};
|
||||
}, [isEnterable, footerFromApi]);
|
||||
}, [isEnterable, isAdminInfo, footerFromApi]);
|
||||
|
||||
const handleReset = () => {
|
||||
setRowData([]);
|
||||
@@ -389,26 +392,27 @@ const SeweragePenetrationRateInp = () => {
|
||||
<>
|
||||
<div className="relative">
|
||||
<SearchForm onSubmit={handleSearch} onReset={handleReset} onValuesChange={setSearchValues} />
|
||||
{rowData.length > 0 && isEnterable && (doList.includes(lastSearchedValues?.bsnmCd || '') ? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL') : true) &&
|
||||
{rowData.length > 0 && (isEnterable && (doList.includes(lastSearchedValues?.bsnmCd || '') ? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL') : true) || isAdminInfo) && (
|
||||
<div className="card !py-0">
|
||||
<div className="flex justify-end items-center gap-[0.6rem] mx-[0.6rem]">
|
||||
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
|
||||
<LastYearDataButton getLastYearDataFunc={() => getLastYearDataFunc(searchValues ?? {})} />
|
||||
<SubmitButton submitFunc={submitFunc} />
|
||||
{
|
||||
// 최고 관리자일땐 저장 가능하게
|
||||
<SaveButton saveFunc={saveFunc} />
|
||||
}
|
||||
{isEnterable && (doList.includes(lastSearchedValues?.bsnmCd || '') ? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL') : true) && (
|
||||
<>
|
||||
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
|
||||
<LastYearDataButton getLastYearDataFunc={() => getLastYearDataFunc(searchValues ?? {})} />
|
||||
<SubmitButton submitFunc={submitFunc} />
|
||||
</>
|
||||
)}
|
||||
{(isEnterable && (doList.includes(lastSearchedValues?.bsnmCd || '') ? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL') : true) || isAdminInfo) && <SaveButton saveFunc={saveFunc} />}
|
||||
</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
|
||||
key={isEnterable ? 'enterable' : 'readonly'}
|
||||
key={(isEnterable || isAdminInfo) ? 'enterable' : 'readonly'}
|
||||
columnInfo={columnInfo}
|
||||
isExcelImport={(rowData.length > 0) && isEnterable && (doList.includes(lastSearchedValues?.bsnmCd || '') ? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL') : true)}
|
||||
rowStartNum={3}
|
||||
|
||||
@@ -12,7 +12,7 @@ export type RealGridColumnInfo = {
|
||||
valueType?: import("realgrid").ValueType;
|
||||
dataFormat?: string;
|
||||
styleName?: string;
|
||||
mergeRule?: { criteria: "value" };
|
||||
mergeRule?: { criteria: "value" | string };
|
||||
sortable?: boolean;
|
||||
footer?:
|
||||
| {
|
||||
@@ -135,6 +135,8 @@ export type ColumnConfig = {
|
||||
/** 셀에 적용할 CSS 클래스명 (예: 'rg-cell-readonly', 'rg-cell-total') */
|
||||
styleName?: string;
|
||||
mergeCells?: boolean;
|
||||
/** 병합 시 그룹 기준 필드(예: 시군구). 지정 시 같은 그룹 내에서만 value 기준 병합 */
|
||||
mergeGroupField?: string;
|
||||
sortdisable?: boolean;
|
||||
sortPreserveGroupOrder?: SortPreserveGroupOrder;
|
||||
valueType?: ValueType;
|
||||
|
||||
@@ -218,7 +218,8 @@ const rawList = [
|
||||
]
|
||||
},
|
||||
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, mergeCells: true, sortdisable: true },
|
||||
{ name: 'swrmtFcltyNm', fieldName: 'swrmtFcltyNm', header: { text: '하수처리시설 명칭' }, mergeCells: true },
|
||||
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, mergeCells: true, sortdisable: true },
|
||||
{ name: 'swrmtFcltyNm', fieldName: 'swrmtFcltyNm', header: { text: '하수처리시설 명' }, mergeCells: true, mergeGroupField: 'admdstNm' },
|
||||
{
|
||||
name: 'displayYear',
|
||||
fieldName: 'displayYear',
|
||||
@@ -276,8 +277,9 @@ const rawList = [
|
||||
name: 'addrGroup',
|
||||
header: { text: '행정구역' },
|
||||
items: [
|
||||
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 3 }, { colspan: 3 }] },
|
||||
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] },
|
||||
'sggNm',
|
||||
'admdstNm',
|
||||
'swrmtFcltyNm',
|
||||
]
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Suspense, useRef, useEffect } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useTabStore } from "@/store/tab-store";
|
||||
import { useAdminStore } from "@/store/admin-store";
|
||||
import Dashboard from "@/components/Dashboard";
|
||||
import { SkeletonPage } from "@/shared/ui/skeleton-page";
|
||||
|
||||
@@ -10,6 +11,7 @@ import "@rsone/components/index.css";
|
||||
|
||||
const StatisticsPage = () => {
|
||||
const { tabs, activeTabId } = useTabStore();
|
||||
const fetchAdminInfo = useAdminStore((state) => state.fetchAdminInfo);
|
||||
|
||||
// 컴포넌트 캐시를 useRef로 유지 (재렌더링 시에도 유지됨)
|
||||
const componentCache = useRef(new Map<string, React.ComponentType>());
|
||||
@@ -26,6 +28,10 @@ const StatisticsPage = () => {
|
||||
});
|
||||
}, [tabs]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAdminInfo();
|
||||
}, [fetchAdminInfo]);
|
||||
|
||||
// 탭이 없으면 Dashboard 표시
|
||||
if (tabs.length === 0) {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { create } from "zustand";
|
||||
import { UserApi } from "@/entities/user";
|
||||
import { getUserInfo } from "../../../../packages/common/components/src/api";
|
||||
|
||||
interface AdminStore {
|
||||
/** 현재 사용자가 관리자 목록에 있는지 */
|
||||
isAdminInfo: boolean;
|
||||
setAdminInfo: (value: boolean) => void;
|
||||
/** API로 관리자 목록 조회 후 현재 사용자 일치 여부 설정 */
|
||||
fetchAdminInfo: () => Promise<void>;
|
||||
}
|
||||
|
||||
const useAdminStoreHook = create<AdminStore>((set) => ({
|
||||
isAdminInfo: false,
|
||||
setAdminInfo: (value) => set({ isAdminInfo: value }),
|
||||
fetchAdminInfo: async () => {
|
||||
try {
|
||||
const userInfo = await getUserInfo();
|
||||
const adminList = await UserApi.findPageAdminUser({ userName: userInfo.userName });
|
||||
const matched = adminList.content?.filter((a) => a.userName === userInfo.userName) ?? [];
|
||||
set({ isAdminInfo: matched.length > 0 });
|
||||
} catch {
|
||||
set({ isAdminInfo: false });
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
export const useAdminStore = useAdminStoreHook;
|
||||
export const getAdminStoreState = () => useAdminStoreHook.getState();
|
||||
Reference in New Issue
Block a user