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

105 lines
4.9 KiB
TypeScript

import React, { useState, useCallback } from 'react'
import { View, Text, TouchableOpacity, FlatList, StyleSheet, RefreshControl } from 'react-native'
import { useFocusEffect } from 'expo-router'
import { COLORS } from '../../constants/Config'
import client from '../../services/api'
const WEEKDAYS = ['일', '월', '화', '수', '목', '금', '토']
export default function WorkCalendarScreen() {
const today = new Date()
const [year, setYear] = useState(today.getFullYear())
const [month, setMonth] = useState(today.getMonth())
const [events, setEvents] = useState<any[]>([])
const [selected, setSelected] = useState<string>('')
const [loading, setLoading] = useState(false)
const load = useCallback(async (y: number, m: number) => {
setLoading(true)
try {
const r = await client.get('/api/mobile2/work-calendar', { params: { year: y, month: m + 1 } })
setEvents(r.data?.events ?? r.data?.items ?? [])
} catch { setEvents([]) } finally { setLoading(false) }
}, [])
useFocusEffect(useCallback(() => { load(year, month) }, [year, month, load]))
const firstDay = new Date(year, month, 1).getDay()
const daysInMonth = new Date(year, month + 1, 0).getDate()
const cells = Array.from({ length: firstDay }, () => null).concat(Array.from({ length: daysInMonth }, (_, i) => i + 1))
const eventsOn = (day: number) => {
const key = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`
return events.filter(e => (e.date ?? e.scheduled_at ?? '').startsWith(key))
}
const selectedEvents = selected ? events.filter(e => (e.date ?? e.scheduled_at ?? '').startsWith(selected)) : []
const prev = () => { if (month === 0) { setYear(y => y - 1); setMonth(11) } else setMonth(m => m - 1) }
const next = () => { if (month === 11) { setYear(y => y + 1); setMonth(0) } else setMonth(m => m + 1) }
return (
<View style={s.container}>
<View style={s.nav}>
<TouchableOpacity onPress={prev} style={s.navBtn}><Text style={s.navText}></Text></TouchableOpacity>
<Text style={s.monthLabel}>{year} {month + 1}</Text>
<TouchableOpacity onPress={next} style={s.navBtn}><Text style={s.navText}></Text></TouchableOpacity>
</View>
<View style={s.weekRow}>
{WEEKDAYS.map(d => <Text key={d} style={[s.weekDay, d === '일' ? { color: COLORS.danger } : d === '토' ? { color: COLORS.blue } : {}]}>{d}</Text>)}
</View>
<View style={s.grid}>
{cells.map((day, i) => {
if (!day) return <View key={i} style={s.cell} />
const key = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`
const dayEvents = eventsOn(day)
const isToday = day === today.getDate() && month === today.getMonth() && year === today.getFullYear()
const isSel = key === selected
return (
<TouchableOpacity key={i} style={[s.cell, isToday && s.today, isSel && s.selCell]} onPress={() => setSelected(isSel ? '' : key)}>
<Text style={[s.dayNum, isToday && { color: '#fff' }]}>{day}</Text>
{dayEvents.length > 0 && <View style={s.dot} />}
</TouchableOpacity>
)
})}
</View>
{selectedEvents.length > 0 && (
<FlatList
data={selectedEvents}
keyExtractor={(_, i) => String(i)}
style={s.eventList}
renderItem={({ item }) => (
<View style={s.eventCard}>
<Text style={s.eventTitle}>{item.title ?? item.name}</Text>
<Text style={s.eventMeta}>{item.start_time ?? ''} · {item.category ?? item.type ?? ''}</Text>
</View>
)}
/>
)}
</View>
)
}
const s = StyleSheet.create({
container: { flex: 1, backgroundColor: COLORS.bg },
nav: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 12 },
navBtn: { padding: 8 },
navText: { fontSize: 16, color: COLORS.accent },
monthLabel: { fontSize: 18, fontWeight: '800', color: COLORS.text },
weekRow: { flexDirection: 'row', paddingHorizontal: 4 },
weekDay: { flex: 1, textAlign: 'center', fontSize: 11, fontWeight: '700', color: COLORS.muted, paddingVertical: 4 },
grid: { flexDirection: 'row', flexWrap: 'wrap', paddingHorizontal: 4 },
cell: { width: '14.28%', aspectRatio: 1, alignItems: 'center', justifyContent: 'center', padding: 2 },
today: { backgroundColor: COLORS.accent, borderRadius: 20 },
selCell: { backgroundColor: COLORS.accent + '30', borderRadius: 20 },
dayNum: { fontSize: 13, color: COLORS.text },
dot: { width: 5, height: 5, borderRadius: 3, backgroundColor: COLORS.danger, marginTop: 2 },
eventList: { flex: 1, padding: 12 },
eventCard: { backgroundColor: '#fff', borderRadius: 8, padding: 12, marginBottom: 6, elevation: 1 },
eventTitle: { fontSize: 13, fontWeight: '700', color: COLORS.text, marginBottom: 2 },
eventMeta: { fontSize: 11, color: COLORS.muted },
})