68 lines
3.0 KiB
TypeScript
68 lines
3.0 KiB
TypeScript
import React, { useState, useCallback } from 'react'
|
|
import { View, Text, ScrollView, StyleSheet, RefreshControl } from 'react-native'
|
|
import { useFocusEffect } from 'expo-router'
|
|
import { COLORS } from '../../constants/Config'
|
|
import { getKPIDashboard } from '../../services/api'
|
|
|
|
interface KPICardProps { label: string; current: number; target: number; unit?: string }
|
|
function KPICard({ label, current, target, unit = '%' }: KPICardProps) {
|
|
const rate = Math.min(100, Math.round((current / target) * 100))
|
|
const color = rate >= 100 ? COLORS.success : rate >= 80 ? COLORS.warning : COLORS.danger
|
|
return (
|
|
<View style={k.card}>
|
|
<Text style={k.label}>{label}</Text>
|
|
<Text style={[k.value, { color }]}>{current}{unit}</Text>
|
|
<Text style={k.target}>목표: {target}{unit}</Text>
|
|
<View style={k.barBg}>
|
|
<View style={[k.barFill, { width: `${rate}%`, backgroundColor: color }]} />
|
|
</View>
|
|
<Text style={[k.rate, { color }]}>달성률 {rate}%</Text>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const k = StyleSheet.create({
|
|
card: { backgroundColor: '#fff', borderRadius: 10, padding: 16, marginBottom: 10, elevation: 1 },
|
|
label: { fontSize: 13, color: COLORS.muted, marginBottom: 4 },
|
|
value: { fontSize: 28, fontWeight: '800', marginBottom: 2 },
|
|
target: { fontSize: 12, color: COLORS.muted, marginBottom: 8 },
|
|
barBg: { height: 6, backgroundColor: COLORS.border, borderRadius: 3, marginBottom: 4 },
|
|
barFill:{ height: 6, borderRadius: 3 },
|
|
rate: { fontSize: 12, fontWeight: '600' },
|
|
})
|
|
|
|
export default function KPIDashboardScreen() {
|
|
const [kpi, setKPI] = useState<any>(null)
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const load = useCallback(async () => {
|
|
setLoading(true)
|
|
try { const r = await getKPIDashboard(); setKPI(r.data) }
|
|
catch { setKPI(null) }
|
|
finally { setLoading(false) }
|
|
}, [])
|
|
|
|
useFocusEffect(useCallback(() => { load() }, [load]))
|
|
|
|
if (!kpi) return null
|
|
|
|
return (
|
|
<ScrollView style={s.container} refreshControl={<RefreshControl refreshing={loading} onRefresh={load} />} contentContainerStyle={{ padding: 12 }}>
|
|
<Text style={s.period}>{kpi.period ?? ''} KPI</Text>
|
|
<KPICard label="SR 완료율" current={kpi.sr_completion_rate ?? 0} target={kpi.targets?.sr_completion_rate ?? 90} />
|
|
<KPICard label="SLA 준수율" current={kpi.sla_compliance_rate ?? 0} target={kpi.targets?.sla_compliance_rate ?? 95} />
|
|
<KPICard label="CSAP 점수" current={kpi.csap_score ?? 0} target={kpi.targets?.csap_score ?? 85} />
|
|
<View style={s.summary}>
|
|
<Text style={s.summaryText}>전체 SR: {kpi.total_sr}건 · 완료: {kpi.completed_sr}건 · SLA위반: {kpi.sla_breach}건</Text>
|
|
</View>
|
|
</ScrollView>
|
|
)
|
|
}
|
|
|
|
const s = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: COLORS.bg },
|
|
period: { fontSize: 13, color: COLORS.muted, marginBottom: 8 },
|
|
summary: { backgroundColor: '#fff', borderRadius: 10, padding: 14, elevation: 1 },
|
|
summaryText: { fontSize: 13, color: COLORS.text },
|
|
})
|