75 lines
3.0 KiB
TypeScript
75 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 client from '../../services/api'
|
||
|
||
const HOURS = Array.from({ length: 24 }, (_, i) => i)
|
||
const DAYS = ['월', '화', '수', '목', '금', '토', '일']
|
||
|
||
export default function SRHeatmapScreen() {
|
||
const [matrix, setMatrix] = useState<number[][]>([])
|
||
const [maxVal, setMaxVal] = useState(1)
|
||
const [loading, setLoading] = useState(false)
|
||
|
||
const load = useCallback(async () => {
|
||
setLoading(true)
|
||
try {
|
||
const r = await client.get('/api/mobile2/hourly-pattern')
|
||
const m = r.data?.matrix ?? r.data ?? []
|
||
setMatrix(m)
|
||
setMaxVal(Math.max(1, ...m.flat().filter(Number.isFinite)))
|
||
} catch { setMatrix([]) } finally { setLoading(false) }
|
||
}, [])
|
||
|
||
useFocusEffect(useCallback(() => { load() }, [load]))
|
||
|
||
const cellColor = (v: number) => {
|
||
const alpha = Math.round((v / maxVal) * 255).toString(16).padStart(2, '0')
|
||
return `${COLORS.accent}${alpha}`
|
||
}
|
||
|
||
return (
|
||
<ScrollView style={s.container} refreshControl={<RefreshControl refreshing={loading} onRefresh={load} />}>
|
||
<Text style={s.title}>SR 발생 히트맵 (요일×시간)</Text>
|
||
|
||
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ paddingLeft: 32 }}>
|
||
<View>
|
||
<View style={s.hourRow}>
|
||
{HOURS.filter(h => h % 3 === 0).map(h => (
|
||
<Text key={h} style={[s.hourLabel, { width: 36 * 3 }]}>{h}시</Text>
|
||
))}
|
||
</View>
|
||
{matrix.map((row, di) => (
|
||
<View key={di} style={s.dayRow}>
|
||
{row.map((v, hi) => (
|
||
<View key={hi} style={[s.cell, { backgroundColor: cellColor(v) }]}>
|
||
{v > 0 && <Text style={s.cellVal}>{v}</Text>}
|
||
</View>
|
||
))}
|
||
</View>
|
||
))}
|
||
</View>
|
||
</ScrollView>
|
||
|
||
<View style={s.legend}>
|
||
{DAYS.slice(0, matrix.length).map((d, i) => <Text key={i} style={s.dayLabel}>{d}</Text>)}
|
||
</View>
|
||
<Text style={s.note}>셀 색이 진할수록 SR 발생량이 많습니다.</Text>
|
||
</ScrollView>
|
||
)
|
||
}
|
||
|
||
const s = StyleSheet.create({
|
||
container: { flex: 1, backgroundColor: COLORS.bg, padding: 12 },
|
||
title: { fontSize: 16, fontWeight: '800', color: COLORS.text, marginBottom: 12 },
|
||
hourRow: { flexDirection: 'row', marginBottom: 4 },
|
||
hourLabel: { fontSize: 9, color: COLORS.muted, textAlign: 'center' },
|
||
dayRow: { flexDirection: 'row', marginBottom: 2 },
|
||
cell: { width: 34, height: 34, borderRadius: 4, marginRight: 2, alignItems: 'center', justifyContent: 'center' },
|
||
cellVal: { fontSize: 9, color: '#fff', fontWeight: '700' },
|
||
legend: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 12 },
|
||
dayLabel: { fontSize: 12, color: COLORS.muted },
|
||
note: { fontSize: 11, color: COLORS.muted, marginTop: 8 },
|
||
})
|