Download src/lib/adminControlPlane.ts from Esketch/CarboAny: direct link, hf CLI and curl.
- Browser
- Download file 7.66 kB
-
https://huggingface.co/spaces/Esketch/CarboAny/resolve/main/src/lib/adminControlPlane.ts
- Command line
-
hf download hf://spaces/Esketch/CarboAny/src/lib/adminControlPlane.ts
-
curl -L -o adminControlPlane.ts https://huggingface.co/spaces/Esketch/CarboAny/resolve/main/src/lib/adminControlPlane.ts
7.66 kB
| import type { AdminProgramSummary } from './admin'; | |
| import type { Program } from '../types'; | |
| export type AdminControlStatus = 'ready' | 'needs_admin' | 'blocked' | 'live' | 'locked'; | |
| export interface AdminControlPlaneStep { | |
| key: 'sponsor' | 'app' | 'evidence' | 'methodology' | 'sme' | 'settlement' | 'report'; | |
| label: string; | |
| status: AdminControlStatus; | |
| owner: string; | |
| reason: string; | |
| action: string; | |
| route: string; | |
| } | |
| export interface AdminControlPlaneSnapshot { | |
| campaignId: string; | |
| title: string; | |
| source: 'db' | 'demo' | 'fallback'; | |
| nextAction: string; | |
| nextRoute: string; | |
| severity: 'ok' | 'watch' | 'blocker'; | |
| blockingReasons: string[]; | |
| steps: AdminControlPlaneStep[]; | |
| } | |
| const statusRank: Record<AdminControlStatus, number> = { | |
| blocked: 0, | |
| needs_admin: 1, | |
| ready: 2, | |
| live: 3, | |
| locked: 4, | |
| }; | |
| export const controlStatusLabel: Record<AdminControlStatus, string> = { | |
| ready: '์ค๋น', | |
| needs_admin: 'ํ์ธ', | |
| blocked: '์ฐจ๋จ', | |
| live: '์ด์', | |
| locked: '์ ๊ธ', | |
| }; | |
| export const controlStatusClass: Record<AdminControlStatus, string> = { | |
| ready: 'border-status-info text-status-info bg-paper-white', | |
| needs_admin: 'border-status-warning text-status-warning bg-paper-cream', | |
| blocked: 'border-status-error text-status-error bg-paper-white', | |
| live: 'border-accent-teal text-accent-teal bg-paper-white', | |
| locked: 'border-ink-black text-ink-black bg-paper-gray', | |
| }; | |
| function classifySeverity(steps: AdminControlPlaneStep[]): AdminControlPlaneSnapshot['severity'] { | |
| if (steps.some((step) => step.status === 'blocked')) return 'blocker'; | |
| if (steps.some((step) => step.status === 'needs_admin')) return 'watch'; | |
| return 'ok'; | |
| } | |
| function firstAction(steps: AdminControlPlaneStep[]) { | |
| return [...steps].sort((a, b) => statusRank[a.status] - statusRank[b.status])[0]; | |
| } | |
| function buildSnapshot(input: { | |
| campaignId: string; | |
| title: string; | |
| source: AdminControlPlaneSnapshot['source']; | |
| status: string; | |
| sponsorName: string; | |
| budgetKrw: number; | |
| targetCo2Kg: number; | |
| currentCo2Kg: number; | |
| totalSubmissions: number; | |
| pendingSubmissions: number; | |
| rejectedSubmissions: number; | |
| participantCount: number; | |
| methodology?: string; | |
| }): AdminControlPlaneSnapshot { | |
| const progressPct = input.targetCo2Kg > 0 ? (input.currentCo2Kg / input.targetCo2Kg) * 100 : 0; | |
| const approvedSubmissions = Math.max( | |
| 0, | |
| input.totalSubmissions - input.pendingSubmissions - input.rejectedSubmissions, | |
| ); | |
| const verificationRate = input.totalSubmissions > 0 ? (approvedSubmissions / input.totalSubmissions) * 100 : 0; | |
| const hasMethodology = !!input.methodology && !/๋ฏธ๋ฑ๋ก|DB ์บ ํ์ธ/.test(input.methodology); | |
| const hasBudget = input.budgetKrw > 0; | |
| const hasAppExposure = input.status === 'active' || input.status === 'completed'; | |
| const settlementReady = input.status === 'completed' || (input.status === 'active' && progressPct >= 80); | |
| const reportReady = hasMethodology && approvedSubmissions > 0 && settlementReady; | |
| const smeReady = hasBudget && (input.status === 'active' || input.status === 'completed'); | |
| const steps: AdminControlPlaneStep[] = [ | |
| { | |
| key: 'sponsor', | |
| label: 'Sponsor', | |
| status: input.sponsorName && hasBudget ? 'ready' : 'needs_admin', | |
| owner: 'admin', | |
| reason: hasBudget ? `${input.sponsorName} ์์ฐ ๊ตฌ์กฐ ํ์ธ` : '์คํฐ์ ์์ฐ/๋ด๋น์ ํ์ธ ํ์', | |
| action: hasBudget ? '์คํฐ์ ์ด์ ํ ํ์ธ' : '์์ฐ๊ณผ ๋ด๋น์ ๋ณด์', | |
| route: `/admin/programs/${input.campaignId}`, | |
| }, | |
| { | |
| key: 'app', | |
| label: 'App', | |
| status: hasAppExposure ? 'live' : input.status === 'draft' ? 'blocked' : 'needs_admin', | |
| owner: 'operations', | |
| reason: hasAppExposure ? '์ฌ์ฉ์์ฑ ๋ ธ์ถ ๊ฐ๋ฅ ์ํ' : '์ฑ ๋ ธ์ถ ์ ์น์ธ/๋ฏธ์ ์ ๊ฒ ํ์', | |
| action: hasAppExposure ? '์ฑ ๋ ธ์ถ ๋ฌธ๊ตฌ ์ ๊ฒ' : '๋ฏธ์ /์ฆ๋น ์กฐ๊ฑด ํ์ธ', | |
| route: `/admin/programs/${input.campaignId}`, | |
| }, | |
| { | |
| key: 'evidence', | |
| label: 'Evidence', | |
| status: input.pendingSubmissions > 0 ? 'needs_admin' : input.totalSubmissions > 0 ? 'ready' : 'blocked', | |
| owner: 'verifier', | |
| reason: | |
| input.totalSubmissions === 0 | |
| ? '์ ์ถ ๋ฐ์ดํฐ ์์' | |
| : input.pendingSubmissions > 0 | |
| ? `๊ฒ์ฆ ๋๊ธฐ ${input.pendingSubmissions.toLocaleString('ko-KR')}๊ฑด` | |
| : `์น์ธ์จ ${verificationRate.toFixed(0)}%`, | |
| action: input.pendingSubmissions > 0 ? '๊ฒ์ฆ ํ ์ฒ๋ฆฌ' : '์ฆ๋น ์ํ ํ์ธ', | |
| route: '/admin/submissions', | |
| }, | |
| { | |
| key: 'methodology', | |
| label: 'Method', | |
| status: hasMethodology ? 'locked' : 'needs_admin', | |
| owner: 'methodology', | |
| reason: hasMethodology ? '๋ฐฉ๋ฒ๋ก ๊ทผ๊ฑฐ ์ฐ๊ฒฐ๋จ' : '๋ฐฉ๋ฒ๋ก /์ ์ฑ ๋ฒ์ ์ ๊ธ ํ์', | |
| action: hasMethodology ? '์ ์ฑ ๋ฒ์ ์ํฅ ํ์ธ' : '๋ฐฉ๋ฒ๋ก ๋งค์นญ ๊ฒํ ', | |
| route: '/admin/methodology', | |
| }, | |
| { | |
| key: 'sme', | |
| label: 'SME', | |
| status: smeReady ? 'ready' : 'needs_admin', | |
| owner: 'sme manager', | |
| reason: smeReady ? 'SME ๋ฐฐ๋ถ ์ค๊ณ ๊ฐ๋ฅ' : 'SME ํ๋ณด/๋ฐฐ๋ถ ๊ทผ๊ฑฐ ํ์ธ ํ์', | |
| action: 'SME ์ ์ ๊ทผ๊ฑฐ ํ์ธ', | |
| route: '/admin/sme', | |
| }, | |
| { | |
| key: 'settlement', | |
| label: 'Settlement', | |
| status: settlementReady ? 'ready' : input.totalSubmissions > 0 ? 'needs_admin' : 'blocked', | |
| owner: 'finance', | |
| reason: settlementReady ? '์ ์ฐ ์ ๊ธ ์ค๋น' : '๊ฒ์ฆ ์๋ฃ ์์น ๋ถ์กฑ', | |
| action: settlementReady ? '์ ์ฐ ๋์กฐ' : '๊ฒ์ฆ/์ฐธ์ฌ ์์น ๋ณด๊ฐ', | |
| route: '/admin/settlement', | |
| }, | |
| { | |
| key: 'report', | |
| label: 'Report', | |
| status: reportReady ? 'ready' : 'needs_admin', | |
| owner: 'report owner', | |
| reason: reportReady ? '๋ณด๊ณ readiness ์ค๋น' : '๋ฐฉ๋ฒ๋ก ยท๊ฒ์ฆยท์ ์ฐ ์ค ๋๋ฝ ์์', | |
| action: reportReady ? '๋ณด๊ณ ์ readiness ํ์ธ' : '๋ณด๊ณ ์ฐจ๋จ ์ฌ์ ์ ๋ฆฌ', | |
| route: `/admin/programs/${input.campaignId}`, | |
| }, | |
| ]; | |
| const severity = classifySeverity(steps); | |
| const next = firstAction(steps); | |
| return { | |
| campaignId: input.campaignId, | |
| title: input.title, | |
| source: input.source, | |
| nextAction: next.action, | |
| nextRoute: next.route, | |
| severity, | |
| blockingReasons: steps | |
| .filter((step) => step.status === 'blocked' || step.status === 'needs_admin') | |
| .map((step) => `${step.label}: ${step.reason}`), | |
| steps, | |
| }; | |
| } | |
| export function deriveAdminControlPlaneFromSummary(program: AdminProgramSummary): AdminControlPlaneSnapshot { | |
| return buildSnapshot({ | |
| campaignId: program.id, | |
| title: program.title, | |
| source: program.source, | |
| status: program.status, | |
| sponsorName: program.sponsorName, | |
| budgetKrw: program.budgetKrw, | |
| targetCo2Kg: program.targetCo2Kg, | |
| currentCo2Kg: program.currentCo2Kg, | |
| totalSubmissions: program.totalSubmissions, | |
| pendingSubmissions: program.pendingSubmissions, | |
| rejectedSubmissions: 0, | |
| participantCount: program.participantCount, | |
| }); | |
| } | |
| export function deriveAdminControlPlaneFromProgram(program: Program): AdminControlPlaneSnapshot { | |
| return buildSnapshot({ | |
| campaignId: program.id, | |
| title: program.title, | |
| source: 'fallback', | |
| status: program.status, | |
| sponsorName: program.organizationName, | |
| budgetKrw: program.budgetKRW, | |
| targetCo2Kg: program.co2ReductionKg, | |
| currentCo2Kg: program.co2ReductionKg, | |
| totalSubmissions: program.totalVerifications, | |
| pendingSubmissions: program.pendingVerifications, | |
| rejectedSubmissions: program.rejectedVerifications, | |
| participantCount: program.currentParticipants, | |
| methodology: program.methodology, | |
| }); | |
| } | |