73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { listAudit } from '../api';
|
|
import { AuditLog } from '../types';
|
|
import { formatDateTime } from '../status';
|
|
|
|
const ACTION_LABEL: Record<AuditLog['action'], string> = {
|
|
APPROVE: '승인',
|
|
REJECT: '반려',
|
|
DELETE: '삭제',
|
|
BLACKLIST_ADD: '블랙리스트 등록',
|
|
BLACKLIST_REMOVE: '블랙리스트 해제',
|
|
ADMIN_CONFIG_UPDATE: '시스템 설정',
|
|
};
|
|
|
|
const ACTION_CLASS: Record<AuditLog['action'], string> = {
|
|
APPROVE: 'green',
|
|
REJECT: 'red',
|
|
DELETE: 'red',
|
|
BLACKLIST_ADD: 'red',
|
|
BLACKLIST_REMOVE: 'gray',
|
|
ADMIN_CONFIG_UPDATE: 'blue',
|
|
};
|
|
|
|
export const AuditLogPage: React.FC = () => {
|
|
const [items, setItems] = useState<AuditLog[]>([]);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
const load = () => {
|
|
setLoading(true);
|
|
listAudit()
|
|
.then(setItems)
|
|
.catch((e) => setError(e instanceof Error ? e.message : '조회 실패'))
|
|
.finally(() => setLoading(false));
|
|
};
|
|
|
|
useEffect(load, []);
|
|
|
|
return (
|
|
<div>
|
|
<div className="page-head">
|
|
<h2>감사 로그</h2>
|
|
<button className="btn-ghost" onClick={load} disabled={loading}>새로고침</button>
|
|
</div>
|
|
{error && <div className="alert alert-error">{error}</div>}
|
|
|
|
<div className="card">
|
|
<h3>최근 관리 행위 ({items.length})</h3>
|
|
{items.length === 0 ? (
|
|
<p className="muted">{loading ? '불러오는 중…' : '기록이 없습니다.'}</p>
|
|
) : (
|
|
<table className="table">
|
|
<thead>
|
|
<tr><th>시각</th><th>수행자</th><th>행위</th><th>대상</th><th>상세</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{items.map((a) => (
|
|
<tr key={a.id}>
|
|
<td>{formatDateTime(a.at)}</td>
|
|
<td>{a.actorUsername || '시스템'}</td>
|
|
<td><span className={`badge badge-${ACTION_CLASS[a.action]}`}>{ACTION_LABEL[a.action] ?? a.action}</span></td>
|
|
<td>{a.targetType ? `${a.targetType}${a.targetId != null ? ` #${a.targetId}` : ''}` : '-'}</td>
|
|
<td>{a.detail || '-'}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|