guardia-messenger/app/(tabs)/my_stats.tsx

111 lines
5.0 KiB
TypeScript

import React, { useState, useCallback } from 'react'
import { View, Text, ScrollView, TouchableOpacity, StyleSheet, RefreshControl } from 'react-native'
import { useFocusEffect } from 'expo-router'
import { COLORS } from '../../constants/Config'
import { getMyStats } from '../../services/api'
type Period = 'this_month' | 'last_month' | 'total'
export default function MyStatsScreen() {
const [data, setData] = useState<any>(null)
const [period, setPeriod] = useState<Period>('this_month')
const [loading, setLoading] = useState(false)
const load = useCallback(async () => {
setLoading(true)
try { const r = await getMyStats(); setData(r.data) }
catch { setData(null) }
finally { setLoading(false) }
}, [])
useFocusEffect(useCallback(() => { load() }, [load]))
const p = data?.[period] ?? data?.this_month ?? {}
const total = data?.total ?? 0
return (
<ScrollView style={s.container} refreshControl={<RefreshControl refreshing={loading} onRefresh={load} />}>
{/* 기간 탭 */}
<View style={s.tabs}>
{([['this_month','이번달'], ['last_month','지난달'], ['total','전체']] as [Period, string][]).map(([k, label]) => (
<TouchableOpacity key={k} style={[s.tab, period===k && s.tabActive]} onPress={() => setPeriod(k)}>
<Text style={[s.tabText, period===k && s.tabTextActive]}>{label}</Text>
</TouchableOpacity>
))}
</View>
{/* 수치 카드 */}
<View style={s.grid}>
<StatCard label="생성" value={period === 'total' ? total : p.created ?? 0} color={COLORS.blue} />
<StatCard label="완료" value={p.completed ?? 0} color={COLORS.success} />
<StatCard label="완료율" value={`${p.rate ?? 0}%`} color={COLORS.accent} />
</View>
{/* 가로 바 비교 */}
{data?.this_month && data?.last_month && (
<>
<Text style={s.sectionTitle}> vs </Text>
{[
{ label: '생성', a: data.this_month.created ?? 0, b: data.last_month.created ?? 0 },
{ label: '완료', a: data.this_month.completed ?? 0, b: data.last_month.completed ?? 0 },
].map(({ label, a, b }) => {
const max = Math.max(a, b, 1)
return (
<View key={label} style={s.compareRow}>
<Text style={s.compareLabel}>{label}</Text>
<View style={s.barWrap}>
<View style={[s.barFill, { width: `${(a/max)*100}%`, backgroundColor: COLORS.accent }]} />
<Text style={s.barVal}>{a}</Text>
</View>
<View style={[s.barWrap, { marginTop: 4 }]}>
<View style={[s.barFill, { width: `${(b/max)*100}%`, backgroundColor: COLORS.muted }]} />
<Text style={s.barVal}>{b}</Text>
</View>
</View>
)
})}
<View style={s.legend}>
<View style={[s.legendDot, { backgroundColor: COLORS.accent }]} /><Text style={s.legendText}></Text>
<View style={[s.legendDot, { backgroundColor: COLORS.muted, marginLeft: 12 }]} /><Text style={s.legendText}></Text>
</View>
</>
)}
</ScrollView>
)
}
function StatCard({ label, value, color }: { label: string; value: string | number; color: string }) {
return (
<View style={[c.card, { borderTopColor: color }]}>
<Text style={[c.value, { color }]}>{value}</Text>
<Text style={c.label}>{label}</Text>
</View>
)
}
const c = StyleSheet.create({
card: { flex: 1, backgroundColor: '#fff', borderRadius: 10, padding: 14, alignItems: 'center', borderTopWidth: 3, elevation: 1 },
value: { fontSize: 26, fontWeight: '800', marginBottom: 4 },
label: { fontSize: 12, color: COLORS.muted },
})
const s = StyleSheet.create({
container: { flex: 1, backgroundColor: COLORS.bg },
tabs: { flexDirection: 'row', backgroundColor: '#fff', borderBottomWidth: 1, borderBottomColor: COLORS.border },
tab: { flex: 1, paddingVertical: 12, alignItems: 'center' },
tabActive: { borderBottomWidth: 2, borderBottomColor: COLORS.accent },
tabText: { fontSize: 13, color: COLORS.muted },
tabTextActive:{ color: COLORS.accent, fontWeight: '700' },
grid: { flexDirection: 'row', gap: 8, padding: 12 },
sectionTitle: { fontSize: 15, fontWeight: '700', color: COLORS.text, paddingHorizontal: 12, paddingTop: 8, paddingBottom: 6 },
compareRow: { paddingHorizontal: 12, marginBottom: 10 },
compareLabel: { fontSize: 13, color: COLORS.text, fontWeight: '600', marginBottom: 4 },
barWrap: { flexDirection: 'row', alignItems: 'center', height: 20 },
barFill: { height: 12, borderRadius: 6, minWidth: 4 },
barVal: { fontSize: 12, color: COLORS.text, marginLeft: 6 },
legend: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12, paddingBottom: 16 },
legendDot: { width: 10, height: 10, borderRadius: 5 },
legendText: { fontSize: 12, color: COLORS.muted, marginLeft: 4 },
})