Merge pull request 'fix: S&P Collector 다크모드 미적용 및 라벨 디자인 통일 (#122)' (#128) from feature/ISSUE-122-collector-feedback into develop

This commit is contained in:
HYOJIN 2026-03-31 15:57:01 +09:00
커밋 bfd86c9eff
5개의 변경된 파일63개의 추가작업 그리고 57개의 파일을 삭제

파일 보기

@ -10,6 +10,12 @@
- 섹션 간 직접 이동 - 섹션 간 직접 이동
- 메인화면 카드 높이 동기화 (CSS Grid) - 메인화면 카드 높이 동기화 (CSS Grid)
### 수정
- S&P Collector 다크모드 미적용 및 라벨 디자인 통일 (#122)
- 실행이력상세/재수집이력상세 API 호출 로그 다크모드 적용
- 개별 호출 로그 필터/테이블 다크모드 적용
- 작업관리 스케줄 라벨 rounded-full 디자인 통일
## [2026-03-31] ## [2026-03-31]
### 추가 ### 추가

파일 보기

@ -71,11 +71,11 @@ export default function ApiLogSection({ stepExecutionId, summary }: ApiLogSectio
className={`px-2.5 py-1 text-xs rounded-full font-medium transition-colors ${ className={`px-2.5 py-1 text-xs rounded-full font-medium transition-colors ${
status === key status === key
? key === 'ERROR' ? key === 'ERROR'
? 'bg-red-100 text-red-700' ? 'bg-red-500/15 text-red-500'
: key === 'SUCCESS' : key === 'SUCCESS'
? 'bg-emerald-100 text-emerald-700' ? 'bg-emerald-500/15 text-emerald-500'
: 'bg-blue-100 text-blue-700' : 'bg-blue-500/15 text-blue-500'
: 'bg-gray-100 text-gray-500 hover:bg-gray-200' : 'bg-wing-card text-wing-muted hover:bg-wing-hover'
}`} }`}
> >
{label} ({count.toLocaleString()}) {label} ({count.toLocaleString()})
@ -92,7 +92,7 @@ export default function ApiLogSection({ stepExecutionId, summary }: ApiLogSectio
<> <>
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full text-xs text-left"> <table className="w-full text-xs text-left">
<thead className="bg-blue-100 text-blue-700 sticky top-0"> <thead className="bg-blue-500/15 text-blue-500 sticky top-0">
<tr> <tr>
<th className="px-2 py-1.5 font-medium">#</th> <th className="px-2 py-1.5 font-medium">#</th>
<th className="px-2 py-1.5 font-medium">URI</th> <th className="px-2 py-1.5 font-medium">URI</th>
@ -104,24 +104,24 @@ export default function ApiLogSection({ stepExecutionId, summary }: ApiLogSectio
<th className="px-2 py-1.5 font-medium"></th> <th className="px-2 py-1.5 font-medium"></th>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-blue-100"> <tbody className="divide-y divide-blue-500/15">
{logData.content.map((log, idx) => { {logData.content.map((log, idx) => {
const isError = (log.statusCode != null && log.statusCode >= 400) || log.errorMessage; const isError = (log.statusCode != null && log.statusCode >= 400) || log.errorMessage;
return ( return (
<tr <tr
key={log.logId} key={log.logId}
className={isError ? 'bg-red-50' : 'bg-white hover:bg-blue-50'} className={isError ? 'bg-red-500/10' : 'bg-wing-surface hover:bg-blue-500/10'}
> >
<td className="px-2 py-1.5 text-blue-500">{page * 10 + idx + 1}</td> <td className="px-2 py-1.5 text-blue-500">{page * 10 + idx + 1}</td>
<td className="px-2 py-1.5 max-w-[200px]"> <td className="px-2 py-1.5 max-w-[200px]">
<div className="flex items-center gap-0.5"> <div className="flex items-center gap-0.5">
<span className="font-mono text-blue-900 truncate" title={log.requestUri}> <span className="font-mono text-wing-text truncate" title={log.requestUri}>
{log.requestUri} {log.requestUri}
</span> </span>
<CopyButton text={log.requestUri} /> <CopyButton text={log.requestUri} />
</div> </div>
</td> </td>
<td className="px-2 py-1.5 font-semibold text-blue-900">{log.httpMethod}</td> <td className="px-2 py-1.5 font-semibold text-wing-text">{log.httpMethod}</td>
<td className="px-2 py-1.5"> <td className="px-2 py-1.5">
<span className={`font-semibold ${ <span className={`font-semibold ${
log.statusCode == null ? 'text-gray-400' log.statusCode == null ? 'text-gray-400'
@ -132,10 +132,10 @@ export default function ApiLogSection({ stepExecutionId, summary }: ApiLogSectio
{log.statusCode ?? '-'} {log.statusCode ?? '-'}
</span> </span>
</td> </td>
<td className="px-2 py-1.5 text-right text-blue-900"> <td className="px-2 py-1.5 text-right text-wing-text">
{log.responseTimeMs?.toLocaleString() ?? '-'} {log.responseTimeMs?.toLocaleString() ?? '-'}
</td> </td>
<td className="px-2 py-1.5 text-right text-blue-900"> <td className="px-2 py-1.5 text-right text-wing-text">
{log.responseCount?.toLocaleString() ?? '-'} {log.responseCount?.toLocaleString() ?? '-'}
</td> </td>
<td className="px-2 py-1.5 text-blue-600 whitespace-nowrap"> <td className="px-2 py-1.5 text-blue-600 whitespace-nowrap">

파일 보기

@ -96,32 +96,32 @@ function StepCard({ step, jobName, jobExecutionId }: StepCardProps) {
{/* API 호출 정보: apiLogSummary가 있으면 개별 로그 리스트, 없으면 기존 apiCallInfo 요약 */} {/* API 호출 정보: apiLogSummary가 있으면 개별 로그 리스트, 없으면 기존 apiCallInfo 요약 */}
{step.apiLogSummary ? ( {step.apiLogSummary ? (
<div className="mt-4 rounded-lg bg-blue-50 border border-blue-200 p-3"> <div className="mt-4 rounded-lg bg-blue-500/10 border border-blue-500/20 p-3">
<p className="text-xs font-medium text-blue-700 mb-2">API </p> <p className="text-xs font-medium text-blue-700 mb-2">API </p>
<div className="grid grid-cols-3 sm:grid-cols-6 gap-2"> <div className="grid grid-cols-3 sm:grid-cols-6 gap-2">
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-wing-text">{step.apiLogSummary.totalCalls.toLocaleString()}</p> <p className="text-sm font-bold text-wing-text">{step.apiLogSummary.totalCalls.toLocaleString()}</p>
<p className="text-[10px] text-wing-muted"> </p> <p className="text-[10px] text-wing-muted"> </p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-emerald-600">{step.apiLogSummary.successCount.toLocaleString()}</p> <p className="text-sm font-bold text-emerald-600">{step.apiLogSummary.successCount.toLocaleString()}</p>
<p className="text-[10px] text-wing-muted"></p> <p className="text-[10px] text-wing-muted"></p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className={`text-sm font-bold ${step.apiLogSummary.errorCount > 0 ? 'text-red-500' : 'text-wing-text'}`}> <p className={`text-sm font-bold ${step.apiLogSummary.errorCount > 0 ? 'text-red-500' : 'text-wing-text'}`}>
{step.apiLogSummary.errorCount.toLocaleString()} {step.apiLogSummary.errorCount.toLocaleString()}
</p> </p>
<p className="text-[10px] text-wing-muted"></p> <p className="text-[10px] text-wing-muted"></p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-blue-600">{Math.round(step.apiLogSummary.avgResponseMs).toLocaleString()}</p> <p className="text-sm font-bold text-blue-600">{Math.round(step.apiLogSummary.avgResponseMs).toLocaleString()}</p>
<p className="text-[10px] text-wing-muted">(ms)</p> <p className="text-[10px] text-wing-muted">(ms)</p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-red-500">{step.apiLogSummary.maxResponseMs.toLocaleString()}</p> <p className="text-sm font-bold text-red-500">{step.apiLogSummary.maxResponseMs.toLocaleString()}</p>
<p className="text-[10px] text-wing-muted">(ms)</p> <p className="text-[10px] text-wing-muted">(ms)</p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-emerald-500">{step.apiLogSummary.minResponseMs.toLocaleString()}</p> <p className="text-sm font-bold text-emerald-500">{step.apiLogSummary.minResponseMs.toLocaleString()}</p>
<p className="text-[10px] text-wing-muted">(ms)</p> <p className="text-[10px] text-wing-muted">(ms)</p>
</div> </div>
@ -132,7 +132,7 @@ function StepCard({ step, jobName, jobExecutionId }: StepCardProps) {
)} )}
</div> </div>
) : step.apiCallInfo && ( ) : step.apiCallInfo && (
<div className="mt-4 rounded-lg bg-blue-50 border border-blue-200 p-3"> <div className="mt-4 rounded-lg bg-blue-500/10 border border-blue-500/20 p-3">
<p className="text-xs font-medium text-blue-700 mb-2">API </p> <p className="text-xs font-medium text-blue-700 mb-2">API </p>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-2 text-xs"> <div className="grid grid-cols-2 sm:grid-cols-4 gap-2 text-xs">
<div> <div>
@ -163,7 +163,7 @@ function StepCard({ step, jobName, jobExecutionId }: StepCardProps) {
)} )}
{step.exitMessage && ( {step.exitMessage && (
<div className="mt-4 rounded-lg bg-red-50 border border-red-200 p-3"> <div className="mt-4 rounded-lg bg-red-500/10 border border-red-500/20 p-3">
<p className="text-xs font-medium text-red-700 mb-1">Exit Message</p> <p className="text-xs font-medium text-red-700 mb-1">Exit Message</p>
<p className="text-xs text-red-600 whitespace-pre-wrap break-words"> <p className="text-xs text-red-600 whitespace-pre-wrap break-words">
{step.exitMessage} {step.exitMessage}
@ -468,7 +468,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
}; };
return ( return (
<div className="mt-4 rounded-lg bg-red-50 border border-red-200 p-3"> <div className="mt-4 rounded-lg bg-red-500/10 border border-red-500/20 p-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<button <button
onClick={() => setOpen((v) => !v)} onClick={() => setOpen((v) => !v)}
@ -522,7 +522,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<div className="mt-2"> <div className="mt-2">
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full text-xs text-left"> <table className="w-full text-xs text-left">
<thead className="bg-red-100 text-red-700"> <thead className="bg-red-500/20 text-red-700">
<tr> <tr>
<th className="px-2 py-1.5 font-medium">Record Key</th> <th className="px-2 py-1.5 font-medium">Record Key</th>
<th className="px-2 py-1.5 font-medium"> </th> <th className="px-2 py-1.5 font-medium"> </th>
@ -531,11 +531,11 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<th className="px-2 py-1.5 font-medium"> </th> <th className="px-2 py-1.5 font-medium"> </th>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-red-100"> <tbody className="divide-y divide-red-500/20">
{pagedRecords.map((record) => ( {pagedRecords.map((record) => (
<tr <tr
key={record.id} key={record.id}
className="bg-white hover:bg-red-50" className="bg-wing-surface hover:bg-red-500/10"
> >
<td className="px-2 py-1.5 font-mono text-red-900"> <td className="px-2 py-1.5 font-mono text-red-900">
{record.recordKey} {record.recordKey}
@ -581,19 +581,19 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
{/* 재수집 확인 다이얼로그 */} {/* 재수집 확인 다이얼로그 */}
{showConfirm && ( {showConfirm && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<div className="bg-white rounded-xl shadow-2xl p-6 w-full max-w-md mx-4"> <div className="bg-wing-surface rounded-xl shadow-2xl p-6 w-full max-w-md mx-4">
<h3 className="text-lg font-semibold text-wing-text mb-2"> <h3 className="text-lg font-semibold text-wing-text mb-2">
</h3> </h3>
<p className="text-sm text-wing-muted mb-3"> <p className="text-sm text-wing-muted mb-3">
{failedRecords.length} IMO에 . {failedRecords.length} IMO에 .
</p> </p>
<div className="bg-gray-50 rounded-lg p-3 mb-4 max-h-40 overflow-y-auto"> <div className="bg-wing-card rounded-lg p-3 mb-4 max-h-40 overflow-y-auto">
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{failedRecords.map((r) => ( {failedRecords.map((r) => (
<span <span
key={r.id} key={r.id}
className="inline-flex px-2 py-0.5 text-xs font-mono bg-red-100 text-red-700 rounded" className="inline-flex px-2 py-0.5 text-xs font-mono bg-red-500/20 text-red-700 rounded"
> >
{r.recordKey} {r.recordKey}
</span> </span>
@ -604,7 +604,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<button <button
onClick={() => setShowConfirm(false)} onClick={() => setShowConfirm(false)}
disabled={retrying} disabled={retrying}
className="px-4 py-2 text-sm font-medium text-wing-muted bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50" className="px-4 py-2 text-sm font-medium text-wing-muted bg-wing-card hover:bg-wing-hover rounded-lg transition-colors disabled:opacity-50"
> >
</button> </button>
@ -630,7 +630,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
{/* 일괄 RESOLVED 확인 다이얼로그 */} {/* 일괄 RESOLVED 확인 다이얼로그 */}
{showResolveConfirm && ( {showResolveConfirm && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<div className="bg-white rounded-xl shadow-2xl p-6 w-full max-w-md mx-4"> <div className="bg-wing-surface rounded-xl shadow-2xl p-6 w-full max-w-md mx-4">
<h3 className="text-lg font-semibold text-wing-text mb-2"> <h3 className="text-lg font-semibold text-wing-text mb-2">
RESOLVED RESOLVED
</h3> </h3>
@ -642,7 +642,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<button <button
onClick={() => setShowResolveConfirm(false)} onClick={() => setShowResolveConfirm(false)}
disabled={resolving} disabled={resolving}
className="px-4 py-2 text-sm font-medium text-wing-muted bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50" className="px-4 py-2 text-sm font-medium text-wing-muted bg-wing-card hover:bg-wing-hover rounded-lg transition-colors disabled:opacity-50"
> >
</button> </button>
@ -668,7 +668,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
{/* 재시도 초기화 확인 다이얼로그 */} {/* 재시도 초기화 확인 다이얼로그 */}
{showResetConfirm && ( {showResetConfirm && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<div className="bg-white rounded-xl shadow-2xl p-6 w-full max-w-md mx-4"> <div className="bg-wing-surface rounded-xl shadow-2xl p-6 w-full max-w-md mx-4">
<h3 className="text-lg font-semibold text-wing-text mb-2"> <h3 className="text-lg font-semibold text-wing-text mb-2">
</h3> </h3>
@ -680,7 +680,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<button <button
onClick={() => setShowResetConfirm(false)} onClick={() => setShowResetConfirm(false)}
disabled={resetting} disabled={resetting}
className="px-4 py-2 text-sm font-medium text-wing-muted bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50" className="px-4 py-2 text-sm font-medium text-wing-muted bg-wing-card hover:bg-wing-hover rounded-lg transition-colors disabled:opacity-50"
> >
</button> </button>

파일 보기

@ -376,11 +376,11 @@ export default function Jobs() {
<div className="flex items-center gap-2 pt-0.5"> <div className="flex items-center gap-2 pt-0.5">
{job.scheduleCron ? ( {job.scheduleCron ? (
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-emerald-100 text-emerald-700"> <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-semibold bg-emerald-500/15 text-emerald-500">
</span> </span>
) : ( ) : (
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-wing-card text-wing-muted"> <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-wing-card text-wing-muted">
</span> </span>
)} )}
@ -478,11 +478,11 @@ export default function Jobs() {
</td> </td>
<td className="px-4 py-3"> <td className="px-4 py-3">
{job.scheduleCron ? ( {job.scheduleCron ? (
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-emerald-100 text-emerald-700"> <span className="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-semibold bg-emerald-100 text-emerald-700">
</span> </span>
) : ( ) : (
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-wing-card text-wing-muted"> <span className="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-semibold bg-wing-card text-wing-muted">
</span> </span>
)} )}

파일 보기

@ -75,32 +75,32 @@ function StepCard({ step, jobName, jobExecutionId }: { step: StepExecutionDto; j
{/* API 호출 로그 요약 (batch_api_log 기반) */} {/* API 호출 로그 요약 (batch_api_log 기반) */}
{summary && ( {summary && (
<div className="mt-4 rounded-lg bg-blue-50 border border-blue-200 p-3"> <div className="mt-4 rounded-lg bg-blue-500/10 border border-blue-500/20 p-3">
<p className="text-xs font-medium text-blue-700 mb-2">API </p> <p className="text-xs font-medium text-blue-700 mb-2">API </p>
<div className="grid grid-cols-3 sm:grid-cols-6 gap-2"> <div className="grid grid-cols-3 sm:grid-cols-6 gap-2">
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-wing-text">{summary.totalCalls.toLocaleString()}</p> <p className="text-sm font-bold text-wing-text">{summary.totalCalls.toLocaleString()}</p>
<p className="text-[10px] text-wing-muted"> </p> <p className="text-[10px] text-wing-muted"> </p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-emerald-600">{summary.successCount.toLocaleString()}</p> <p className="text-sm font-bold text-emerald-600">{summary.successCount.toLocaleString()}</p>
<p className="text-[10px] text-wing-muted"></p> <p className="text-[10px] text-wing-muted"></p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className={`text-sm font-bold ${summary.errorCount > 0 ? 'text-red-500' : 'text-wing-text'}`}> <p className={`text-sm font-bold ${summary.errorCount > 0 ? 'text-red-500' : 'text-wing-text'}`}>
{summary.errorCount.toLocaleString()} {summary.errorCount.toLocaleString()}
</p> </p>
<p className="text-[10px] text-wing-muted"></p> <p className="text-[10px] text-wing-muted"></p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-blue-600">{Math.round(summary.avgResponseMs).toLocaleString()}</p> <p className="text-sm font-bold text-blue-600">{Math.round(summary.avgResponseMs).toLocaleString()}</p>
<p className="text-[10px] text-wing-muted">(ms)</p> <p className="text-[10px] text-wing-muted">(ms)</p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-red-500">{summary.maxResponseMs.toLocaleString()}</p> <p className="text-sm font-bold text-red-500">{summary.maxResponseMs.toLocaleString()}</p>
<p className="text-[10px] text-wing-muted">(ms)</p> <p className="text-[10px] text-wing-muted">(ms)</p>
</div> </div>
<div className="rounded bg-white px-2 py-1.5 text-center"> <div className="rounded bg-wing-surface px-2 py-1.5 text-center">
<p className="text-sm font-bold text-emerald-500">{summary.minResponseMs.toLocaleString()}</p> <p className="text-sm font-bold text-emerald-500">{summary.minResponseMs.toLocaleString()}</p>
<p className="text-[10px] text-wing-muted">(ms)</p> <p className="text-[10px] text-wing-muted">(ms)</p>
</div> </div>
@ -118,7 +118,7 @@ function StepCard({ step, jobName, jobExecutionId }: { step: StepExecutionDto; j
)} )}
{step.exitMessage && ( {step.exitMessage && (
<div className="mt-4 rounded-lg bg-red-50 border border-red-200 p-3"> <div className="mt-4 rounded-lg bg-red-500/10 border border-red-500/20 p-3">
<p className="text-xs font-medium text-red-700 mb-1">Exit Message</p> <p className="text-xs font-medium text-red-700 mb-1">Exit Message</p>
<p className="text-xs text-red-600 whitespace-pre-wrap break-words"> <p className="text-xs text-red-600 whitespace-pre-wrap break-words">
{step.exitMessage} {step.exitMessage}
@ -361,7 +361,7 @@ export default function RecollectDetail() {
<h2 className="text-lg font-semibold text-red-600 mb-3"> <h2 className="text-lg font-semibold text-red-600 mb-3">
</h2> </h2>
<pre className="text-sm text-wing-text font-mono bg-red-50 border border-red-200 px-4 py-3 rounded-lg whitespace-pre-wrap break-words max-h-64 overflow-y-auto"> <pre className="text-sm text-wing-text font-mono bg-red-500/10 border border-red-500/20 px-4 py-3 rounded-lg whitespace-pre-wrap break-words max-h-64 overflow-y-auto">
{history.failureReason} {history.failureReason}
</pre> </pre>
</div> </div>
@ -536,7 +536,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
}; };
return ( return (
<div className="mt-4 rounded-lg bg-red-50 border border-red-200 p-3"> <div className="mt-4 rounded-lg bg-red-500/10 border border-red-500/20 p-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<button <button
onClick={() => setOpen((v) => !v)} onClick={() => setOpen((v) => !v)}
@ -590,7 +590,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<div className="mt-2"> <div className="mt-2">
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full text-xs text-left"> <table className="w-full text-xs text-left">
<thead className="bg-red-100 text-red-700"> <thead className="bg-red-500/20 text-red-700">
<tr> <tr>
<th className="px-2 py-1.5 font-medium">Record Key</th> <th className="px-2 py-1.5 font-medium">Record Key</th>
<th className="px-2 py-1.5 font-medium"> </th> <th className="px-2 py-1.5 font-medium"> </th>
@ -599,11 +599,11 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<th className="px-2 py-1.5 font-medium"> </th> <th className="px-2 py-1.5 font-medium"> </th>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-red-100"> <tbody className="divide-y divide-red-500/20">
{pagedRecords.map((record) => ( {pagedRecords.map((record) => (
<tr <tr
key={record.id} key={record.id}
className="bg-white hover:bg-red-50" className="bg-wing-surface hover:bg-red-500/10"
> >
<td className="px-2 py-1.5 font-mono text-red-900"> <td className="px-2 py-1.5 font-mono text-red-900">
{record.recordKey} {record.recordKey}
@ -649,19 +649,19 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
{/* 재수집 확인 다이얼로그 */} {/* 재수집 확인 다이얼로그 */}
{showConfirm && ( {showConfirm && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<div className="bg-white rounded-xl shadow-2xl p-6 w-full max-w-md mx-4"> <div className="bg-wing-surface rounded-xl shadow-2xl p-6 w-full max-w-md mx-4">
<h3 className="text-lg font-semibold text-wing-text mb-2"> <h3 className="text-lg font-semibold text-wing-text mb-2">
</h3> </h3>
<p className="text-sm text-wing-muted mb-3"> <p className="text-sm text-wing-muted mb-3">
{failedRecords.length} IMO에 . {failedRecords.length} IMO에 .
</p> </p>
<div className="bg-gray-50 rounded-lg p-3 mb-4 max-h-40 overflow-y-auto"> <div className="bg-wing-card rounded-lg p-3 mb-4 max-h-40 overflow-y-auto">
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{failedRecords.map((r) => ( {failedRecords.map((r) => (
<span <span
key={r.id} key={r.id}
className="inline-flex px-2 py-0.5 text-xs font-mono bg-red-100 text-red-700 rounded" className="inline-flex px-2 py-0.5 text-xs font-mono bg-red-500/20 text-red-700 rounded"
> >
{r.recordKey} {r.recordKey}
</span> </span>
@ -672,7 +672,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<button <button
onClick={() => setShowConfirm(false)} onClick={() => setShowConfirm(false)}
disabled={retrying} disabled={retrying}
className="px-4 py-2 text-sm font-medium text-wing-muted bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50" className="px-4 py-2 text-sm font-medium text-wing-muted bg-wing-card hover:bg-wing-hover rounded-lg transition-colors disabled:opacity-50"
> >
</button> </button>
@ -698,7 +698,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
{/* 일괄 RESOLVED 확인 다이얼로그 */} {/* 일괄 RESOLVED 확인 다이얼로그 */}
{showResolveConfirm && ( {showResolveConfirm && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<div className="bg-white rounded-xl shadow-2xl p-6 w-full max-w-md mx-4"> <div className="bg-wing-surface rounded-xl shadow-2xl p-6 w-full max-w-md mx-4">
<h3 className="text-lg font-semibold text-wing-text mb-2"> <h3 className="text-lg font-semibold text-wing-text mb-2">
RESOLVED RESOLVED
</h3> </h3>
@ -710,7 +710,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<button <button
onClick={() => setShowResolveConfirm(false)} onClick={() => setShowResolveConfirm(false)}
disabled={resolving} disabled={resolving}
className="px-4 py-2 text-sm font-medium text-wing-muted bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50" className="px-4 py-2 text-sm font-medium text-wing-muted bg-wing-card hover:bg-wing-hover rounded-lg transition-colors disabled:opacity-50"
> >
</button> </button>
@ -736,7 +736,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
{/* 재시도 초기화 확인 다이얼로그 */} {/* 재시도 초기화 확인 다이얼로그 */}
{showResetConfirm && ( {showResetConfirm && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"> <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<div className="bg-white rounded-xl shadow-2xl p-6 w-full max-w-md mx-4"> <div className="bg-wing-surface rounded-xl shadow-2xl p-6 w-full max-w-md mx-4">
<h3 className="text-lg font-semibold text-wing-text mb-2"> <h3 className="text-lg font-semibold text-wing-text mb-2">
</h3> </h3>
@ -748,7 +748,7 @@ function FailedRecordsToggle({ records, jobName, jobExecutionId }: { records: Fa
<button <button
onClick={() => setShowResetConfirm(false)} onClick={() => setShowResetConfirm(false)}
disabled={resetting} disabled={resetting}
className="px-4 py-2 text-sm font-medium text-wing-muted bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50" className="px-4 py-2 text-sm font-medium text-wing-muted bg-wing-card hover:bg-wing-hover rounded-lg transition-colors disabled:opacity-50"
> >
</button> </button>