Refactor Schedule component to use week-based navigation and improve workdays fetching logic

This commit is contained in:
2025-08-01 12:51:35 +02:00
parent ccc8b00348
commit f81591ccb0

View File

@@ -1,5 +1,5 @@
import { Ionicons, MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons'; import { Ionicons, MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons';
import { addDays, addMonths, endOfMonth, endOfWeek, format, getMonth, getYear, isSameDay, startOfMonth, startOfWeek, subMonths } from 'date-fns'; import { addDays, addWeeks, format, getMonth, getYear, isSameDay, startOfWeek, subWeeks } from 'date-fns';
import { useRouter } from 'expo-router'; import { useRouter } from 'expo-router';
import * as SecureStore from 'expo-secure-store'; import * as SecureStore from 'expo-secure-store';
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
@@ -9,14 +9,14 @@ const PRIMARY = '#A24BFA';
const BG = '#0c0a0a'; const BG = '#0c0a0a';
export default function Schedule() { export default function Schedule() {
const [currentMonth, setCurrentMonth] = useState(new Date()); const [currentWeek, setCurrentWeek] = useState(new Date());
const [workdays, setWorkdays] = useState<any[]>([]); const [workdays, setWorkdays] = useState<any[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {
fetchWorkdays(); fetchWorkdays();
}, [currentMonth]); }, [currentWeek]);
async function fetchWorkdays() { async function fetchWorkdays() {
setLoading(true); setLoading(true);
@@ -26,9 +26,21 @@ export default function Schedule() {
throw new Error('Nincs elmentett token, kérlek jelentkezz be újra!'); throw new Error('Nincs elmentett token, kérlek jelentkezz be újra!');
} }
const year = getYear(currentMonth); // Az aktuális hét első és utolsó napja
const month = getMonth(currentMonth) + 1; const weekStart = startOfWeek(currentWeek, { weekStartsOn: 1 });
const weekEnd = addDays(weekStart, 6);
const months = new Set([
getMonth(weekStart) + 1,
getMonth(weekEnd) + 1
]);
const years = new Set([
getYear(weekStart),
getYear(weekEnd)
]);
let allWorkdays: any[] = [];
for (const year of years) {
for (const month of months) {
const response = await fetch(`https://menuapi.devbeni.lol/api/@me/schedule?year=${year}&month=${month}`, { const response = await fetch(`https://menuapi.devbeni.lol/api/@me/schedule?year=${year}&month=${month}`, {
method: 'GET', method: 'GET',
headers: { headers: {
@@ -36,17 +48,13 @@ export default function Schedule() {
'Accept': 'application/json', 'Accept': 'application/json',
}, },
}); });
const data = await response.json(); const data = await response.json();
if (data && data.data && Array.isArray(data.data.Data)) {
console.log('API teljes válasz:', data); allWorkdays = allWorkdays.concat(data.data.Data);
if (!data || !data.data || !Array.isArray(data.data.Data)) {
console.log('Nincs beosztás adat a válaszban!');
setWorkdays([]);
return;
} }
setWorkdays(data.data.Data); }
}
setWorkdays(allWorkdays);
} catch (e) { } catch (e) {
const err = e as any; const err = e as any;
if (err.response) { if (err.response) {
@@ -63,59 +71,83 @@ export default function Schedule() {
} }
function renderCalendar() { function renderCalendar() {
const monthStart = startOfMonth(currentMonth); const weekStart = startOfWeek(currentWeek, { weekStartsOn: 1 });
const monthEnd = endOfMonth(monthStart);
const startDate = startOfWeek(monthStart, { weekStartsOn: 1 });
const endDate = endOfWeek(monthEnd, { weekStartsOn: 1 });
const today = new Date(); const today = new Date();
const rows = []; // 2 soros grid: első sor 4 nap, második sor 3 nap
let days = []; const weekDays = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i));
let day = startDate; const firstRow = weekDays.slice(0, 4);
let formattedDate = ''; const secondRow = weekDays.slice(4);
while (day <= endDate) {
for (let i = 0; i < 7; i++) { function renderDayBox(day: string | number | Date) {
formattedDate = format(day, 'yyyy-MM-dd'); const formattedDate = format(day, 'yyyy-MM-dd');
const wd = workdays.find(w => w.WorkDay?.slice(0, 10) === formattedDate); const wd = workdays.find(w => w.WorkDay?.slice(0, 10) === formattedDate);
let bg = 'rgba(24,20,28,0.7)'; let bg = '#ede7f6';
let color = '#fff'; let color = '#6a1b9a';
let borderWidth = 0; let borderWidth = 0;
if (isSameDay(day, today)) { if (isSameDay(day, today)) {
borderWidth = 2; borderWidth = 2;
color = PRIMARY; color = PRIMARY;
} }
if (wd) { if (wd) {
bg = wd.color || 'deepskyblue'; if (wd.Type === 1) {
color = '#222'; bg = '#B388FF';
color = '#311b92';
} else if (wd.Type === 10) {
bg = '#A24BFA';
color = '#fff';
} }
days.push( }
<View key={day.toString()} style={{ flex: 1, aspectRatio: 1, margin: 2 }}> return (
<View
key={day.toString()}
style={{
flex: 1,
margin: 6,
minWidth: 60,
minHeight: 70,
maxWidth: 120,
maxHeight: 120,
justifyContent: 'center',
alignItems: 'center',
}}
>
<View style={{ <View style={{
backgroundColor: bg, backgroundColor: bg,
borderRadius: 8, borderRadius: 18,
borderWidth, borderWidth,
borderColor: PRIMARY, borderColor: PRIMARY,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
flex: 1, width: '100%',
minHeight: 60,
paddingVertical: 10,
paddingHorizontal: 4,
shadowColor: '#a24bfa',
shadowOpacity: 0.10,
shadowRadius: 8,
shadowOffset: { width: 0, height: 2 },
overflow: 'hidden',
}}> }}>
<Text style={{ color, fontWeight: isSameDay(day, today) ? 'bold' : 'normal' }}>{format(day, 'd')}</Text> <Text style={{ color, fontWeight: 'bold', fontSize: 22, textAlign: 'center', width: '100%' }}>{format(day, 'd')}</Text>
{wd && <Text style={{ color, fontSize: 10 }}>{wd.text}</Text>} {wd && wd.Type === 1 && (
<Text style={{ color, fontSize: 15, fontWeight: 'bold', marginTop: 4, textAlign: 'center', width: '100%' }} numberOfLines={2} ellipsizeMode="tail">PN</Text>
)}
{wd && wd.Type === 10 && wd.text && (
<Text style={{ color, fontSize: 15, fontWeight: 'bold', marginTop: 4, textAlign: 'center', width: '100%' }} numberOfLines={2} ellipsizeMode="tail">{wd.text}</Text>
)}
</View> </View>
</View> </View>
); );
day = addDays(day, 1);
}
rows.push(<View key={day.toString()} style={{ flexDirection: 'row' }}>{days}</View>);
days = [];
} }
return ( return (
<View> <View>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}> <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
<TouchableOpacity onPress={() => setCurrentMonth(subMonths(currentMonth, 1))}><Ionicons name="chevron-back" size={28} color="#fff" /></TouchableOpacity> <TouchableOpacity onPress={() => setCurrentWeek(subWeeks(currentWeek, 1))}><Ionicons name="chevron-back" size={28} color="#fff" /></TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 18, fontWeight: 'bold' }}>{format(currentMonth, 'yyyy. MMMM')}</Text> <Text style={{ color: '#fff', fontSize: 18, fontWeight: 'bold' }}>{format(weekStart, 'yyyy. MMMM d.') + ' - ' + format(addDays(weekStart, 6), 'MMMM d.')}</Text>
<TouchableOpacity onPress={() => setCurrentMonth(addMonths(currentMonth, 1))}><Ionicons name="chevron-forward" size={28} color="#fff" /></TouchableOpacity> <TouchableOpacity onPress={() => setCurrentWeek(addWeeks(currentWeek, 1))}><Ionicons name="chevron-forward" size={28} color="#fff" /></TouchableOpacity>
</View> </View>
<View style={{ flexDirection: 'row', marginBottom: 4 }}> <View style={{ flexDirection: 'row', marginBottom: 4, width: '100%' }}>
{["H", "K", "Sz", "Cs", "P", "Szo", "V"].map(d => ( {["H", "K", "Sz", "Cs", "P", "Szo", "V"].map(d => (
<Text key={d} style={{ flex: 1, color: '#bdbdbd', textAlign: 'center', fontWeight: 'bold' }}>{d}</Text> <Text key={d} style={{ flex: 1, color: '#bdbdbd', textAlign: 'center', fontWeight: 'bold' }}>{d}</Text>
))} ))}
@@ -123,16 +155,23 @@ export default function Schedule() {
{loading ? ( {loading ? (
<Text style={{ color: '#fff', textAlign: 'center', marginTop: 24 }}>Betöltés</Text> <Text style={{ color: '#fff', textAlign: 'center', marginTop: 24 }}>Betöltés</Text>
) : ( ) : (
<View>{rows}</View> <View style={{ width: '100%', alignItems: 'center', justifyContent: 'center' }}>
<View style={{ flexDirection: 'row', width: '100%', justifyContent: 'center' }}>
{firstRow.map(renderDayBox)}
</View>
<View style={{ flexDirection: 'row', width: '75%', justifyContent: 'center' }}>
{secondRow.map(renderDayBox)}
</View>
</View>
)} )}
</View> </View>
); );
} }
return ( return (
<ScrollView style={{ flex: 1, backgroundColor: BG }} contentContainerStyle={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center', minHeight: '100%', paddingBottom: 80 }}> <ScrollView style={{ flex: 1, backgroundColor: BG }} contentContainerStyle={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center', minHeight: '100%', paddingBottom: 80, width: '100%' }}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', width: '100%' }}> <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', width: '100%' }}>
<View style={[styles.card, { alignSelf: 'center', marginTop: 48, marginBottom: 48 }]}> <View style={[styles.card, { alignSelf: 'center', marginTop: 48, marginBottom: 48, width: '100%' }]}>
<Text style={styles.label}>Beosztás naptár</Text> <Text style={styles.label}>Beosztás naptár</Text>
{(!loading && workdays.length === 0) && ( {(!loading && workdays.length === 0) && (
<Text style={{ color: '#ff5252', textAlign: 'center', marginBottom: 12, fontWeight: 'bold' }}> <Text style={{ color: '#ff5252', textAlign: 'center', marginBottom: 12, fontWeight: 'bold' }}>