| |
| |
|
|
| export type AuditResult = 'pass' | 'fail' | 'minor_issue'; |
| export type IssueStatus = 'open' | 'fixed' | 'verified'; |
|
|
| export interface AuditLog { |
| id: string; |
| scenarioId: string; |
| scenarioTitle: string; |
| timestamp: string; |
| performer: string; |
| result: AuditResult; |
| notes: string; |
| relatedIssueId?: string; |
| } |
|
|
| export interface SystemIssue { |
| id: string; |
| title: string; |
| description: string; |
| severity: 'high' | 'medium' | 'low'; |
| status: IssueStatus; |
| domain: string; |
| createdAt: string; |
| resolvedAt?: string; |
| } |
|
|
| const LOG_KEY = 'carboany_audit_logs'; |
| const ISSUE_KEY = 'carboany_system_issues'; |
|
|
| |
|
|
| export function getAuditLogs(): AuditLog[] { |
| const raw = localStorage.getItem(LOG_KEY); |
| if (!raw) return []; |
| const logs = JSON.parse(raw) as AuditLog[]; |
|
|
| |
| const seen = new Set<string>(); |
| let mutated = false; |
| for (const log of logs) { |
| if (!log.id || seen.has(log.id)) { |
| log.id = newId('log'); |
| mutated = true; |
| } |
| seen.add(log.id); |
| } |
| if (mutated) { |
| localStorage.setItem(LOG_KEY, JSON.stringify(logs)); |
| } |
| return logs; |
| } |
|
|
| function newId(prefix: string): string { |
| |
| if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { |
| return `${prefix}-${crypto.randomUUID()}`; |
| } |
| return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; |
| } |
|
|
| export function recordAudit(log: Omit<AuditLog, 'id' | 'timestamp'>): AuditLog { |
| const logs = getAuditLogs(); |
| const newLog: AuditLog = { |
| ...log, |
| id: newId('log'), |
| timestamp: new Date().toISOString(), |
| }; |
| logs.unshift(newLog); |
| localStorage.setItem(LOG_KEY, JSON.stringify(logs)); |
| return newLog; |
| } |
|
|
| |
|
|
| export function getSystemIssues(): SystemIssue[] { |
| const raw = localStorage.getItem(ISSUE_KEY); |
| return raw ? JSON.parse(raw) : []; |
| } |
|
|
| export function reportIssue(issue: Omit<SystemIssue, 'id' | 'createdAt' | 'status'>): SystemIssue { |
| const issues = getSystemIssues(); |
| const newIssue: SystemIssue = { |
| ...issue, |
| id: newId('issue'), |
| status: 'open', |
| createdAt: new Date().toISOString(), |
| }; |
| issues.unshift(newIssue); |
| localStorage.setItem(ISSUE_KEY, JSON.stringify(issues)); |
| return newIssue; |
| } |
|
|
| export function updateIssueStatus(id: string, status: IssueStatus): void { |
| const issues = getSystemIssues(); |
| const idx = issues.findIndex(i => i.id === id); |
| if (idx >= 0) { |
| issues[idx].status = status; |
| if (status === 'fixed' || status === 'verified') { |
| issues[idx].resolvedAt = new Date().toISOString(); |
| } |
| localStorage.setItem(ISSUE_KEY, JSON.stringify(issues)); |
| } |
| } |
|
|
| |
| export function seedPreviousHistory() { |
| if (getAuditLogs().length > 0) return; |
|
|
| const prevTasks = [ |
| { sid: 'p-01', title: '์ฌ์ฉ์ ์จ๋ณด๋ฉ', result: 'pass' as const, notes: 'AuthContext ๋ด ์๋ ์ฐธ์ฌ ๋ก์ง ํ์ธ ์๋ฃ' }, |
| { sid: 'a-05', title: '๊ด๋ฆฌ์ ๋์๋ณด๋ ์ค๋ฐ์ดํฐ', result: 'pass' as const, notes: 'Stage 1-3 LIVE ์ฐ๋ ํ์ธ' }, |
| { sid: 's-03', title: 'ESG ๋ณด๊ณ ์ โ Scope3 ์ฐ๋', result: 'pass' as const, notes: 'SME ๊ธฐ๋ถ ์ค์ ์ค๋ฒ๋ ์ด ์ฑ๊ณต' }, |
| ]; |
|
|
| prevTasks.forEach(t => { |
| recordAudit({ |
| scenarioId: t.sid, |
| scenarioTitle: t.title, |
| performer: 'Gemini CLI', |
| result: t.result, |
| notes: t.notes |
| }); |
| }); |
| } |
|
|