From bfee90a6fc0b5e8f00532ce1c17367dc55430c1c Mon Sep 17 00:00:00 2001 From: b3ni15 Date: Mon, 28 Jul 2025 00:43:07 +0200 Subject: [PATCH] Add schedule and requests screens with workdays fetching and navigation --- app/index.tsx | 3 +- app/profile.tsx | 115 ++++++++++++++++++++++- app/requests.tsx | 98 +++++++++++++++++++ app/schedule.tsx | 239 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + pnpm-lock.yaml | 19 ++++ 6 files changed, 471 insertions(+), 5 deletions(-) create mode 100644 app/requests.tsx create mode 100644 app/schedule.tsx diff --git a/app/index.tsx b/app/index.tsx index 778af5b..6f60be4 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -20,6 +20,7 @@ export default function Index() { (async () => { const savedEmail = await SecureStore.getItemAsync('email'); const savedPassword = await SecureStore.getItemAsync('password'); + console.log('Mentett email:', savedEmail, 'Mentett password:', savedPassword); if (savedEmail && savedPassword) { setUsername(savedEmail); setPassword(savedPassword); @@ -69,7 +70,7 @@ export default function Index() { ToastAndroid.show('Sikeres automatikus bejelentkezés', ToastAndroid.SHORT); } else { // iOS: custom toast helyett Alert - Alert.alert('Sikeres automatikus bejelentkezés'); + console.log('Sikeres automatikus bejelentkezés'); } } router.replace('/profile'); diff --git a/app/profile.tsx b/app/profile.tsx index 06a74b1..8368dc8 100644 --- a/app/profile.tsx +++ b/app/profile.tsx @@ -1,8 +1,10 @@ import { Ionicons, MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons'; +import axios from 'axios'; +import { addDays, addMonths, endOfMonth, endOfWeek, format, getMonth, getYear, isSameDay, startOfMonth, startOfWeek, subMonths } from 'date-fns'; import { useRouter } from 'expo-router'; import * as SecureStore from 'expo-secure-store'; import React, { useEffect, useState } from "react"; -import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; +import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; const PRIMARY = '#A24BFA'; const BG = '#0c0a0a'; @@ -17,6 +19,9 @@ export default function Profile() { const [email, setEmail] = useState(''); const [userId, setUserId] = useState(''); const [activeTab, setActiveTab] = useState('profile'); + const [currentMonth, setCurrentMonth] = useState(new Date()); + const [workdays, setWorkdays] = useState([]); + const [loading, setLoading] = useState(false); const router = useRouter(); useEffect(() => { @@ -30,6 +35,38 @@ export default function Profile() { })(); }, []); + useEffect(() => { + if (activeTab === 'schedule') { + fetchWorkdays(); + } + // eslint-disable-next-line + }, [currentMonth, activeTab]); + + async function fetchWorkdays() { + setLoading(true); + try { + const userCookie = await SecureStore.getItemAsync('cookie'); + const year = getYear(currentMonth); + const month = getMonth(currentMonth) + 1; + const response = await axios.post( + 'https://mymenu.mcdonalds.hu/api/UserDataApi/GetWorkDayMonthList', + { Data: { Year: year, Month: month } }, + { + headers: { + 'Content-Type': 'application/json', + 'cookie': userCookie, + 'origin': 'https://mymenu.mcdonalds.hu', + }, + } + ); + setWorkdays(response.data.data.Data || []); + } catch (e) { + setWorkdays([]); + } finally { + setLoading(false); + } + } + async function handleLogout() { await SecureStore.deleteItemAsync('cookie'); await SecureStore.deleteItemAsync('userId'); @@ -39,6 +76,73 @@ export default function Profile() { router.replace('/'); } + function renderCalendar() { + const monthStart = startOfMonth(currentMonth); + const monthEnd = endOfMonth(monthStart); + const startDate = startOfWeek(monthStart, { weekStartsOn: 1 }); + const endDate = endOfWeek(monthEnd, { weekStartsOn: 1 }); + const today = new Date(); + const rows = []; + let days = []; + let day = startDate; + let formattedDate = ''; + while (day <= endDate) { + for (let i = 0; i < 7; i++) { + formattedDate = format(day, 'yyyy-MM-dd'); + const wd = workdays.find(w => w.WorkDay?.slice(0, 10) === formattedDate); + let bg = 'rgba(24,20,28,0.7)'; + let color = '#fff'; + let borderWidth = 0; + if (isSameDay(day, today)) { + borderWidth = 2; + color = PRIMARY; + } + if (wd) { + bg = wd.color || 'deepskyblue'; + color = '#222'; + } + days.push( + + + {format(day, 'd')} + {wd && {wd.text}} + + + ); + day = addDays(day, 1); + } + rows.push({days}); + days = []; + } + return ( + + + setCurrentMonth(subMonths(currentMonth, 1))}> + {format(currentMonth, 'yyyy. MMMM')} + setCurrentMonth(addMonths(currentMonth, 1))}> + + + {["H", "K", "Sz", "Cs", "P", "Szo", "V"].map(d => ( + {d} + ))} + + {loading ? ( + Betöltés… + ) : ( + {rows} + )} + + ); + } + function renderContent() { if (activeTab === 'profile') { return ( @@ -56,9 +160,12 @@ export default function Profile() { ); } else if (activeTab === 'schedule') { return ( - - Beosztás funkció hamarosan… - + + + Beosztás naptár + {renderCalendar()} + + ); } else if (activeTab === 'requests') { return ( diff --git a/app/requests.tsx b/app/requests.tsx new file mode 100644 index 0000000..1012a6c --- /dev/null +++ b/app/requests.tsx @@ -0,0 +1,98 @@ +import { Ionicons, MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons'; +import { useRouter } from 'expo-router'; +import React, { useEffect } from 'react'; +import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; + +const PRIMARY = '#A24BFA'; +const BG = '#0c0a0a'; + +export default function Requests() { + useEffect(() => { + console.log('Requests oldal betöltve'); + }, []); + return ( + + + Kérelmek funkció hamarosan… + + + + + + ); +} + +function NavBar({ activeTab }: { activeTab: string }) { + const router = useRouter(); + return ( + + router.replace('/profile')}> + + Profilom + + router.replace('/schedule')}> + + Beosztás + + router.replace('/requests')}> + + Kérelmek + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: BG, + justifyContent: 'center', + alignItems: 'center', + paddingBottom: 64, + }, + card: { + backgroundColor: 'rgba(24, 20, 28, 0.95)', + borderRadius: 24, + padding: 32, + width: '90%', + maxWidth: 400, + shadowColor: '#000', + shadowOpacity: 0.3, + shadowRadius: 24, + shadowOffset: { width: 0, height: 8 }, + elevation: 8, + marginBottom: 32, + marginTop: 32, + }, + label: { + color: '#bdbdbd', + fontSize: 16, + marginBottom: 16, + fontWeight: 'bold', + textAlign: 'center', + }, + navBar: { + flexDirection: 'row', + justifyContent: 'space-around', + alignItems: 'center', + backgroundColor: 'rgba(24, 20, 28, 0.98)', + borderTopWidth: 1, + borderTopColor: '#222', + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + height: 64, + paddingHorizontal: 16, + }, + navItem: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + navLabel: { + fontSize: 13, + marginTop: 2, + fontWeight: 'bold', + }, +}); diff --git a/app/schedule.tsx b/app/schedule.tsx new file mode 100644 index 0000000..2c747e8 --- /dev/null +++ b/app/schedule.tsx @@ -0,0 +1,239 @@ +// Csak a név=érték részt tartjuk meg a cookie stringből +function extractCookie(cookieStr: string) { + return cookieStr.split(';')[0]; +} +import { Ionicons, MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons'; +import axios from 'axios'; +import { addDays, addMonths, endOfMonth, endOfWeek, format, getMonth, getYear, isSameDay, startOfMonth, startOfWeek, subMonths } from 'date-fns'; +import { useRouter } from 'expo-router'; +import * as SecureStore from 'expo-secure-store'; +import React, { useEffect, useState } from "react"; +import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; + +const PRIMARY = '#A24BFA'; +const BG = '#0c0a0a'; + +export default function Schedule() { + const [currentMonth, setCurrentMonth] = useState(new Date()); + const [workdays, setWorkdays] = useState([]); + const [loading, setLoading] = useState(false); + const router = useRouter(); + + useEffect(() => { + fetchWorkdays(); + }, [currentMonth]); + + async function fetchWorkdays() { + setLoading(true); + try { + const userCookie = await SecureStore.getItemAsync('cookie'); + const year = getYear(currentMonth); + const month = getMonth(currentMonth) + 1; + console.log('Lekérdezett hónap:', year, month); + console.log('Cookie:', userCookie); + if (!userCookie) { + console.log('Nincs elmentett cookie, nem vagy bejelentkezve!'); + setWorkdays([]); + return; + } + const response = await axios.post( + 'https://mymenu.mcdonalds.hu/api/UserDataApi/GetWorkDayMonthList', + { Data: { Year: year, Month: month } }, + { + headers: { + 'Content-Type': 'application/json', + 'cookie': userCookie, + 'origin': 'https://mymenu.mcdonalds.hu', + }, + // Axios v1: get full response + withCredentials: true, + } + ); + // Cookie frissítés, ha van új set-cookie header + const setCookieHeader = response.headers && (response.headers['set-cookie'] || response.headers['Set-Cookie']); + if (setCookieHeader) { + let cookieString = ''; + if (Array.isArray(setCookieHeader)) { + // Ha van .ASPXAUTH, azt mentsük el, különben az elsőt + const authCookie = setCookieHeader.find(c => c.startsWith('.ASPXAUTH=')); + if (authCookie) { + cookieString = extractCookie(authCookie); + } else { + cookieString = extractCookie(setCookieHeader[0]); + } + } else { + cookieString = extractCookie(setCookieHeader); + } + console.log('Frissített cookie:', cookieString); + await SecureStore.setItemAsync('cookie', cookieString); + } + console.log('API teljes válasz:', response.data); + if (!response.data || !response.data.data || !response.data.data.Data) { + console.log('Nincs beosztás adat a válaszban!'); + setWorkdays([]); + return; + } + setWorkdays(response.data.data.Data); + } catch (e) { + console.log('Beosztás API hiba:', e); + setWorkdays([]); + } finally { + setLoading(false); + } + } + + function renderCalendar() { + const monthStart = startOfMonth(currentMonth); + const monthEnd = endOfMonth(monthStart); + const startDate = startOfWeek(monthStart, { weekStartsOn: 1 }); + const endDate = endOfWeek(monthEnd, { weekStartsOn: 1 }); + const today = new Date(); + const rows = []; + let days = []; + let day = startDate; + let formattedDate = ''; + while (day <= endDate) { + for (let i = 0; i < 7; i++) { + formattedDate = format(day, 'yyyy-MM-dd'); + const wd = workdays.find(w => w.WorkDay?.slice(0, 10) === formattedDate); + let bg = 'rgba(24,20,28,0.7)'; + let color = '#fff'; + let borderWidth = 0; + if (isSameDay(day, today)) { + borderWidth = 2; + color = PRIMARY; + } + if (wd) { + bg = wd.color || 'deepskyblue'; + color = '#222'; + } + days.push( + + + {format(day, 'd')} + {wd && {wd.text}} + + + ); + day = addDays(day, 1); + } + rows.push({days}); + days = []; + } + return ( + + + setCurrentMonth(subMonths(currentMonth, 1))}> + {format(currentMonth, 'yyyy. MMMM')} + setCurrentMonth(addMonths(currentMonth, 1))}> + + + {["H", "K", "Sz", "Cs", "P", "Szo", "V"].map(d => ( + {d} + ))} + + {loading ? ( + Betöltés… + ) : ( + {rows} + )} + + ); + } + + return ( + + + + Beosztás naptár + {/* Hibaüzenet, ha nincs bejelentkezve vagy nincs beosztás */} + {(!loading && workdays.length === 0) && ( + + Nincs beosztás vagy nem vagy bejelentkezve! + + )} + {renderCalendar()} + + + + + + + ); +} + +function NavBar({ activeTab }: { activeTab: string }) { + const router = useRouter(); + return ( + + router.push('/profile')}> + + Profilom + + router.push('/schedule')}> + + Beosztás + + router.push('/requests')}> + + Kérelmek + + + ); +} + +const styles = StyleSheet.create({ + card: { + backgroundColor: 'rgba(24, 20, 28, 0.95)', + borderRadius: 24, + padding: 32, + width: '90%', + maxWidth: 400, + shadowColor: '#000', + shadowOpacity: 0.3, + shadowRadius: 24, + shadowOffset: { width: 0, height: 8 }, + elevation: 8, + marginBottom: 32, + marginTop: 32, + }, + label: { + color: '#bdbdbd', + fontSize: 16, + marginBottom: 16, + fontWeight: 'bold', + textAlign: 'center', + }, + navBar: { + flexDirection: 'row', + justifyContent: 'space-around', + alignItems: 'center', + backgroundColor: 'rgba(24, 20, 28, 0.98)', + borderTopWidth: 1, + borderTopColor: '#222', + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + height: 64, + paddingHorizontal: 16, + }, + navItem: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + navLabel: { + fontSize: 13, + marginTop: 2, + fontWeight: 'bold', + }, +}); diff --git a/package.json b/package.json index 9bbec04..ee694cc 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@react-navigation/elements": "^2.3.8", "@react-navigation/native": "^7.1.6", "axios": "^1.11.0", + "date-fns": "^4.1.0", "expo": "~53.0.20", "expo-blur": "~14.1.5", "expo-constants": "~17.1.7", @@ -43,6 +44,7 @@ }, "devDependencies": { "@babel/core": "^7.25.2", + "@types/date-fns": "^2.6.3", "@types/react": "~19.0.10", "eslint": "^9.25.0", "eslint-config-expo": "~9.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d78cc9c..c522bea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: axios: specifier: ^1.11.0 version: 1.11.0 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 expo: specifier: ~53.0.20 version: 53.0.20(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.5(@babel/core@7.28.0)(@types/react@19.0.14)(react@19.0.0)))(react-native-webview@13.13.5(react-native@0.79.5(@babel/core@7.28.0)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0))(react-native@0.79.5(@babel/core@7.28.0)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0) @@ -99,6 +102,9 @@ importers: '@babel/core': specifier: ^7.25.2 version: 7.28.0 + '@types/date-fns': + specifier: ^2.6.3 + version: 2.6.3 '@types/react': specifier: ~19.0.10 version: 19.0.14 @@ -1011,6 +1017,10 @@ packages: '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/date-fns@2.6.3': + resolution: {integrity: sha512-Ke1lw2Ni1t/wMUoLtKFmSNCLozcTBd6vmMqFP4hRzXn6qzkNt97bPAX0x5Y/c15DP43kKvwW1ycStD5+43jVQA==} + deprecated: This is a stub types definition. date-fns provides its own type definitions, so you do not need this installed. + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1654,6 +1664,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -5305,6 +5318,10 @@ snapshots: dependencies: '@babel/types': 7.28.2 + '@types/date-fns@2.6.3': + dependencies: + date-fns: 4.1.0 + '@types/estree@1.0.8': {} '@types/graceful-fs@4.1.9': @@ -6058,6 +6075,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + date-fns@4.1.0: {} + debug@2.6.9: dependencies: ms: 2.0.0