import { useEffect, useState } from 'react' import { View, Text, ScrollView, StyleSheet, TouchableOpacity, RefreshControl, ActivityIndicator } from 'react-native' import { COLORS, PRIORITY_COLOR } from '../../constants/Config' import { getDashboard, getLicenseStatus } from '../../services/api' import { useAuth } from '../../hooks/useAuth' import LineIcon from '../../components/LineIcon' interface Stats { total_tasks: number open_tasks: number in_progress_tasks: number completed_today: number critical_count: number pending_approvals: number recent_tasks?: any[] } type IconName = Parameters[0]['name'] /* Variant 스타일 StatCard — screenshot9 패턴 */ function StatCard({ iconName, label, value, color }: { iconName: IconName; label: string; value: number | string; color: string }) { return ( {/* 아이콘 박스 — 연파랑 컨테이너 */} {/* 라벨 (위) */} {label} {/* 수치 (아래) */} {value} ) } export default function Dashboard() { const { user } = useAuth() const [stats, setStats] = useState(null) const [license, setLicense] = useState(null) const [loading, setLoading] = useState(true) const [refresh, setRefresh] = useState(false) const load = async (isRefresh = false) => { if (isRefresh) setRefresh(true) else setLoading(true) try { const [d, l] = await Promise.allSettled([getDashboard(), getLicenseStatus()]) if (d.status === 'fulfilled') setStats(d.value.data) if (l.status === 'fulfilled') setLicense(l.value.data) } catch {} setLoading(false) setRefresh(false) } useEffect(() => { load() }, []) if (loading) return ( 로딩 중... ) return ( load(true)} />} > {/* 인사말 */} 안녕하세요, {user?.display_name ?? user?.username}님 👋 GUARDiA ITSM 현황을 확인하세요 {/* 라이선스 배너 */} {license?.upgrade_banner?.show && ( ⚠️ {license.upgrade_banner.message} )} {license?.valid && ( {license.edition} {license.days_remaining}일 남음 )} {/* 통계 카드 */} {/* 최근 SR */} {stats?.recent_tasks && stats.recent_tasks.length > 0 && ( 최근 서비스 요청 {stats.recent_tasks.slice(0, 5).map((sr: any) => ( {sr.title} {sr.sr_id} · {sr.status} ))} )} {/* 빠른 실행 */} 빠른 실행 {[ { iconName: 'sr' as const, label: 'SR 등록' }, { iconName: 'ai' as const, label: 'AI 질문' }, { iconName: 'dashboard'as const, label: '리포트' }, { iconName: 'lock' as const, label: '감사로그' }, ].map(q => ( {q.label} ))} ) } const s = StyleSheet.create({ scroll: { flex: 1, backgroundColor: COLORS.bg }, center: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: COLORS.bg }, /* 헤더 — Variant 딥네이비 그라디언트 */ header: { backgroundColor: '#001530', padding: 24, paddingTop: 20, borderBottomLeftRadius: 20, borderBottomRightRadius: 20, }, greeting: { fontSize: 20, fontWeight: '800', color: '#fff', letterSpacing: -0.5 }, subGreet: { fontSize: 13, color: 'rgba(0,160,200,.85)', marginTop: 4 }, licenseBanner: { backgroundColor: '#fff3cd', padding: 12, marginHorizontal: 16, marginTop: 12, borderRadius: 10, borderLeftWidth: 3, borderLeftColor: COLORS.warning }, licenseBannerText: { fontSize: 12, color: '#856404' }, licenseBar: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', backgroundColor: COLORS.light, marginHorizontal: 16, marginTop: 10, paddingHorizontal: 14, paddingVertical: 8, borderRadius: 10, borderWidth: 1, borderColor: 'rgba(0,160,200,.2)', }, licenseEdition: { fontSize: 12, fontWeight: '700', color: COLORS.accent }, licenseDays: { fontSize: 12, color: COLORS.muted }, /* 통계 그리드 */ statsGrid: { flexDirection: 'row', flexWrap: 'wrap', padding: 14, gap: 10 }, /* Variant StatCard — 상단 컬러 바 + 아이콘 박스 */ statCard: { width: '47%', backgroundColor: '#fff', borderRadius: 12, padding: 14, shadowColor: '#003366', shadowOffset: { width: 0, height: 2 }, shadowOpacity: .08, shadowRadius: 8, elevation: 3, }, statIconBox: { width: 40, height: 40, borderRadius: 10, marginBottom: 10, alignItems: 'center', justifyContent: 'center' }, statIcon: { fontSize: 20 }, statValue: { fontSize: 28, fontWeight: '900', letterSpacing: -0.5 }, statLabel: { fontSize: 11, fontWeight: '600', color: COLORS.muted, letterSpacing: 0.3, textTransform: 'uppercase', marginBottom: 4 }, /* 섹션 */ section: { backgroundColor: '#fff', marginHorizontal: 16, marginTop: 12, borderRadius: 14, padding: 16, shadowColor: '#003366', shadowOffset: { width: 0, height: 2 }, shadowOpacity: .05, shadowRadius: 6, elevation: 2, }, sectionTitle: { fontSize: 14, fontWeight: '800', color: COLORS.primary, marginBottom: 12, letterSpacing: -0.3, }, srItem: { flexDirection: 'row', alignItems: 'center', gap: 10, paddingVertical: 9, borderBottomWidth: 1, borderBottomColor: '#f1f5f9' }, priorityDot: { width: 8, height: 8, borderRadius: 4 }, srTitle: { fontSize: 13, fontWeight: '600', color: COLORS.text }, srMeta: { fontSize: 11, color: COLORS.muted, marginTop: 2 }, /* 빠른 실행 */ quickRow: { flexDirection: 'row', justifyContent: 'space-around' }, quickBtn: { alignItems: 'center', padding: 12, backgroundColor: COLORS.bg, borderRadius: 12, flex: 1, marginHorizontal: 3, }, quickIconBox: { width: 36, height: 36, borderRadius: 10, marginBottom: 5, backgroundColor: 'rgba(0,160,200,.1)', alignItems: 'center', justifyContent: 'center' }, quickIcon: { fontSize: 26, marginBottom: 5 }, quickLabel: { fontSize: 11, fontWeight: '600', color: COLORS.primary }, })