Compare commits

..

5 Commits

8 changed files with 755 additions and 104 deletions

View File

@@ -1,7 +1,7 @@
{
"expo": {
"name": "iso-test-app",
"slug": "iso-test-app",
"slug": "mcbeno",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
@@ -17,7 +17,8 @@
"foregroundImage": "./assets/images/icon.png",
"backgroundColor": "#0c0a0a"
},
"edgeToEdgeEnabled": true
"edgeToEdgeEnabled": true,
"package": "com.devbeni.mcbeno"
},
"web": {
"bundler": "metro",
@@ -38,6 +39,13 @@
],
"experiments": {
"typedRoutes": true
}
},
"extra": {
"router": {},
"eas": {
"projectId": "972fe547-cd46-4ed0-b9e1-9f13b035e4b7"
}
},
"owner": "devbeni"
}
}

View File

@@ -1,105 +1,134 @@
import * as Notifications from 'expo-notifications';
import { MaterialIcons } from '@expo/vector-icons';
import axios from 'axios';
import { useRouter } from 'expo-router';
import * as SecureStore from 'expo-secure-store';
import { StatusBar } from 'expo-status-bar';
import React, { useEffect, useState } from "react";
import { Alert, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
import { ActivityIndicator, Alert, Platform, StyleSheet, Text, TextInput, ToastAndroid, TouchableOpacity, View } from "react-native";
const PRIMARY = '#A24BFA';
const BG = '#0c0a0a';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowBanner: true,
shouldShowList: true,
shouldPlaySound: false,
shouldSetBadge: false,
})
});
async function registerForPushNotificationsAsync() {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
Alert.alert('Permission for notifications not granted!');
return false;
}
return true;
}
async function sendNotification() {
try {
const id = await Notifications.scheduleNotificationAsync({
content: {
title: "Hello!",
body: "Ez egy értesítés példája.",
},
trigger: Platform.OS === 'android'
? ({ seconds: 1, repeats: false } as any)
: null,
});
Alert.alert('Notification scheduled!', `ID: ${id}`);
} catch (e) {
Alert.alert('Hiba történt!', String(e));
}
}
export default function Index() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [autoLogin, setAutoLogin] = useState(false);
const router = useRouter();
useEffect(() => {
registerForPushNotificationsAsync();
(async () => {
const savedEmail = await SecureStore.getItemAsync('email');
const savedPassword = await SecureStore.getItemAsync('password');
// ...
if (savedEmail && savedPassword) {
setUsername(savedEmail);
setPassword(savedPassword);
setAutoLogin(true);
setTimeout(() => {
handleLogin(savedEmail, savedPassword, true);
}, 1000);
}
})();
}, []);
async function handleLogin(emailOverride?: string, passwordOverride?: string, isAuto?: boolean) {
setLoading(true);
const user = emailOverride ?? username;
const pass = passwordOverride ?? password;
// ...
try {
const response = await axios.post(
"https://mymenu.mcdonalds.hu/api/AccountApi/Login",
{
Data: {
UserName: user,
Password: pass
}
},
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'referer': 'https://mymenu.mcdonalds.hu/',
'origin': 'https://mymenu.mcdonalds.hu'
}
}
);
const cookie = response.headers['set-cookie']?.join('; ') || '';
const userId = response.data.Data.UserID;
const fullName = response.data.Data.FullName;
await SecureStore.setItemAsync('cookie', cookie || '');
await SecureStore.setItemAsync('userId', String(userId));
await SecureStore.setItemAsync('email', user);
await SecureStore.setItemAsync('password', pass);
await SecureStore.setItemAsync('fullName', fullName || '');
if (isAuto) {
if (Platform.OS === 'android') {
ToastAndroid.show('Sikeres automatikus bejelentkezés', ToastAndroid.SHORT);
} else {
// ...
}
}
router.replace('/profile');
} catch (e) {
// ...
Alert.alert('Hiba', 'Hibás felhasználónév vagy jelszó, vagy hálózati hiba.');
} finally {
setLoading(false);
setAutoLogin(false);
}
}
return (
<View style={styles.container}>
<StatusBar style="light" backgroundColor={BG} />
<View style={styles.card}>
<View style={{ alignItems: 'center', marginBottom: 16 }}>
<Text style={styles.logoText}>ANGELIC{"\n"}MC</Text>
</View>
<Text style={styles.title}>Üdv újra</Text>
<Text style={styles.subtitle}>Jelentkezz be a fiókodba</Text>
<Text style={styles.subtitle}>Jelentkezz be a mymenu fiókodba</Text>
<View style={{ height: 24 }} />
<Text style={styles.label}>Felhasználónév</Text>
<TextInput
style={styles.input}
placeholder="Add meg a felhasználóneved"
placeholderTextColor="#aaa"
value={username}
onChangeText={setUsername}
autoCapitalize="none"
/>
<View style={{ height: 16 }} />
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
<Text style={styles.label}>Jelszó</Text>
<TouchableOpacity>
<Text style={styles.forgot}>Elfelejtetted?</Text>
</TouchableOpacity>
<View style={styles.inputRow}>
<MaterialIcons name="email" size={22} color="#bdbdbd" style={{ marginRight: 8 }} />
<TextInput
style={styles.input}
placeholder="valaki@gmail.com"
placeholderTextColor="#aaa"
value={username}
onChangeText={setUsername}
autoCapitalize="none"
/>
</View>
<View style={{ height: 16 }} />
<Text style={styles.label}>Jelszó</Text>
<View style={styles.inputRow}>
<MaterialIcons name="lock" size={22} color="#bdbdbd" style={{ marginRight: 8 }} />
<TextInput
style={styles.input}
placeholder="••••••••"
placeholderTextColor="#aaa"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
</View>
<TextInput
style={styles.input}
placeholder="••••••••"
placeholderTextColor="#aaa"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<View style={{ height: 24 }} />
<TouchableOpacity style={styles.button} onPress={sendNotification}>
<Text style={styles.buttonText}>Bejelentkezés</Text>
<TouchableOpacity style={styles.button} onPress={() => handleLogin()} disabled={loading}>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Bejelentkezés</Text>
)}
</TouchableOpacity>
<View style={{ height: 16 }} />
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<Text style={styles.bottomText}>Nincs még fiókod? </Text>
<TouchableOpacity>
<Text style={styles.register}>Regisztrálj most!</Text>
</TouchableOpacity>
</View>
</View>
{(loading || autoLogin) && (
<View style={styles.loadingOverlay}>
<ActivityIndicator size="large" color={PRIMARY} />
<Text style={styles.loadingText}>{autoLogin ? 'Bejelentkezés...' : 'Betöltés...'}</Text>
</View>
)}
</View>
);
}
@@ -127,14 +156,6 @@ const styles = StyleSheet.create({
shadowOffset: { width: 0, height: 8 },
elevation: 8,
},
logoText: {
color: PRIMARY,
fontWeight: 'bold',
fontSize: 28,
textAlign: 'center',
letterSpacing: 2,
marginBottom: 4,
},
title: {
color: '#fff',
fontWeight: 'bold',
@@ -146,27 +167,31 @@ const styles = StyleSheet.create({
color: '#bdbdbd',
fontSize: 16,
textAlign: 'center',
marginBottom: 8,
marginBottom: 24,
},
label: {
color: '#bdbdbd',
fontSize: 14,
marginBottom: 4,
marginBottom: 8,
},
input: {
inputRow: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(20,18,24,1)',
borderColor: PRIMARY,
borderWidth: 1,
borderRadius: 12,
marginBottom: 16,
paddingHorizontal: 8,
},
input: {
flex: 1,
backgroundColor: 'transparent',
color: '#fff',
paddingHorizontal: 16,
paddingHorizontal: 8,
paddingVertical: 12,
fontSize: 16,
},
forgot: {
color: PRIMARY,
fontSize: 14,
fontWeight: '500',
borderWidth: 0,
},
button: {
backgroundColor: PRIMARY,
@@ -185,13 +210,21 @@ const styles = StyleSheet.create({
fontWeight: 'bold',
fontSize: 18,
},
bottomText: {
color: '#bdbdbd',
fontSize: 15,
loadingOverlay: {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(12,10,10,0.85)',
justifyContent: 'center',
alignItems: 'center',
zIndex: 10,
},
register: {
loadingText: {
color: PRIMARY,
fontSize: 18,
marginTop: 16,
fontWeight: 'bold',
fontSize: 15,
},
});

160
app/profile.tsx Normal file
View File

@@ -0,0 +1,160 @@
import { Ionicons, MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons';
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";
const PRIMARY = '#A24BFA';
const BG = '#0c0a0a';
export default function Profile() {
const [fullName, setFullName] = useState('');
const [email, setEmail] = useState('');
const [userId, setUserId] = useState('');
const router = useRouter();
useEffect(() => {
(async () => {
const name = await SecureStore.getItemAsync('fullName');
const mail = await SecureStore.getItemAsync('email');
const uid = await SecureStore.getItemAsync('userId');
setFullName(name || '');
setEmail(mail || '');
setUserId(uid || '');
})();
}, []);
async function handleLogout() {
await SecureStore.deleteItemAsync('cookie');
await SecureStore.deleteItemAsync('userId');
await SecureStore.deleteItemAsync('email');
await SecureStore.deleteItemAsync('password');
await SecureStore.deleteItemAsync('fullName');
router.replace('/');
}
return (
<View style={styles.container}>
<Text style={styles.title}>Profil</Text>
<View style={styles.card}>
<Text style={styles.label}>Név:</Text>
<Text style={styles.value}>{fullName}</Text>
<Text style={styles.label}>Email:</Text>
<Text style={styles.value}>{email}</Text>
<Text style={styles.label}>UserID:</Text>
<Text style={styles.value}>{userId}</Text>
<TouchableOpacity style={styles.logoutButton} onPress={handleLogout}>
<Text style={styles.logoutText}>Kijelentkezés</Text>
</TouchableOpacity>
</View>
<View style={styles.navBar}>
<NavBar activeTab="profile" />
</View>
</View>
);
}
function NavBar({ activeTab }: { activeTab: string }) {
const router = useRouter();
return (
<View style={styles.navBar}>
<TouchableOpacity style={styles.navItem} onPress={() => router.push('/profile')}>
<MaterialIcons name="person" size={28} color={activeTab === 'profile' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'profile' ? PRIMARY : '#bdbdbd' }]}>Profilom</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => router.push('/schedule')}>
<MaterialCommunityIcons name="calendar-month" size={28} color={activeTab === 'schedule' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'schedule' ? PRIMARY : '#bdbdbd' }]}>Beosztás</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => router.push('/requests')}>
<Ionicons name="mail" size={28} color={activeTab === 'requests' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'requests' ? PRIMARY : '#bdbdbd' }]}>Kérelmek</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: BG,
justifyContent: 'center',
alignItems: 'center',
paddingBottom: 64,
},
title: {
color: '#fff',
fontWeight: 'bold',
fontSize: 28,
textAlign: 'center',
marginBottom: 24,
marginTop: 32,
},
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,
},
label: {
color: '#bdbdbd',
fontSize: 16,
marginTop: 12,
fontWeight: 'bold',
},
value: {
color: PRIMARY,
fontSize: 18,
marginBottom: 8,
},
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',
},
logoutButton: {
backgroundColor: PRIMARY,
borderRadius: 12,
paddingVertical: 16,
alignItems: 'center',
marginTop: 32,
shadowColor: PRIMARY,
shadowOpacity: 0.3,
shadowRadius: 8,
shadowOffset: { width: 0, height: 2 },
elevation: 2,
},
logoutText: {
color: '#fff',
fontWeight: 'bold',
fontSize: 18,
letterSpacing: 1,
},
});

98
app/requests.tsx Normal file
View File

@@ -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 (
<View style={styles.container}>
<View style={styles.card}>
<Text style={styles.label}>Kérelmek funkció hamarosan</Text>
</View>
<View style={styles.navBar}>
<NavBar activeTab="requests" />
</View>
</View>
);
}
function NavBar({ activeTab }: { activeTab: string }) {
const router = useRouter();
return (
<View style={styles.navBar}>
<TouchableOpacity style={styles.navItem} onPress={() => router.replace('/profile')}>
<MaterialIcons name="person" size={28} color={activeTab === 'profile' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'profile' ? PRIMARY : '#bdbdbd' }]}>Profilom</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => router.replace('/schedule')}>
<MaterialCommunityIcons name="calendar-month" size={28} color={activeTab === 'schedule' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'schedule' ? PRIMARY : '#bdbdbd' }]}>Beosztás</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => router.replace('/requests')}>
<Ionicons name="mail" size={28} color={activeTab === 'requests' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'requests' ? PRIMARY : '#bdbdbd' }]}>Kérelmek</Text>
</TouchableOpacity>
</View>
);
}
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',
},
});

236
app/schedule.tsx Normal file
View File

@@ -0,0 +1,236 @@
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<any[]>([]);
const [loading, setLoading] = useState(false);
const router = useRouter();
useEffect(() => {
fetchWorkdays();
}, [currentMonth]);
async function fetchWorkdays() {
setLoading(true);
try {
const userCookieRaw = await SecureStore.getItemAsync('cookie');
if (!userCookieRaw) {
throw new Error('Nincs elmentett cookie, kérlek jelentkezz be újra!');
}
console.log('Lekért cookie:', userCookieRaw);
const userCookie = userCookieRaw.split(';')[0];
const year = getYear(currentMonth);
const month = getMonth(currentMonth) + 1;
console.log('Lekérdezett hónap:', year, month);
console.log('Beosztás API hívás:', { year, month });
console.log('Küldött cookie:', userCookie);
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',
'Referer': 'https://mymenu.mcdonalds.hu/',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'hu-HU,hu;q=0.9',
}
}
);
const cookie = response.headers['set-cookie']?.join('; ');
if (cookie) {
await SecureStore.setItemAsync('cookie', cookie);
} else {
console.warn('Nincs set-cookie fejléc a válaszban!');
}
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) {
const err = e as any;
if (err.response) {
console.log('API válasz hiba:', err.response.data);
} else {
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(
<View key={day.toString()} style={{ flex: 1, aspectRatio: 1, margin: 2 }}>
<View style={{
backgroundColor: bg,
borderRadius: 8,
borderWidth,
borderColor: PRIMARY,
justifyContent: 'center',
alignItems: 'center',
flex: 1,
}}>
<Text style={{ color, fontWeight: isSameDay(day, today) ? 'bold' : 'normal' }}>{format(day, 'd')}</Text>
{wd && <Text style={{ color, fontSize: 10 }}>{wd.text}</Text>}
</View>
</View>
);
day = addDays(day, 1);
}
rows.push(<View key={day.toString()} style={{ flexDirection: 'row' }}>{days}</View>);
days = [];
}
return (
<View>
<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>
<Text style={{ color: '#fff', fontSize: 18, fontWeight: 'bold' }}>{format(currentMonth, 'yyyy. MMMM')}</Text>
<TouchableOpacity onPress={() => setCurrentMonth(addMonths(currentMonth, 1))}><Ionicons name="chevron-forward" size={28} color="#fff" /></TouchableOpacity>
</View>
<View style={{ flexDirection: 'row', marginBottom: 4 }}>
{["H", "K", "Sz", "Cs", "P", "Szo", "V"].map(d => (
<Text key={d} style={{ flex: 1, color: '#bdbdbd', textAlign: 'center', fontWeight: 'bold' }}>{d}</Text>
))}
</View>
{loading ? (
<Text style={{ color: '#fff', textAlign: 'center', marginTop: 24 }}>Betöltés</Text>
) : (
<View>{rows}</View>
)}
</View>
);
}
return (
<ScrollView style={{ flex: 1, backgroundColor: BG }} contentContainerStyle={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center', minHeight: '100%', paddingBottom: 80 }}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', width: '100%' }}>
<View style={[styles.card, { alignSelf: 'center', marginTop: 48, marginBottom: 48 }]}>
<Text style={styles.label}>Beosztás naptár</Text>
{/* Hibaüzenet, ha nincs bejelentkezve vagy nincs beosztás */}
{(!loading && workdays.length === 0) && (
<Text style={{ color: '#ff5252', textAlign: 'center', marginBottom: 12, fontWeight: 'bold' }}>
Nincs beosztás vagy nem vagy bejelentkezve!
</Text>
)}
{renderCalendar()}
</View>
</View>
<View style={styles.navBar}>
<NavBar activeTab="schedule" />
</View>
</ScrollView>
);
}
function NavBar({ activeTab }: { activeTab: string }) {
const router = useRouter();
return (
<View style={styles.navBar}>
<TouchableOpacity style={styles.navItem} onPress={() => router.push('/profile')}>
<MaterialIcons name="person" size={28} color={activeTab === 'profile' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'profile' ? PRIMARY : '#bdbdbd' }]}>Profilom</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => router.push('/schedule')}>
<MaterialCommunityIcons name="calendar-month" size={28} color={activeTab === 'schedule' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'schedule' ? PRIMARY : '#bdbdbd' }]}>Beosztás</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem} onPress={() => router.push('/requests')}>
<Ionicons name="mail" size={28} color={activeTab === 'requests' ? PRIMARY : '#bdbdbd'} />
<Text style={[styles.navLabel, { color: activeTab === 'requests' ? PRIMARY : '#bdbdbd' }]}>Kérelmek</Text>
</TouchableOpacity>
</View>
);
}
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',
},
});

21
eas.json Normal file
View File

@@ -0,0 +1,21 @@
{
"cli": {
"version": ">= 16.17.3",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {}
}
}

View File

@@ -4,7 +4,6 @@
"version": "1.0.0",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
@@ -15,6 +14,8 @@
"@react-navigation/bottom-tabs": "^7.3.10",
"@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",
@@ -24,6 +25,7 @@
"expo-linking": "~7.1.7",
"expo-notifications": "~0.31.4",
"expo-router": "~5.1.4",
"expo-secure-store": "^14.2.3",
"expo-splash-screen": "~0.30.10",
"expo-status-bar": "~2.2.3",
"expo-symbols": "~0.4.5",
@@ -41,6 +43,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",

92
pnpm-lock.yaml generated
View File

@@ -20,6 +20,12 @@ importers:
'@react-navigation/native':
specifier: ^7.1.6
version: 7.1.16(react-native@0.79.5(@babel/core@7.28.0)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0)
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)
@@ -47,6 +53,9 @@ importers:
expo-router:
specifier: ~5.1.4
version: 5.1.4(e1c8eefe113e01b6d50ed7cbca38deea)
expo-secure-store:
specifier: ^14.2.3
version: 14.2.3(expo@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))
expo-splash-screen:
specifier: ~0.30.10
version: 0.30.10(expo@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))
@@ -93,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
@@ -1005,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==}
@@ -1347,10 +1363,16 @@ packages:
async-limiter@1.0.1:
resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
axios@1.11.0:
resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==}
babel-jest@29.7.0:
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -1569,6 +1591,10 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
commander@12.1.0:
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
engines: {node: '>=18'}
@@ -1638,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:
@@ -1693,6 +1722,10 @@ packages:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
@@ -2032,6 +2065,11 @@ packages:
react-native-reanimated:
optional: true
expo-secure-store@14.2.3:
resolution: {integrity: sha512-hYBbaAD70asKTFd/eZBKVu+9RTo9OSTMMLqXtzDF8ndUGjpc6tmRCoZtrMHlUo7qLtwL5jm+vpYVBWI8hxh/1Q==}
peerDependencies:
expo: '*'
expo-splash-screen@0.30.10:
resolution: {integrity: sha512-Tt9va/sLENQDQYeOQ6cdLdGvTZ644KR3YG9aRlnpcs2/beYjOX1LHT510EGzVN9ljUTg+1ebEo5GGt2arYtPjw==}
peerDependencies:
@@ -2155,6 +2193,15 @@ packages:
flow-enums-runtime@0.0.6:
resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==}
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
fontfaceobserver@2.3.0:
resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==}
@@ -2166,6 +2213,10 @@ packages:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
form-data@4.0.4:
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
engines: {node: '>= 6'}
freeport-async@2.0.0:
resolution: {integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==}
engines: {node: '>=8'}
@@ -3141,6 +3192,9 @@ packages:
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -5264,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':
@@ -5628,10 +5686,20 @@ snapshots:
async-limiter@1.0.1: {}
asynckit@0.4.0: {}
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.1.0
axios@1.11.0:
dependencies:
follow-redirects: 1.15.9
form-data: 4.0.4
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
babel-jest@29.7.0(@babel/core@7.28.0):
dependencies:
'@babel/core': 7.28.0
@@ -5917,6 +5985,10 @@ snapshots:
color-convert: 2.0.1
color-string: 1.9.1
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
commander@12.1.0: {}
commander@2.20.3: {}
@@ -6003,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
@@ -6041,6 +6115,8 @@ snapshots:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
delayed-stream@1.0.0: {}
depd@2.0.0: {}
destroy@1.2.0: {}
@@ -6514,6 +6590,10 @@ snapshots:
- react-native
- supports-color
expo-secure-store@14.2.3(expo@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)):
dependencies:
expo: 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)
expo-splash-screen@0.30.10(expo@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)):
dependencies:
'@expo/prebuild-config': 9.0.11
@@ -6667,6 +6747,8 @@ snapshots:
flow-enums-runtime@0.0.6: {}
follow-redirects@1.15.9: {}
fontfaceobserver@2.3.0: {}
for-each@0.3.5:
@@ -6678,6 +6760,14 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
form-data@4.0.4:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
hasown: 2.0.2
mime-types: 2.1.35
freeport-async@2.0.0: {}
fresh@0.5.2: {}
@@ -7744,6 +7834,8 @@ snapshots:
object-assign: 4.1.1
react-is: 16.13.1
proxy-from-env@1.1.0: {}
punycode@2.3.1: {}
qrcode-terminal@0.11.0: {}