Files
operationSystem-front/src/components/common/modal/LogDetailModal.js
bcjang ac9bcdda8b 로그인정보 만료시 비밀번호 초기화 추가,
랭킹 강제 초기화 버튼 추가,
랭킹 시스템 조회 및 수정
2025-11-28 16:39:39 +09:00

253 lines
6.5 KiB
JavaScript

import { styled } from 'styled-components';
import React, { Fragment, useEffect, useState } from 'react';
import { Title } from '../../../styles/Components';
import { BtnWrapper, TableStyle } from '../../../styles/Components';
import Button from '../../../components/common/button/Button';
import Modal from '../../../components/common/modal/Modal';
import { TableWrapper } from '../../../styles/Components';
import { Tab, TabContent } from '../../../styles/ModuleComponents';
import { convertKTC, getFieldLabel } from '../../../utils';
import { historyBenField } from '../../../assets/data/data';
const LogDetailModal = ({ detailView,
handleDetailView,
detailData,
changedData,
viewMode = 'both',
title = '로그 상세정보'
}) => {
const [activeTab, setActiveTab] = useState('data');
// viewMode가 변경될 때 적절한 탭 활성화
useEffect(() => {
if (viewMode === 'data' || viewMode === 'changed') {
setActiveTab(viewMode);
}
}, [viewMode]);
// data 객체의 키-값 쌍을 테이블 형식으로 변환
const renderDataTable = () => {
if (!detailData || !detailData) return null;
// data 객체를 평탄화하는 함수 (중첩된 객체도 처리)
const flattenObject = (obj, prefix = '') => {
return Object.keys(obj).reduce((acc, key) => {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
// $date나 $oid 같은 특수 키를 포함하는 MongoDB 형식 객체 처리
if (obj[key].$date) {
acc[newKey] = new Date(obj[key].$date).toLocaleString('ko-KR');
return acc;
}
if (obj[key].$oid) {
acc[newKey] = obj[key].$oid;
return acc;
}
if (obj[key].$numberLong) {
acc[newKey] = obj[key].$numberLong;
return acc;
}
// 일반 중첩 객체는 재귀적으로 평탄화
return {...acc, ...flattenObject(obj[key], newKey)};
}
// 배열 값은 JSON 문자열로 변환
if (Array.isArray(obj[key])) {
acc[newKey] = JSON.stringify(obj[key]);
} else {
acc[newKey] = obj[key];
}
return acc;
}, {});
};
const flattenedData = flattenObject(detailData);
return (
<TableStyle>
<caption>데이터 상세 정보</caption>
<thead>
<tr>
<th width="120px">필드명</th>
<th width="200px"></th>
</tr>
</thead>
<tbody>
{detailData && Object.entries(flattenedData).map(([key, value], index) => (
<tr key={index}>
<td>{getFieldLabel(key)}</td>
<td>{value !== null && value !== undefined ? String(value) : ''}</td>
</tr>
))}
</tbody>
</TableStyle>
);
};
// changed 배열의 항목들을 테이블로 표시
const renderChangedTable = () => {
if (!changedData || !Array.isArray(changedData)) {
return null;
}
const allChangedItems = [];
changedData.forEach((item, itemIndex) => {
if (item.domain.changed && Array.isArray(item.domain.changed)) {
item.domain.changed.forEach((changedItem) => {
allChangedItems.push({
...changedItem,
// 어떤 데이터 항목에서 온 것인지 구분하기 위한 정보 추가
sourceIndex: itemIndex,
sourceInfo: {
dbType: item.dbType,
logTime: item.logTime,
operationType: item.domain.operationType,
historyType: item.historyType,
tableName: item.tableName,
tranId: item.tranId,
worker: item.worker
}
});
});
}
});
if (allChangedItems.length === 0) {
return (
<TableWrapper>
<div style={{ textAlign: 'center', padding: '20px', color: '#666' }}>
변경 항목이 없습니다.
</div>
</TableWrapper>
);
}
// 값 포맷팅 함수
const formatValue = (value) => {
if (value === null || value === undefined) return '';
if (typeof value === 'object') {
if (value.$date) return convertKTC(value.$date,false);
if (value.$oid) return value.$oid;
if (value.$numberLong) return value.$numberLong;
return JSON.stringify(value);
}
return String(value);
};
return (
<TableStyle style={{ minWidth: 'auto', width: '100%', tableLayout: 'fixed' }}>
<caption>변경 항목 목록</caption>
<thead>
<tr>
<th width="60px">유형</th>
<th width="120px">필드명</th>
<th width="150px">신규 </th>
<th width="150px">이전 </th>
<th width="100px">작업자</th>
<th width="130px">작업일시(KST)</th>
</tr>
</thead>
<tbody>
{allChangedItems.map((item, index) => {
if (historyBenField.includes(item.fieldName)) {
return null;
}
return (
<tr key={index}>
<td>{getFieldLabel(item.sourceInfo.operationType)}</td>
<td>{getFieldLabel(item.fieldName)}</td>
<td>{formatValue(item.newValue)}</td>
<td>{formatValue(item.oldValue)}</td>
<td>{item.sourceInfo.worker}</td>
<td>{convertKTC(item.sourceInfo.logTime, false)}</td>
</tr>
)
})}
</tbody>
</TableStyle>
);
};
// 단일 뷰 또는 탭 뷰 렌더링 결정
const renderContent = () => {
if ((viewMode === 'data' && !detailData) || (viewMode === 'changed' && !changedData)) return null;
// 단일 뷰 모드
if (viewMode === 'data') {
return renderDataTable();
}
if (viewMode === 'changed') {
return renderChangedTable();
}
return (
<>
{/* 탭 메뉴 */}
<TabContainer>
<Tab
$active={activeTab === 'data'}
onClick={() => setActiveTab('data')}
>
데이터 정보
</Tab>
<Tab
$active={activeTab === 'changed'}
onClick={() => setActiveTab('changed')}
>
변경 항목
</Tab>
</TabContainer>
{/* 탭 컨텐츠 */}
<TabContent $active={activeTab === 'data'}>
{renderDataTable()}
</TabContent>
<TabContent $active={activeTab === 'changed'}>
{renderChangedTable()}
</TabContent>
</>
);
};
return (
<>
<Modal $view={detailView} min="400px">
<Title $align="center">{title}</Title>
{renderContent()}
<BtnWrapper2 $justify="center">
<Button
theme="line"
text="확인"
handleClick={e => {
e.preventDefault();
handleDetailView();
}}
/>
</BtnWrapper2>
</Modal>
</>
);
};
export default LogDetailModal;
const BtnWrapper2 = styled(BtnWrapper)`
margin-top: 30px;
`;
const TabContainer = styled.div`
display: flex;
margin: 20px 0 10px;
border-bottom: 1px solid #ddd;
`;