import { useCallback, useState } from 'react' import { View, Text, ScrollView, TouchableOpacity, StyleSheet, RefreshControl } from 'react-native' import { router, useFocusEffect } from 'expo-router' import { Feather } from '@expo/vector-icons' import { useTheme } from '../../constants/ThemeContext' import { type ThemeTokens } from '../../constants/ItmsTheme' import { StatusPill, SectionTitle } from '../../components/ui' import { rowTitle, rowStatus, rowDate, reqIncidentNum } from '../../constants/rows' import { itmsProfile, itmsMyIncidents, itmsMyReports, itmsMyTodo, itmsResourceStat, listRows, type ItmsProfile, type RowMap, } from '../itmsApi' export default function Home() { const { t } = useTheme() const s = makeStyles(t) const [me, setMe] = useState(null) const [incidents, setIncidents] = useState([]) const [reports, setReports] = useState([]) const [todo, setTodo] = useState([]) const [statRows, setStatRows] = useState([]) const [refreshing, setRefreshing] = useState(false) const load = useCallback(async () => { // 각 호출 독립 — 하나 실패해도 나머지 표시(서버 미기동 시 빈 상태). try { setMe(await itmsProfile()) } catch { /* graceful */ } try { setIncidents(listRows(await itmsMyIncidents())) } catch { setIncidents([]) } try { setReports(listRows(await itmsMyReports())) } catch { setReports([]) } try { setTodo(listRows(await itmsMyTodo())) } catch { setTodo([]) } try { setStatRows(listRows(await itmsResourceStat())) } catch { setStatRows([]) } }, []) useFocusEffect(useCallback(() => { load() }, [load])) const onRefresh = useCallback(async () => { setRefreshing(true); await load(); setRefreshing(false) }, [load]) const kpis = [ { key: 'todo', label: '내 할 일', count: todo.length, color: t.brand, icon: 'check-square' as const }, { key: 'inc', label: '내 인시던트', count: incidents.length, color: t.info, icon: 'alert-circle' as const }, { key: 'rep', label: '내 보고', count: reports.length, color: t.warning, icon: 'file-text' as const }, ] const quick = [ { key: 'new', icon: 'plus-circle' as const, label: '요청 등록', go: () => router.push('/request-new') }, { key: 'req', icon: 'file-text' as const, label: '내 요청', go: () => router.push('/requests') }, { key: 'inc', icon: 'alert-circle' as const, label: '인시던트', go: () => router.push('/incidents') }, { key: 'asset', icon: 'server' as const, label: '자산 조회', go: () => router.push('/assets') }, ] return ( } > 안녕하세요, {me?.userNm || 'ITMS'} 님 router.push('/notices')} style={s.iconBtn}> {/* KPI 3분할 */} {kpis.map((k) => ( {k.count} {k.label} ))} {/* 자산 요약 통계(있을 때만) */} {statRows.length > 0 && ( 자산 현황 {statRows.slice(0, 6).map((r, i) => ( {rowTitle(r)} {r.cnt ?? r.totCnt ?? r.count ?? r.value ?? '-'} ))} )} {/* 퀵액션 */} {quick.map((q) => ( {q.label} ))} {/* 내 할 일 */} 내 할 일 ({todo.length}) {todo.length === 0 ? ( 표시할 항목이 없습니다. ) : todo.slice(0, 5).map((r, i) => ( {rowTitle(r)} {rowDate(r)} ))} {/* 내 인시던트 */} 내 인시던트 ({incidents.length}) {incidents.length === 0 ? ( 표시할 항목이 없습니다. ) : incidents.slice(0, 5).map((r, i) => ( router.push({ pathname: '/request-detail', params: { num: reqIncidentNum(r), raw: JSON.stringify(r) } })} > {rowTitle(r)} {rowDate(r)} ))} ) } const makeStyles = (t: ThemeTokens) => StyleSheet.create({ wrap: { flex: 1, backgroundColor: t.bg }, content: { padding: 16 }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, marginTop: 8 }, greeting: { color: t.muted, fontSize: 13 }, userName: { color: t.text, fontSize: 20, fontWeight: '900', marginTop: 2 }, iconBtn: { width: 40, height: 40, borderRadius: 20, backgroundColor: t.card, borderWidth: 1, borderColor: t.border, justifyContent: 'center', alignItems: 'center' }, kpiRow: { flexDirection: 'row', gap: 10, marginBottom: 14 }, kpiTile: { flex: 1, backgroundColor: t.card, borderRadius: 12, borderWidth: 1, borderColor: t.border, paddingVertical: 14, alignItems: 'center', gap: 4 }, kpiIcon: { width: 30, height: 30, borderRadius: 15, justifyContent: 'center', alignItems: 'center', marginBottom: 2 }, kpiCount: { fontSize: 22, fontWeight: '900' }, kpiLbl: { color: t.muted, fontSize: 11, fontWeight: '600' }, statCard: { backgroundColor: t.card, borderRadius: 14, borderWidth: 1, borderColor: t.border, padding: 16, marginBottom: 14 }, statRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 6 }, statLbl: { color: t.muted, fontSize: 13, flex: 1, marginRight: 12 }, statVal: { color: t.text, fontSize: 14, fontWeight: '800' }, quickGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 }, quickCell: { width: '47.6%', backgroundColor: t.card, borderRadius: 14, borderWidth: 1, borderColor: t.border, paddingVertical: 18, alignItems: 'center', gap: 8 }, quickIcon: { width: 44, height: 44, borderRadius: 22, backgroundColor: t.brand + '18', justifyContent: 'center', alignItems: 'center' }, quickLbl: { color: t.text, fontSize: 13, fontWeight: '700' }, mt: { marginTop: 22 }, empty: { color: t.muted, fontSize: 13, paddingVertical: 12 }, rowCard: { flexDirection: 'row', alignItems: 'center', gap: 10, backgroundColor: t.card, borderRadius: 12, borderWidth: 1, borderColor: t.border, padding: 13, marginBottom: 8 }, rowTitle: { color: t.text, fontSize: 14, fontWeight: '700' }, rowSub: { color: t.muted, fontSize: 12, marginTop: 2 }, })