Download src/lib/admin.ts from Esketch/CarboAny: direct link, hf CLI and curl.
- Browser
- Download file 7.73 kB
-
https://huggingface.co/spaces/Esketch/CarboAny/resolve/main/src/lib/admin.ts
- Command line
-
hf download hf://spaces/Esketch/CarboAny/src/lib/admin.ts
-
curl -L -o admin.ts https://huggingface.co/spaces/Esketch/CarboAny/resolve/main/src/lib/admin.ts
7.73 kB
| import { supabase } from './supabase'; | |
| import type { CampaignStatus } from './database.types'; | |
| import type { Program } from '../types'; | |
| import { mockPrograms, resolveMockProgramId } from '../data/mockPrograms'; | |
| export interface AdminProgramSummary { | |
| id: string; | |
| title: string; | |
| sponsorName: string; | |
| status: string; | |
| startDate: string; | |
| endDate?: string; | |
| budgetKrw: number; | |
| targetCo2Kg: number; | |
| currentCo2Kg: number; | |
| totalSubmissions: number; | |
| pendingSubmissions: number; | |
| participantCount: number; | |
| achievementRate: number; | |
| source: 'db' | 'demo'; | |
| } | |
| export interface AdminSettlementStat { | |
| asset: 'KRW' | 'POINT' | 'TON'; | |
| category: string; | |
| totalAmount: number; | |
| txCount: number; | |
| lastTxAt: string; | |
| } | |
| function toSettlementAsset(value: unknown): AdminSettlementStat['asset'] { | |
| return value === 'POINT' || value === 'TON' ? value : 'KRW'; | |
| } | |
| function isMissingSchemaError(error: { code?: string } | null) { | |
| return error?.code === 'PGRST205' || error?.code === '42P01'; | |
| } | |
| function normalizeStatus(value: unknown): CampaignStatus { | |
| if ( | |
| value === 'draft' || | |
| value === 'pending_review' || | |
| value === 'active' || | |
| value === 'paused' || | |
| value === 'completed' | |
| ) { | |
| return value; | |
| } | |
| return 'draft'; | |
| } | |
| function inferOrganizationType( | |
| sponsorName: string | |
| ): Program['organizationType'] { | |
| if (/๊ตฐ์ฒญ|์์ฒญ|๋์ฒญ|๊ตฌ์ฒญ|ํน๋ณ์|๊ด์ญ์/.test(sponsorName)) return 'municipality'; | |
| if (/๊ณต์ฌ|๊ณต๋จ|์ฌ๋จ|๊ณต๊ณต|์ ๋ถ|๋ถ์ฒ|์ฒญ$/.test(sponsorName)) return 'public_corp'; | |
| return 'enterprise'; | |
| } | |
| function dbSummaryToProgram(row: AdminProgramSummary): Program { | |
| const approvedVerifications = Math.max( | |
| 0, | |
| row.totalSubmissions - row.pendingSubmissions | |
| ); | |
| return { | |
| id: row.id, | |
| title: row.title, | |
| programType: inferOrganizationType(row.sponsorName) === 'enterprise' | |
| ? 'corporate_event' | |
| : 'public_caas', | |
| organizationName: row.sponsorName || '๋ฏธ๋ฑ๋ก ์คํฐ์', | |
| organizationType: inferOrganizationType(row.sponsorName || ''), | |
| status: normalizeStatus(row.status), | |
| startDate: row.startDate, | |
| endDate: row.endDate || '์งํ ์ค', | |
| targetParticipants: row.participantCount, | |
| currentParticipants: row.participantCount, | |
| totalVerifications: row.totalSubmissions, | |
| approvedVerifications, | |
| rejectedVerifications: 0, | |
| pendingVerifications: row.pendingSubmissions, | |
| co2ReductionKg: row.currentCo2Kg, | |
| budgetKRW: row.budgetKrw, | |
| pointsDistributed: 0, | |
| methodology: 'DB ์บ ํ์ธ โ ์์ธ ๋ฐฉ๋ฒ๋ก ์ ๋ณด ๋ฏธ๋ฑ๋ก', | |
| methodologyAutoAssigned: false, | |
| apiCallsTotal: row.totalSubmissions, | |
| apiCallsToday: 0, | |
| fraudDetected: 0, | |
| verifications: [], | |
| }; | |
| } | |
| function simulatedAdminPrograms(): AdminProgramSummary[] { | |
| return mockPrograms.map(p => ({ | |
| id: p.id, | |
| title: p.title, | |
| sponsorName: p.organizationName, | |
| status: p.status, | |
| startDate: p.startDate, | |
| endDate: p.endDate, | |
| budgetKrw: p.budgetKRW || 0, | |
| targetCo2Kg: p.co2ReductionKg || 0, | |
| currentCo2Kg: p.co2ReductionKg || 0, | |
| totalSubmissions: p.totalVerifications || 0, | |
| pendingSubmissions: p.pendingVerifications || 0, | |
| participantCount: p.currentParticipants || 0, | |
| achievementRate: 0, | |
| source: 'demo', | |
| })); | |
| } | |
| function mapAdminProgramRow(row: Record<string, unknown>): AdminProgramSummary { | |
| const campaignId = String(row.campaign_id ?? ''); | |
| return { | |
| id: resolveMockProgramId(campaignId) ?? campaignId, | |
| title: String(row.title ?? '์ ๋ชฉ ๋ฏธ๋ฑ๋ก ์บ ํ์ธ'), | |
| sponsorName: String(row.sponsor_name ?? '๋ฏธ๋ฑ๋ก ์คํฐ์'), | |
| status: String(row.status ?? 'draft'), | |
| startDate: String(row.start_date ?? ''), | |
| endDate: row.end_date ? String(row.end_date) : undefined, | |
| budgetKrw: Number(row.budget_krw ?? 0), | |
| targetCo2Kg: Number(row.target_co2_kg ?? 0), | |
| currentCo2Kg: Number(row.current_co2_kg ?? 0), | |
| totalSubmissions: Number(row.total_submissions ?? 0), | |
| pendingSubmissions: Number(row.pending_submissions ?? 0), | |
| participantCount: Number(row.participant_count ?? 0), | |
| achievementRate: Number(row.achievement_rate ?? 0), | |
| source: 'db', | |
| }; | |
| } | |
| /** | |
| * ์ ์ฒด ํ๋ก๊ทธ๋จ(์บ ํ์ธ) ๋ชฉ๋ก ๋ฐ ์์ฝ ์ ๋ณด ์กฐํ | |
| */ | |
| export async function getAdminPrograms(): Promise<AdminProgramSummary[]> { | |
| const { data, error } = await supabase | |
| .from('admin_program_summary') | |
| .select('*') | |
| .order('start_date', { ascending: false }); | |
| if (error) { | |
| if (!isMissingSchemaError(error)) console.error('getAdminPrograms error:', error); | |
| return simulatedAdminPrograms(); | |
| } | |
| const dbPrograms = (data || []).map(mapAdminProgramRow); | |
| // Mock ๋ฐ์ดํฐ์ ๋ณํฉ (์๋๊ตฐ ํ๋ก์ ํธ ๊ฐ์ ๋ ธ์ถ) | |
| const dbProgramIds = new Set(dbPrograms.map((program) => program.id)); | |
| return [ | |
| ...dbPrograms, | |
| ...simulatedAdminPrograms().filter((program) => !dbProgramIds.has(program.id)), | |
| ]; | |
| } | |
| export async function getAdminProgram(id: string): Promise<Program | null> { | |
| const canonicalId = resolveMockProgramId(id) ?? id; | |
| const mockProgram = mockPrograms.find((program) => program.id === canonicalId); | |
| if (mockProgram) return mockProgram; | |
| const { data, error } = await supabase | |
| .from('admin_program_summary') | |
| .select('*') | |
| .eq('campaign_id', id) | |
| .maybeSingle(); | |
| if (error) { | |
| if (!isMissingSchemaError(error)) console.error('getAdminProgram error:', error); | |
| return null; | |
| } | |
| return data ? dbSummaryToProgram(mapAdminProgramRow(data)) : null; | |
| } | |
| /** | |
| * ์ ์ญ ์ ์ฐ ํํฉ ๋ฐ์ดํฐ ์กฐํ | |
| */ | |
| export async function getGlobalSettlementStats(): Promise<AdminSettlementStat[]> { | |
| const { data, error } = await supabase | |
| .from('admin_global_settlement_stats') | |
| .select('*'); | |
| if (error) { | |
| if (!isMissingSchemaError(error)) console.error('getGlobalSettlementStats error:', error); | |
| return []; | |
| } | |
| return (data || []).map((row) => ({ | |
| asset: toSettlementAsset(row.asset), | |
| category: row.category, | |
| totalAmount: Number(row.total_amount), | |
| txCount: row.tx_count, | |
| lastTxAt: row.last_tx_at, | |
| })); | |
| } | |
| export interface AdminVerificationProject { | |
| id: string; | |
| title: string; | |
| sponsorName: string; | |
| registry: string; | |
| methodology: string; | |
| reductionTons: number; | |
| stage: 'pending' | 'submitted' | 'reviewing' | 'approved' | 'rejected'; | |
| verifier?: string; | |
| slaRemainingDays?: number; | |
| lastUpdatedAt: string; | |
| } | |
| /** | |
| * ์ธ๋ถ ๋ ์ง์คํธ๋ฆฌ(VVB) ๊ฒ์ฆ ํ๋ก์ ํธ ๋ชฉ๋ก ์กฐํ | |
| */ | |
| export async function getVerificationProjects(): Promise<AdminVerificationProject[]> { | |
| // ์ค์ ๋ก๋ external_registry_mapping ํ ์ด๋ธ ๋ฑ๊ณผ ์กฐ์ธ ํ์ | |
| // ์ฐ์ ์ ์บ ํ์ธ ๋ชฉ๋ก์ ๊ธฐ๋ฐ์ผ๋ก ๊ฒ์ฆ ์ํ๋ฅผ ์๋ฎฌ๋ ์ด์ ํ์ฌ ๋ฐํ (์ถํ ํ ์ด๋ธ ํ์ ์ ์์ ) | |
| const { data, error } = await supabase | |
| .from('admin_program_summary') | |
| .select('*'); | |
| if (error) return []; | |
| return (data || []).map((row) => ({ | |
| id: row.campaign_id, | |
| title: row.title, | |
| sponsorName: row.sponsor_name, | |
| registry: 'VCS', // ์์ ๋งคํ | |
| methodology: 'VM0042', // ์์ ๋งคํ | |
| reductionTons: Number(row.current_co2_kg) / 1000, | |
| stage: row.status === 'completed' ? 'approved' : 'reviewing', | |
| verifier: 'KQA (ํ๊ตญํ์ง์ฌ๋จ)', | |
| slaRemainingDays: 14, | |
| lastUpdatedAt: new Date().toISOString(), | |
| })); | |
| } | |
| /** | |
| * ํน์ ์บ ํ์ธ์ ๊ฒ์ฆ ํต๊ณ ์กฐํ | |
| */ | |
| export async function getVerificationStats(campaignId?: string) { | |
| let query = supabase.from('admin_verification_stats').select('*'); | |
| if (campaignId) query = query.eq('campaign_id', campaignId); | |
| const { data, error } = await query; | |
| if (error) { | |
| console.error('getVerificationStats error:', error); | |
| return []; | |
| } | |
| return data; | |
| } | |