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'
interface Stats {
total_tasks: number
open_tasks: number
in_progress_tasks: number
completed_today: number
critical_count: number
pending_approvals: number
recent_tasks?: any[]
}
/* Variant 스타일 StatCard — screenshot9 패턴 */
function StatCard({ icon, label, value, color }: { icon: string; label: string; value: number | string; color: string }) {
return (
{/* 아이콘 박스 — 연파랑 컨테이너 */}
{icon}
{/* 라벨 (위) */}
{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}
))}
)}
{/* 빠른 실행 */}
⚡ 빠른 실행
{[
{ icon: '📝', label: 'SR 등록' },
{ icon: '🤖', label: 'AI 질문' },
{ icon: '📊', label: '리포트' },
{ icon: '🔒', label: '감사로그' },
].map(q => (
{q.icon}
{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,
},
quickIcon: { fontSize: 26, marginBottom: 5 },
quickLabel: { fontSize: 11, fontWeight: '600', color: COLORS.primary },
})