253 lines
6.5 KiB
JavaScript
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;
|
|
`; |