feat: add admin test data reset
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
PublicPass,
|
||||
Team,
|
||||
VisitRequestCreate,
|
||||
VisitRequestResetResult,
|
||||
VisitRequestView,
|
||||
AdminUser,
|
||||
Watcher1Settings,
|
||||
@@ -194,6 +195,8 @@ export const runSmsDiagnostics = () =>
|
||||
request<SmsDiagnosticsResult>('/admin/sms/diagnostics', { method: 'POST' });
|
||||
export const runEmailDiagnostics = () =>
|
||||
request<EmailDiagnosticsResult>('/admin/email/diagnostics', { method: 'POST' });
|
||||
export const resetVisitRequestTestData = (confirm: string) =>
|
||||
request<VisitRequestResetResult>('/admin/visit-requests/reset-test-data', jsonInit('POST', { confirm }));
|
||||
export const listAdminUsers = () => request<AdminUser[]>('/admin/users');
|
||||
export const updateAdminUserRoles = (id: number, roles: Role[]) =>
|
||||
request<AdminUser>(`/admin/users/${id}/roles`, jsonInit('PUT', { roles }));
|
||||
|
||||
@@ -14,14 +14,15 @@ import {
|
||||
updateAdminUserRoles,
|
||||
runEmailDiagnostics,
|
||||
runSmsDiagnostics,
|
||||
resetVisitRequestTestData,
|
||||
uploadAdminTeams,
|
||||
uploadAdminUserRoles,
|
||||
updateWatcher1Settings,
|
||||
getWatcher1Settings,
|
||||
} from '../api';
|
||||
import { AdminExcelImportResult, AdminUser, EmailDiagnosticsResult, PurposeCode, Role, SmsDiagnosticsResult, Team, Watcher1Settings } from '../types';
|
||||
import { AdminExcelImportResult, AdminUser, EmailDiagnosticsResult, PurposeCode, Role, SmsDiagnosticsResult, Team, VisitRequestResetResult, Watcher1Settings } from '../types';
|
||||
|
||||
type Tab = 'watcher' | 'purpose' | 'teams' | 'roles' | 'sms' | 'email';
|
||||
type Tab = 'watcher' | 'purpose' | 'teams' | 'roles' | 'sms' | 'email' | 'data';
|
||||
|
||||
const EMPTY_PURPOSE = {
|
||||
code: '',
|
||||
@@ -141,6 +142,8 @@ export const AdminManagementPage: React.FC = () => {
|
||||
const [userRoleUploadResult, setUserRoleUploadResult] = useState<AdminExcelImportResult | null>(null);
|
||||
const [smsDiagnostics, setSmsDiagnostics] = useState<SmsDiagnosticsResult | null>(null);
|
||||
const [emailDiagnostics, setEmailDiagnostics] = useState<EmailDiagnosticsResult | null>(null);
|
||||
const [resetConfirm, setResetConfirm] = useState('');
|
||||
const [resetResult, setResetResult] = useState<VisitRequestResetResult | null>(null);
|
||||
const [notice, setNotice] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
@@ -203,6 +206,27 @@ export const AdminManagementPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const resetVisitData = async () => {
|
||||
if (resetConfirm.trim() !== '초기화') {
|
||||
setError('확인 입력란에 "초기화"를 입력하세요.');
|
||||
return;
|
||||
}
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
setNotice(null);
|
||||
setResetResult(null);
|
||||
try {
|
||||
const result = await resetVisitRequestTestData(resetConfirm);
|
||||
setResetResult(result);
|
||||
setResetConfirm('');
|
||||
setNotice(`출입신청 테스트 데이터 초기화 완료: 신청 ${result.visitRequests}건 삭제`);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : '테스트 데이터 초기화 실패');
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const editPurpose = (p: PurposeCode) => {
|
||||
setEditingPurposeId(p.id);
|
||||
setPurposeForm({
|
||||
@@ -440,6 +464,7 @@ export const AdminManagementPage: React.FC = () => {
|
||||
<button className={tab === 'roles' ? 'tab-active' : ''} onClick={() => setTab('roles')}>권한관리</button>
|
||||
<button className={tab === 'sms' ? 'tab-active' : ''} onClick={() => setTab('sms')}>SMS 점검</button>
|
||||
<button className={tab === 'email' ? 'tab-active' : ''} onClick={() => setTab('email')}>Email 점검</button>
|
||||
<button className={tab === 'data' ? 'tab-active' : ''} onClick={() => setTab('data')}>데이터 정리</button>
|
||||
</div>
|
||||
|
||||
{tab === 'watcher' && (
|
||||
@@ -616,6 +641,43 @@ export const AdminManagementPage: React.FC = () => {
|
||||
</>
|
||||
)}
|
||||
|
||||
{tab === 'data' && (
|
||||
<div className="card">
|
||||
<div className="admin-upload-head">
|
||||
<h3>출입신청 테스트 데이터 초기화</h3>
|
||||
</div>
|
||||
<p className="muted">
|
||||
출입신청, 승인 기록, 입/퇴장 기록, 출입증 발송 기록을 삭제합니다. 사용자, 팀, 권한, 출입목적 코드, 시스템 설정, 블랙리스트는 유지됩니다.
|
||||
</p>
|
||||
<div className="admin-upload-controls">
|
||||
<input
|
||||
value={resetConfirm}
|
||||
onChange={(e) => setResetConfirm(e.target.value)}
|
||||
placeholder="초기화"
|
||||
/>
|
||||
<button
|
||||
className="btn-danger"
|
||||
type="button"
|
||||
disabled={busy || resetConfirm.trim() !== '초기화'}
|
||||
onClick={() => void resetVisitData()}
|
||||
>
|
||||
테스트 데이터 초기화
|
||||
</button>
|
||||
</div>
|
||||
{resetResult && (
|
||||
<table className="table diagnostic-table">
|
||||
<tbody>
|
||||
<tr><th>출입신청</th><td>{resetResult.visitRequests}건</td></tr>
|
||||
<tr><th>방문자</th><td>{resetResult.visitors}건</td></tr>
|
||||
<tr><th>승인 기록</th><td>{resetResult.approvals}건</td></tr>
|
||||
<tr><th>입/퇴장 기록</th><td>{resetResult.accessEvents}건</td></tr>
|
||||
<tr><th>출입증 발송 기록</th><td>{resetResult.passDeliveries}건</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === 'sms' && (
|
||||
<div className="card">
|
||||
<div className="admin-upload-head">
|
||||
|
||||
@@ -197,6 +197,14 @@ export interface AdminExcelImportResult {
|
||||
rows: AdminExcelImportRow[];
|
||||
}
|
||||
|
||||
export interface VisitRequestResetResult {
|
||||
visitRequests: number;
|
||||
visitors: number;
|
||||
approvals: number;
|
||||
accessEvents: number;
|
||||
passDeliveries: number;
|
||||
}
|
||||
|
||||
export interface InsideVisitor {
|
||||
visitRequestId: number;
|
||||
visitorName: string;
|
||||
|
||||
Reference in New Issue
Block a user