fix: add error feedback and accessibility to data management page
- Add user-facing error state with red alert box for failed operations - Improve error handling in fetchJobs and runCollector functions - Add aria-labels to collector buttons for better accessibility - Add scope="col" to table headers for proper semantic HTML - Add refresh loading state with disabled button styling - Create handleRefresh function for proper state management during refresh Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
11e5158378
commit
68f9974cd6
@ -35,6 +35,8 @@ export default function DataManagementPage() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [jobs, setJobs] = useState<JobLog[]>([]);
|
||||
const [collecting, setCollecting] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
@ -53,25 +55,40 @@ export default function DataManagementPage() {
|
||||
|
||||
const fetchJobs = async () => {
|
||||
try {
|
||||
setError(null);
|
||||
const data = await api.get<JobLog[]>('/api/admin/collect/status');
|
||||
setJobs(data);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Failed to fetch jobs';
|
||||
setError(message);
|
||||
console.error('Failed to fetch jobs:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const runCollector = async (key: string) => {
|
||||
setCollecting(key);
|
||||
setError(null);
|
||||
try {
|
||||
await api.post(`/api/admin/collect/${key}`);
|
||||
await fetchJobs();
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Collection failed';
|
||||
setError(message);
|
||||
console.error('Collection failed:', err);
|
||||
} finally {
|
||||
setCollecting(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRefresh = async () => {
|
||||
setRefreshing(true);
|
||||
try {
|
||||
await fetchJobs();
|
||||
} finally {
|
||||
setRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
const colors: Record<string, string> = {
|
||||
success: 'bg-green-100 text-green-800',
|
||||
@ -97,6 +114,12 @@ export default function DataManagementPage() {
|
||||
<main className="p-6">
|
||||
<h1 className="text-2xl font-bold text-gray-800 mb-6">데이터 수집 관리</h1>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-white rounded-lg shadow mb-6">
|
||||
<div className="p-4 border-b">
|
||||
<h2 className="text-lg font-semibold">수집 작업</h2>
|
||||
@ -113,6 +136,7 @@ export default function DataManagementPage() {
|
||||
<button
|
||||
onClick={() => runCollector(col.key)}
|
||||
disabled={collecting !== null}
|
||||
aria-label={`${col.label} 수집 실행`}
|
||||
className="mt-auto px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:bg-blue-400 transition-colors"
|
||||
>
|
||||
{collecting === col.key ? '수집 중...' : '실행'}
|
||||
@ -127,21 +151,22 @@ export default function DataManagementPage() {
|
||||
<div className="p-4 border-b flex justify-between items-center">
|
||||
<h2 className="text-lg font-semibold">최근 작업 이력</h2>
|
||||
<button
|
||||
onClick={fetchJobs}
|
||||
className="text-sm text-blue-600 hover:text-blue-800"
|
||||
onClick={handleRefresh}
|
||||
disabled={refreshing}
|
||||
className="text-sm text-blue-600 hover:text-blue-800 disabled:text-gray-400"
|
||||
>
|
||||
새로고침
|
||||
{refreshing ? '새로고침 중...' : '새로고침'}
|
||||
</button>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-sm font-medium text-gray-600">작업명</th>
|
||||
<th className="px-4 py-3 text-left text-sm font-medium text-gray-600">상태</th>
|
||||
<th className="px-4 py-3 text-left text-sm font-medium text-gray-600">시작 시간</th>
|
||||
<th className="px-4 py-3 text-left text-sm font-medium text-gray-600">건수</th>
|
||||
<th className="px-4 py-3 text-left text-sm font-medium text-gray-600">에러</th>
|
||||
<th scope="col" className="px-4 py-3 text-left text-sm font-medium text-gray-600">작업명</th>
|
||||
<th scope="col" className="px-4 py-3 text-left text-sm font-medium text-gray-600">상태</th>
|
||||
<th scope="col" className="px-4 py-3 text-left text-sm font-medium text-gray-600">시작 시간</th>
|
||||
<th scope="col" className="px-4 py-3 text-left text-sm font-medium text-gray-600">건수</th>
|
||||
<th scope="col" className="px-4 py-3 text-left text-sm font-medium text-gray-600">에러</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user