diff --git a/mobile/App.tsx b/mobile/App.tsx deleted file mode 100644 index 0329d0c..0000000 --- a/mobile/App.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, Text, View } from 'react-native'; - -export default function App() { - return ( - - Open up App.tsx to start working on your app! - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -}); diff --git a/mobile/app.json b/mobile/app.json index 55afdac..847762c 100644 --- a/mobile/app.json +++ b/mobile/app.json @@ -1,34 +1,41 @@ { "expo": { - "name": "mobile", - "slug": "mobile", + "name": "Task Team", + "slug": "task-team", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/icon.png", - "userInterfaceStyle": "light", - "newArchEnabled": true, + "userInterfaceStyle": "automatic", "splash": { "image": "./assets/splash-icon.png", "resizeMode": "contain", - "backgroundColor": "#ffffff" + "backgroundColor": "#3B82F6" }, "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "info.hasdo.taskteam" }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", - "backgroundColor": "#ffffff" + "backgroundColor": "#3B82F6" }, - "edgeToEdgeEnabled": true, - "predictiveBackGestureEnabled": false + "package": "info.hasdo.taskteam" }, "web": { - "favicon": "./assets/favicon.png" + "bundler": "metro" }, + "scheme": "taskteam", "plugins": [ "expo-router", - "expo-secure-store" + "expo-secure-store", + [ + "expo-notifications", + { + "icon": "./assets/icon.png", + "color": "#3B82F6" + } + ] ] } } diff --git a/mobile/app/(tabs)/_layout.tsx b/mobile/app/(tabs)/_layout.tsx new file mode 100644 index 0000000..56cd7bb --- /dev/null +++ b/mobile/app/(tabs)/_layout.tsx @@ -0,0 +1,63 @@ +import { Tabs } from 'expo-router'; +import { Ionicons } from '@expo/vector-icons'; + +export default function TabLayout() { + return ( + + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + }} + /> + + ); +} diff --git a/mobile/app/(tabs)/calendar.tsx b/mobile/app/(tabs)/calendar.tsx new file mode 100644 index 0000000..27309d0 --- /dev/null +++ b/mobile/app/(tabs)/calendar.tsx @@ -0,0 +1,264 @@ +import { useState, useEffect, useCallback } from 'react'; +import { + View, + Text, + StyleSheet, + ScrollView, + RefreshControl, + TouchableOpacity, + Alert, +} from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { useAuthContext } from '../../lib/AuthContext'; +import * as api from '../../lib/api'; + +const DAYS = ['Po', 'Ut', 'St', 'Ct', 'Pa', 'So', 'Ne']; +const MONTHS = [ + 'Leden', 'Unor', 'Brezen', 'Duben', 'Kveten', 'Cerven', + 'Cervenec', 'Srpen', 'Zari', 'Rijen', 'Listopad', 'Prosinec', +]; + +function getDaysInMonth(year: number, month: number) { + return new Date(year, month + 1, 0).getDate(); +} + +function getFirstDayOfMonth(year: number, month: number) { + const day = new Date(year, month, 1).getDay(); + return day === 0 ? 6 : day - 1; // Monday = 0 +} + +export default function CalendarScreen() { + const { token } = useAuthContext(); + const [tasks, setTasks] = useState([]); + const [refreshing, setRefreshing] = useState(false); + const [currentDate, setCurrentDate] = useState(new Date()); + const [selectedDate, setSelectedDate] = useState(null); + + const year = currentDate.getFullYear(); + const month = currentDate.getMonth(); + + const loadTasks = useCallback(async () => { + if (!token) return; + try { + const res = await api.getTasks(token); + setTasks(res.data || []); + } catch (err: any) { + Alert.alert('Chyba', err.message); + } + }, [token]); + + useEffect(() => { + loadTasks(); + }, [loadTasks]); + + const onRefresh = useCallback(async () => { + setRefreshing(true); + await loadTasks(); + setRefreshing(false); + }, [loadTasks]); + + const prevMonth = () => { + setCurrentDate(new Date(year, month - 1, 1)); + setSelectedDate(null); + }; + + const nextMonth = () => { + setCurrentDate(new Date(year, month + 1, 1)); + setSelectedDate(null); + }; + + const daysInMonth = getDaysInMonth(year, month); + const firstDay = getFirstDayOfMonth(year, month); + + const tasksByDate: Record = {}; + tasks.forEach((t) => { + if (t.due_date) { + const d = t.due_date.substring(0, 10); + if (!tasksByDate[d]) tasksByDate[d] = []; + tasksByDate[d].push(t); + } + }); + + const today = new Date().toISOString().substring(0, 10); + + const selectedTasks = selectedDate ? tasksByDate[selectedDate] || [] : []; + + return ( + + } + > + {/* Month navigator */} + + + + + + {MONTHS[month]} {year} + + + + + + + {/* Day headers */} + + {DAYS.map((d) => ( + + {d} + + ))} + + + {/* Calendar grid */} + + {Array.from({ length: firstDay }).map((_, i) => ( + + ))} + {Array.from({ length: daysInMonth }).map((_, i) => { + const day = i + 1; + const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; + const hasTasks = !!tasksByDate[dateStr]; + const isToday = dateStr === today; + const isSelected = dateStr === selectedDate; + + return ( + setSelectedDate(dateStr)} + > + + {day} + + {hasTasks && ( + + )} + + ); + })} + + + {/* Selected date tasks */} + {selectedDate && ( + + + Ukoly na {selectedDate} + + {selectedTasks.length === 0 ? ( + Zadne ukoly + ) : ( + selectedTasks.map((t) => ( + + + + {t.title} + + + )) + )} + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#F8FAFC' }, + monthNav: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + padding: 16, + backgroundColor: '#fff', + }, + navBtn: { padding: 8 }, + monthTitle: { fontSize: 18, fontWeight: '700', color: '#1E293B' }, + dayHeaders: { + flexDirection: 'row', + backgroundColor: '#fff', + paddingBottom: 8, + borderBottomWidth: 1, + borderBottomColor: '#E2E8F0', + }, + dayHeader: { + flex: 1, + textAlign: 'center', + fontSize: 12, + fontWeight: '600', + color: '#64748B', + }, + grid: { + flexDirection: 'row', + flexWrap: 'wrap', + backgroundColor: '#fff', + padding: 4, + }, + dayCell: { + width: '14.28%', + aspectRatio: 1, + alignItems: 'center', + justifyContent: 'center', + }, + todayCell: { + backgroundColor: '#EFF6FF', + borderRadius: 20, + }, + selectedCell: { + backgroundColor: '#3B82F6', + borderRadius: 20, + }, + dayText: { fontSize: 15, color: '#1E293B' }, + todayText: { color: '#3B82F6', fontWeight: '700' }, + selectedText: { color: '#fff', fontWeight: '700' }, + dot: { + width: 5, + height: 5, + borderRadius: 3, + backgroundColor: '#3B82F6', + marginTop: 2, + }, + tasksSection: { + margin: 12, + padding: 16, + backgroundColor: '#fff', + borderRadius: 12, + }, + tasksSectionTitle: { fontSize: 16, fontWeight: '600', color: '#1E293B', marginBottom: 12 }, + noTasks: { color: '#94A3B8', fontSize: 14 }, + taskItem: { + flexDirection: 'row', + alignItems: 'center', + gap: 10, + paddingVertical: 8, + borderBottomWidth: 1, + borderBottomColor: '#F1F5F9', + }, + taskItemText: { fontSize: 14, color: '#1E293B', flex: 1 }, +}); diff --git a/mobile/app/(tabs)/chat.tsx b/mobile/app/(tabs)/chat.tsx new file mode 100644 index 0000000..5da5e6f --- /dev/null +++ b/mobile/app/(tabs)/chat.tsx @@ -0,0 +1,217 @@ +import { useState, useRef, useCallback } from 'react'; +import { + View, + Text, + TextInput, + FlatList, + TouchableOpacity, + StyleSheet, + KeyboardAvoidingView, + Platform, + ActivityIndicator, + Alert, +} from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { useAuthContext } from '../../lib/AuthContext'; +import * as api from '../../lib/api'; + +type Message = { + id: string; + role: 'user' | 'assistant'; + content: string; + timestamp: Date; +}; + +export default function ChatScreen() { + const { token } = useAuthContext(); + const [messages, setMessages] = useState([ + { + id: '0', + role: 'assistant', + content: 'Ahoj! Jsem vas AI asistent pro Task Team. Mohu vam pomoci s ukoly, planovani a organizaci. Na co se chcete zeptat?', + timestamp: new Date(), + }, + ]); + const [input, setInput] = useState(''); + const [sending, setSending] = useState(false); + const flatListRef = useRef(null); + + const sendMessage = useCallback(async () => { + if (!input.trim() || !token || sending) return; + + const userMsg: Message = { + id: Date.now().toString(), + role: 'user', + content: input.trim(), + timestamp: new Date(), + }; + + setMessages((prev) => [...prev, userMsg]); + setInput(''); + setSending(true); + + try { + const res = await api.sendChatMessage(token, userMsg.content); + const aiMsg: Message = { + id: (Date.now() + 1).toString(), + role: 'assistant', + content: res.data?.reply || 'Omlouvam se, nepodarilo se zpracovat odpoved.', + timestamp: new Date(), + }; + setMessages((prev) => [...prev, aiMsg]); + } catch (err: any) { + const errMsg: Message = { + id: (Date.now() + 1).toString(), + role: 'assistant', + content: `Chyba: ${err.message}`, + timestamp: new Date(), + }; + setMessages((prev) => [...prev, errMsg]); + } finally { + setSending(false); + } + }, [input, token, sending]); + + const renderMessage = ({ item }: { item: Message }) => { + const isUser = item.role === 'user'; + return ( + + {!isUser && ( + + + + )} + + + {item.content} + + + {item.timestamp.toLocaleTimeString('cs-CZ', { + hour: '2-digit', + minute: '2-digit', + })} + + + + ); + }; + + return ( + + item.id} + renderItem={renderMessage} + contentContainerStyle={styles.listContent} + onContentSizeChange={() => + flatListRef.current?.scrollToEnd({ animated: true }) + } + /> + + + + + {sending ? ( + + ) : ( + + )} + + + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#F8FAFC' }, + listContent: { padding: 12, paddingBottom: 4 }, + msgRow: { flexDirection: 'row', marginBottom: 12, maxWidth: '85%' }, + msgRowUser: { alignSelf: 'flex-end' }, + msgRowAi: { alignSelf: 'flex-start' }, + avatar: { + width: 32, + height: 32, + borderRadius: 16, + backgroundColor: '#EFF6FF', + alignItems: 'center', + justifyContent: 'center', + marginRight: 8, + marginTop: 4, + }, + bubble: { borderRadius: 16, padding: 12, maxWidth: '100%' }, + bubbleUser: { + backgroundColor: '#3B82F6', + borderBottomRightRadius: 4, + }, + bubbleAi: { + backgroundColor: '#fff', + borderBottomLeftRadius: 4, + borderWidth: 1, + borderColor: '#E2E8F0', + }, + bubbleText: { fontSize: 15, lineHeight: 21, color: '#1E293B' }, + timestamp: { fontSize: 10, color: '#94A3B8', marginTop: 4, textAlign: 'right' }, + inputBar: { + flexDirection: 'row', + alignItems: 'flex-end', + padding: 12, + backgroundColor: '#fff', + borderTopWidth: 1, + borderTopColor: '#E2E8F0', + }, + input: { + flex: 1, + borderWidth: 1, + borderColor: '#E2E8F0', + borderRadius: 20, + paddingHorizontal: 16, + paddingVertical: 10, + fontSize: 15, + maxHeight: 100, + backgroundColor: '#F8FAFC', + }, + sendBtn: { + width: 42, + height: 42, + borderRadius: 21, + backgroundColor: '#3B82F6', + alignItems: 'center', + justifyContent: 'center', + marginLeft: 8, + }, + sendBtnDisabled: { backgroundColor: '#94A3B8' }, +}); diff --git a/mobile/app/(tabs)/goals.tsx b/mobile/app/(tabs)/goals.tsx new file mode 100644 index 0000000..d27c901 --- /dev/null +++ b/mobile/app/(tabs)/goals.tsx @@ -0,0 +1,162 @@ +import { useState, useEffect, useCallback } from 'react'; +import { + View, + Text, + FlatList, + StyleSheet, + RefreshControl, + Alert, +} from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { useAuthContext } from '../../lib/AuthContext'; +import * as api from '../../lib/api'; + +export default function GoalsScreen() { + const { token } = useAuthContext(); + const [goals, setGoals] = useState([]); + const [refreshing, setRefreshing] = useState(false); + + const loadGoals = useCallback(async () => { + if (!token) return; + try { + const res = await api.getGoals(token); + setGoals(res.data || []); + } catch (err: any) { + Alert.alert('Chyba', err.message); + } + }, [token]); + + useEffect(() => { + loadGoals(); + }, [loadGoals]); + + const onRefresh = useCallback(async () => { + setRefreshing(true); + await loadGoals(); + setRefreshing(false); + }, [loadGoals]); + + const renderGoal = ({ item }: { item: any }) => { + const progress = item.progress || 0; + const total = item.target || 100; + const pct = Math.min(Math.round((progress / total) * 100), 100); + + return ( + + + + = 100 ? 'trophy' : 'flag-outline'} + size={24} + color={pct >= 100 ? '#F59E0B' : '#3B82F6'} + /> + + + {item.title} + {item.description && ( + + {item.description} + + )} + + {pct}% + + + + = 100 ? '#22C55E' : pct >= 50 ? '#3B82F6' : '#F59E0B', + }, + ]} + /> + + + + + {progress} / {total} + + {item.deadline && ( + + {' '} + {new Date(item.deadline).toLocaleDateString('cs-CZ')} + + )} + + + ); + }; + + return ( + + item.id?.toString()} + renderItem={renderGoal} + refreshControl={ + + } + contentContainerStyle={styles.listContent} + ListEmptyComponent={ + + + Zatim zadne cile + + Vytvorte cile ve webove aplikaci + + + } + /> + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#F8FAFC' }, + listContent: { padding: 12, paddingBottom: 20 }, + goalCard: { + backgroundColor: '#fff', + borderRadius: 12, + padding: 16, + marginBottom: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 3, + elevation: 2, + }, + goalHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 }, + goalIcon: { + width: 40, + height: 40, + borderRadius: 10, + backgroundColor: '#EFF6FF', + alignItems: 'center', + justifyContent: 'center', + }, + goalInfo: { flex: 1, marginLeft: 12 }, + goalTitle: { fontSize: 16, fontWeight: '600', color: '#1E293B' }, + goalDesc: { fontSize: 13, color: '#64748B', marginTop: 2 }, + goalPct: { fontSize: 18, fontWeight: '700', color: '#3B82F6' }, + progressBarBg: { + height: 8, + borderRadius: 4, + backgroundColor: '#E2E8F0', + }, + progressBarFill: { + height: 8, + borderRadius: 4, + }, + goalFooter: { + flexDirection: 'row', + justifyContent: 'space-between', + marginTop: 8, + }, + goalStat: { fontSize: 12, color: '#64748B' }, + goalDeadline: { fontSize: 12, color: '#94A3B8' }, + empty: { alignItems: 'center', marginTop: 60 }, + emptyText: { color: '#94A3B8', fontSize: 16, marginTop: 12 }, + emptySubtext: { color: '#CBD5E1', fontSize: 13, marginTop: 4 }, +}); diff --git a/mobile/app/(tabs)/index.tsx b/mobile/app/(tabs)/index.tsx new file mode 100644 index 0000000..ed68e62 --- /dev/null +++ b/mobile/app/(tabs)/index.tsx @@ -0,0 +1,329 @@ +import { useState, useEffect, useCallback } from 'react'; +import { + View, + Text, + FlatList, + TouchableOpacity, + StyleSheet, + RefreshControl, + Alert, + TextInput, + Modal, +} from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { useAuthContext } from '../../lib/AuthContext'; +import * as api from '../../lib/api'; + +const PRIORITY_COLORS: Record = { + high: '#EF4444', + medium: '#F59E0B', + low: '#22C55E', +}; + +const STATUS_ICONS: Record = { + todo: 'ellipse-outline', + in_progress: 'time-outline', + done: 'checkmark-circle', +}; + +export default function TasksScreen() { + const { token } = useAuthContext(); + const [tasks, setTasks] = useState([]); + const [groups, setGroups] = useState([]); + const [refreshing, setRefreshing] = useState(false); + const [showAdd, setShowAdd] = useState(false); + const [newTitle, setNewTitle] = useState(''); + const [filter, setFilter] = useState('all'); + + const loadData = useCallback(async () => { + if (!token) return; + try { + const [tasksRes, groupsRes] = await Promise.all([ + api.getTasks(token), + api.getGroups(token), + ]); + setTasks(tasksRes.data || []); + setGroups(groupsRes.data || []); + } catch (err: any) { + Alert.alert('Chyba', err.message); + } + }, [token]); + + useEffect(() => { + loadData(); + }, [loadData]); + + const onRefresh = useCallback(async () => { + setRefreshing(true); + await loadData(); + setRefreshing(false); + }, [loadData]); + + const addTask = async () => { + if (!newTitle.trim() || !token) return; + try { + await api.createTask(token, { title: newTitle.trim() }); + setNewTitle(''); + setShowAdd(false); + await loadData(); + } catch (err: any) { + Alert.alert('Chyba', err.message); + } + }; + + const toggleTask = async (task: any) => { + if (!token) return; + const newStatus = task.status === 'done' ? 'todo' : 'done'; + try { + await api.updateTask(token, task.id, { status: newStatus }); + await loadData(); + } catch (err: any) { + Alert.alert('Chyba', err.message); + } + }; + + const filteredTasks = + filter === 'all' ? tasks : tasks.filter((t) => t.status === filter); + + const getGroupColor = (groupId: string) => { + const group = groups.find((g) => g.id === groupId); + return group?.color || '#64748B'; + }; + + const renderTask = ({ item }: { item: any }) => ( + toggleTask(item)} + activeOpacity={0.7} + > + + + + + {item.title} + + {item.group_name && ( + + + {item.group_name} + + + )} + + + {item.priority && ( + + )} + + ); + + return ( + + {/* Filter bar */} + + {['all', 'todo', 'in_progress', 'done'].map((f) => ( + setFilter(f)} + > + + {f === 'all' + ? 'Vse' + : f === 'todo' + ? 'K provedeni' + : f === 'in_progress' + ? 'Rozpracovano' + : 'Hotovo'} + + + ))} + + + {/* Task list */} + item.id?.toString()} + renderItem={renderTask} + refreshControl={ + + } + contentContainerStyle={styles.listContent} + ListEmptyComponent={ + + + Zatim zadne ukoly + + } + /> + + {/* FAB */} + setShowAdd(true)} + > + + + + {/* Add task modal */} + + + + Novy ukol + + + { + setShowAdd(false); + setNewTitle(''); + }} + > + Zrusit + + + Pridat + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#F8FAFC' }, + filterBar: { + flexDirection: 'row', + padding: 12, + gap: 8, + backgroundColor: '#fff', + borderBottomWidth: 1, + borderBottomColor: '#E2E8F0', + }, + filterBtn: { + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: 16, + backgroundColor: '#F1F5F9', + }, + filterBtnActive: { backgroundColor: '#3B82F6' }, + filterText: { fontSize: 13, color: '#64748B' }, + filterTextActive: { color: '#fff', fontWeight: '600' }, + listContent: { padding: 12, paddingBottom: 80 }, + taskCard: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: '#fff', + borderRadius: 12, + padding: 16, + marginBottom: 8, + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 3, + elevation: 2, + }, + taskLeft: { flexDirection: 'row', alignItems: 'center', flex: 1 }, + taskInfo: { marginLeft: 12, flex: 1 }, + taskTitle: { fontSize: 15, color: '#1E293B', fontWeight: '500' }, + taskDone: { + textDecorationLine: 'line-through', + color: '#94A3B8', + }, + groupBadge: { marginTop: 4, paddingHorizontal: 8, paddingVertical: 2, borderRadius: 8, alignSelf: 'flex-start' }, + groupBadgeText: { fontSize: 11, fontWeight: '600' }, + priorityDot: { width: 10, height: 10, borderRadius: 5 }, + empty: { alignItems: 'center', marginTop: 60 }, + emptyText: { color: '#94A3B8', fontSize: 16, marginTop: 12 }, + fab: { + position: 'absolute', + right: 20, + bottom: 20, + width: 56, + height: 56, + borderRadius: 28, + backgroundColor: '#3B82F6', + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#3B82F6', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 8, + elevation: 6, + }, + modalOverlay: { + flex: 1, + backgroundColor: 'rgba(0,0,0,0.4)', + justifyContent: 'flex-end', + }, + modalContent: { + backgroundColor: '#fff', + borderTopLeftRadius: 20, + borderTopRightRadius: 20, + padding: 24, + paddingBottom: 40, + }, + modalTitle: { fontSize: 18, fontWeight: '700', color: '#1E293B', marginBottom: 16 }, + input: { + borderWidth: 1, + borderColor: '#E2E8F0', + borderRadius: 12, + padding: 14, + fontSize: 16, + backgroundColor: '#F8FAFC', + }, + modalActions: { + flexDirection: 'row', + justifyContent: 'flex-end', + gap: 12, + marginTop: 16, + }, + cancelBtn: { paddingHorizontal: 20, paddingVertical: 10 }, + cancelText: { color: '#64748B', fontSize: 15 }, + addBtn: { + backgroundColor: '#3B82F6', + paddingHorizontal: 24, + paddingVertical: 10, + borderRadius: 10, + }, + addBtnText: { color: '#fff', fontSize: 15, fontWeight: '600' }, +}); diff --git a/mobile/app/(tabs)/settings.tsx b/mobile/app/(tabs)/settings.tsx new file mode 100644 index 0000000..9bb5621 --- /dev/null +++ b/mobile/app/(tabs)/settings.tsx @@ -0,0 +1,220 @@ +import { useState } from 'react'; +import { + View, + Text, + StyleSheet, + ScrollView, + TouchableOpacity, + Switch, + Alert, +} from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { useAuthContext } from '../../lib/AuthContext'; +import Constants from 'expo-constants'; + +export default function SettingsScreen() { + const { user, logout } = useAuthContext(); + const [darkMode, setDarkMode] = useState(false); + const [notifications, setNotifications] = useState(true); + const [language, setLanguage] = useState<'cs' | 'en'>('cs'); + + const handleLogout = () => { + Alert.alert( + 'Odhlaseni', + 'Opravdu se chcete odhlasit?', + [ + { text: 'Zrusit', style: 'cancel' }, + { + text: 'Odhlasit', + style: 'destructive', + onPress: logout, + }, + ], + ); + }; + + const renderSection = (title: string, children: React.ReactNode) => ( + + {title} + {children} + + ); + + const renderRow = ( + icon: string, + label: string, + right?: React.ReactNode, + onPress?: () => void, + ) => ( + + + + {label} + + {right || (onPress && )} + + ); + + return ( + + {/* User card */} + + + + {(user?.name || 'U')[0].toUpperCase()} + + + + {user?.name || 'Uzivatel'} + {user?.email || ''} + + + + {renderSection('Predvolby', ( + <> + {renderRow( + 'moon-outline', + 'Tmavy rezim', + , + )} + {renderRow( + 'notifications-outline', + 'Oznameni', + , + )} + {renderRow( + 'language-outline', + 'Jazyk', + setLanguage(language === 'cs' ? 'en' : 'cs')} + > + + {language === 'cs' ? 'Cestina' : 'English'} + + , + )} + + ))} + + {renderSection('Ucet', ( + <> + {renderRow('person-outline', 'Upravit profil', undefined, () => + Alert.alert('Info', 'Bude k dispozici brzy'), + )} + {renderRow('lock-closed-outline', 'Zmenit heslo', undefined, () => + Alert.alert('Info', 'Bude k dispozici brzy'), + )} + + ))} + + {renderSection('Informace', ( + <> + {renderRow('information-circle-outline', 'Verze', ( + + {Constants.expoConfig?.version || '1.0.0'} + + ))} + {renderRow('document-text-outline', 'Podminky pouziti', undefined, () => + Alert.alert('Info', 'Bude k dispozici brzy'), + )} + + ))} + + {/* Logout */} + + + Odhlasit se + + + + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#F8FAFC' }, + userCard: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: '#fff', + padding: 20, + margin: 12, + borderRadius: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 3, + elevation: 2, + }, + userAvatar: { + width: 56, + height: 56, + borderRadius: 28, + backgroundColor: '#3B82F6', + alignItems: 'center', + justifyContent: 'center', + }, + userAvatarText: { color: '#fff', fontSize: 22, fontWeight: '700' }, + userInfo: { marginLeft: 16, flex: 1 }, + userName: { fontSize: 18, fontWeight: '600', color: '#1E293B' }, + userEmail: { fontSize: 14, color: '#64748B', marginTop: 2 }, + section: { marginTop: 12, marginHorizontal: 12 }, + sectionTitle: { + fontSize: 13, + fontWeight: '600', + color: '#64748B', + textTransform: 'uppercase', + marginBottom: 6, + marginLeft: 4, + }, + sectionContent: { + backgroundColor: '#fff', + borderRadius: 12, + overflow: 'hidden', + }, + row: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 16, + paddingVertical: 14, + borderBottomWidth: 1, + borderBottomColor: '#F1F5F9', + }, + rowLeft: { flexDirection: 'row', alignItems: 'center', gap: 12 }, + rowLabel: { fontSize: 15, color: '#1E293B' }, + langToggle: { + backgroundColor: '#F1F5F9', + paddingHorizontal: 12, + paddingVertical: 4, + borderRadius: 8, + }, + langText: { fontSize: 13, color: '#3B82F6', fontWeight: '600' }, + versionText: { fontSize: 14, color: '#94A3B8' }, + logoutBtn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 8, + marginTop: 24, + marginHorizontal: 12, + paddingVertical: 14, + backgroundColor: '#FEF2F2', + borderRadius: 12, + }, + logoutText: { color: '#EF4444', fontSize: 16, fontWeight: '600' }, +}); diff --git a/mobile/app/_layout.tsx b/mobile/app/_layout.tsx new file mode 100644 index 0000000..27cc906 --- /dev/null +++ b/mobile/app/_layout.tsx @@ -0,0 +1,32 @@ +import { useEffect } from 'react'; +import { Slot, useRouter, useSegments } from 'expo-router'; +import { StatusBar } from 'expo-status-bar'; +import { AuthProvider, useAuthContext } from '../lib/AuthContext'; + +function AuthGate() { + const { token, loading } = useAuthContext(); + const segments = useSegments(); + const router = useRouter(); + + useEffect(() => { + if (loading) return; + const inAuthGroup = segments[0] === 'login'; + + if (!token && !inAuthGroup) { + router.replace('/login'); + } else if (token && inAuthGroup) { + router.replace('/'); + } + }, [token, loading, segments]); + + return ; +} + +export default function RootLayout() { + return ( + + + + + ); +} diff --git a/mobile/app/login.tsx b/mobile/app/login.tsx new file mode 100644 index 0000000..c55080b --- /dev/null +++ b/mobile/app/login.tsx @@ -0,0 +1,256 @@ +import { useState } from 'react'; +import { + View, + Text, + TextInput, + TouchableOpacity, + StyleSheet, + KeyboardAvoidingView, + Platform, + ScrollView, + Alert, + ActivityIndicator, +} from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { useAuthContext } from '../lib/AuthContext'; + +export default function LoginScreen() { + const { login, register } = useAuthContext(); + const [mode, setMode] = useState<'login' | 'register'>('login'); + const [email, setEmail] = useState(''); + const [name, setName] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [loading, setLoading] = useState(false); + + const handleSubmit = async () => { + if (!email.trim() || !password.trim()) { + Alert.alert('Chyba', 'Vyplnte vsechna pole'); + return; + } + if (mode === 'register' && !name.trim()) { + Alert.alert('Chyba', 'Vyplnte jmeno'); + return; + } + + setLoading(true); + try { + if (mode === 'login') { + await login(email.trim(), password); + } else { + await register(email.trim(), name.trim(), password); + } + } catch (err: any) { + Alert.alert('Chyba', err.message || 'Nepodarilo se prihlasit'); + } finally { + setLoading(false); + } + }; + + return ( + + + {/* Logo */} + + + + + Task Team + + Spravujte ukoly, cile a tymovou spolupraci + + + + {/* Form */} + + {/* Mode toggle */} + + setMode('login')} + > + + Prihlaseni + + + setMode('register')} + > + + Registrace + + + + + {mode === 'register' && ( + + + + + )} + + + + + + + + + + setShowPassword(!showPassword)} + style={styles.eyeBtn} + > + + + + + + {loading ? ( + + ) : ( + + {mode === 'login' ? 'Prihlasit se' : 'Zaregistrovat se'} + + )} + + + + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#F8FAFC' }, + scrollContent: { + flexGrow: 1, + justifyContent: 'center', + padding: 24, + }, + logoSection: { alignItems: 'center', marginBottom: 40 }, + logoCircle: { + width: 80, + height: 80, + borderRadius: 20, + backgroundColor: '#3B82F6', + alignItems: 'center', + justifyContent: 'center', + marginBottom: 16, + }, + appName: { fontSize: 28, fontWeight: '800', color: '#1E293B' }, + appDesc: { + fontSize: 15, + color: '#64748B', + textAlign: 'center', + marginTop: 8, + }, + form: {}, + modeToggle: { + flexDirection: 'row', + backgroundColor: '#E2E8F0', + borderRadius: 12, + padding: 4, + marginBottom: 24, + }, + modeBtn: { + flex: 1, + paddingVertical: 10, + borderRadius: 10, + alignItems: 'center', + }, + modeBtnActive: { backgroundColor: '#fff' }, + modeBtnText: { fontSize: 15, color: '#64748B', fontWeight: '500' }, + modeBtnTextActive: { color: '#3B82F6', fontWeight: '700' }, + inputGroup: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: '#fff', + borderRadius: 12, + borderWidth: 1, + borderColor: '#E2E8F0', + marginBottom: 12, + paddingHorizontal: 14, + }, + inputIcon: { marginRight: 10 }, + input: { + flex: 1, + paddingVertical: 14, + fontSize: 16, + color: '#1E293B', + }, + eyeBtn: { padding: 4 }, + submitBtn: { + backgroundColor: '#3B82F6', + borderRadius: 12, + paddingVertical: 16, + alignItems: 'center', + marginTop: 8, + shadowColor: '#3B82F6', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.2, + shadowRadius: 8, + elevation: 4, + }, + submitBtnDisabled: { backgroundColor: '#94A3B8' }, + submitBtnText: { color: '#fff', fontSize: 17, fontWeight: '700' }, +}); diff --git a/mobile/index.ts b/mobile/index.ts index 1d6e981..5b83418 100644 --- a/mobile/index.ts +++ b/mobile/index.ts @@ -1,8 +1 @@ -import { registerRootComponent } from 'expo'; - -import App from './App'; - -// registerRootComponent calls AppRegistry.registerComponent('main', () => App); -// It also ensures that whether you load the app in Expo Go or in a native build, -// the environment is set up appropriately -registerRootComponent(App); +import 'expo-router/entry'; diff --git a/mobile/lib/AuthContext.tsx b/mobile/lib/AuthContext.tsx new file mode 100644 index 0000000..20dae10 --- /dev/null +++ b/mobile/lib/AuthContext.tsx @@ -0,0 +1,17 @@ +import React, { createContext, useContext } from 'react'; +import { useAuth } from './useAuth'; + +type AuthContextType = ReturnType; + +const AuthContext = createContext(null); + +export function AuthProvider({ children }: { children: React.ReactNode }) { + const auth = useAuth(); + return {children}; +} + +export function useAuthContext(): AuthContextType { + const ctx = useContext(AuthContext); + if (!ctx) throw new Error('useAuthContext must be used within AuthProvider'); + return ctx; +} diff --git a/mobile/lib/api.ts b/mobile/lib/api.ts new file mode 100644 index 0000000..aff15d4 --- /dev/null +++ b/mobile/lib/api.ts @@ -0,0 +1,64 @@ +const API_BASE = 'https://api.hasdo.info'; + +export async function apiFetch( + path: string, + opts: { method?: string; body?: any; token?: string } = {} +): Promise { + const headers: Record = { 'Content-Type': 'application/json' }; + if (opts.token) headers['Authorization'] = `Bearer ${opts.token}`; + + const res = await fetch(`${API_BASE}${path}`, { + method: opts.method || 'GET', + headers, + body: opts.body ? JSON.stringify(opts.body) : undefined, + }); + + if (!res.ok) { + const err = await res.json().catch(() => ({ message: `HTTP ${res.status}` })); + throw new Error(err.message || `HTTP ${res.status}`); + } + + return res.json(); +} + +// Auth +export const login = (data: { email: string; password: string }) => + apiFetch<{ data: { token: string; user: any } }>('/api/v1/auth/login', { + method: 'POST', + body: data, + }); + +export const register = (data: { email: string; name: string; password: string }) => + apiFetch<{ data: { token: string; user: any } }>('/api/v1/auth/register', { + method: 'POST', + body: data, + }); + +// Tasks +export const getTasks = (token: string) => + apiFetch<{ data: any[] }>('/api/v1/tasks', { token }); + +export const createTask = (token: string, data: any) => + apiFetch<{ data: any }>('/api/v1/tasks', { method: 'POST', body: data, token }); + +export const updateTask = (token: string, id: string, data: any) => + apiFetch<{ data: any }>(`/api/v1/tasks/${id}`, { method: 'PUT', body: data, token }); + +export const deleteTask = (token: string, id: string) => + apiFetch(`/api/v1/tasks/${id}`, { method: 'DELETE', token }); + +// Groups +export const getGroups = (token: string) => + apiFetch<{ data: any[] }>('/api/v1/groups', { token }); + +// Goals +export const getGoals = (token: string) => + apiFetch<{ data: any[] }>('/api/v1/goals', { token }); + +// Chat +export const sendChatMessage = (token: string, message: string) => + apiFetch<{ data: { reply: string } }>('/api/v1/chat', { + method: 'POST', + body: { message }, + token, + }); diff --git a/mobile/lib/auth.ts b/mobile/lib/auth.ts new file mode 100644 index 0000000..84afc95 --- /dev/null +++ b/mobile/lib/auth.ts @@ -0,0 +1,60 @@ +import * as SecureStore from 'expo-secure-store'; +import { Platform } from 'react-native'; + +const TOKEN_KEY = 'taskteam_token'; +const USER_KEY = 'taskteam_user'; + +export async function getToken(): Promise { + if (Platform.OS === 'web') { + return localStorage.getItem(TOKEN_KEY); + } + return SecureStore.getItemAsync(TOKEN_KEY); +} + +export async function setToken(token: string): Promise { + if (Platform.OS === 'web') { + localStorage.setItem(TOKEN_KEY, token); + return; + } + await SecureStore.setItemAsync(TOKEN_KEY, token); +} + +export async function removeToken(): Promise { + if (Platform.OS === 'web') { + localStorage.removeItem(TOKEN_KEY); + return; + } + await SecureStore.deleteItemAsync(TOKEN_KEY); +} + +export async function getUser(): Promise { + let raw: string | null; + if (Platform.OS === 'web') { + raw = localStorage.getItem(USER_KEY); + } else { + raw = await SecureStore.getItemAsync(USER_KEY); + } + if (!raw) return null; + try { + return JSON.parse(raw); + } catch { + return null; + } +} + +export async function setUser(user: any): Promise { + const raw = JSON.stringify(user); + if (Platform.OS === 'web') { + localStorage.setItem(USER_KEY, raw); + return; + } + await SecureStore.setItemAsync(USER_KEY, raw); +} + +export async function removeUser(): Promise { + if (Platform.OS === 'web') { + localStorage.removeItem(USER_KEY); + return; + } + await SecureStore.deleteItemAsync(USER_KEY); +} diff --git a/mobile/lib/useAuth.ts b/mobile/lib/useAuth.ts new file mode 100644 index 0000000..02eceef --- /dev/null +++ b/mobile/lib/useAuth.ts @@ -0,0 +1,44 @@ +import { useState, useEffect, useCallback } from 'react'; +import { getToken, setToken, removeToken, getUser, setUser, removeUser } from './auth'; +import * as api from './api'; + +export function useAuth() { + const [token, setTokenState] = useState(null); + const [user, setUserState] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + (async () => { + const t = await getToken(); + const u = await getUser(); + setTokenState(t); + setUserState(u); + setLoading(false); + })(); + }, []); + + const loginFn = useCallback(async (email: string, password: string) => { + const res = await api.login({ email, password }); + await setToken(res.data.token); + await setUser(res.data.user); + setTokenState(res.data.token); + setUserState(res.data.user); + }, []); + + const registerFn = useCallback(async (email: string, name: string, password: string) => { + const res = await api.register({ email, name, password }); + await setToken(res.data.token); + await setUser(res.data.user); + setTokenState(res.data.token); + setUserState(res.data.user); + }, []); + + const logout = useCallback(async () => { + await removeToken(); + await removeUser(); + setTokenState(null); + setUserState(null); + }, []); + + return { token, user, loading, login: loginFn, register: registerFn, logout }; +} diff --git a/mobile/package-lock.json b/mobile/package-lock.json index 7199218..fc95645 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -1,13 +1,14 @@ { - "name": "mobile", + "name": "task-team-mobile", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "mobile", + "name": "task-team-mobile", "version": "1.0.0", "dependencies": { + "@expo/vector-icons": "^14.0.4", "@react-navigation/native": "^7.2.2", "expo": "~54.0.33", "expo-constants": "~18.0.13", @@ -17,12 +18,17 @@ "expo-router": "~6.0.23", "expo-secure-store": "~15.0.8", "expo-status-bar": "~3.0.9", + "nativewind": "^4.1.23", "react": "19.1.0", + "react-dom": "19.1.0", "react-native": "0.81.5", + "react-native-css-interop": "^0.2.3", "react-native-gesture-handler": "^2.30.1", "react-native-reanimated": "^4.3.0", "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.16.0" + "react-native-screens": "~4.16.0", + "react-native-web": "^0.21.0", + "tailwindcss": "^3.4.19" }, "devDependencies": { "@types/react": "~19.1.0", @@ -43,6 +49,18 @@ } } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -2240,6 +2258,17 @@ "integrity": "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==", "license": "MIT" }, + "node_modules/@expo/vector-icons": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.1.0.tgz", + "integrity": "sha512-7T09UE9h8QDTsUeMGymB4i+iqvtEeaO5VvUjryFB4tugDTG/bkzViWA74hm5pfjjDEhYMXWaX112mcvhccmIwQ==", + "license": "MIT", + "peerDependencies": { + "expo-font": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/@expo/ws-tunnel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.6.tgz", @@ -2717,6 +2746,41 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@radix-ui/primitive": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", @@ -3756,6 +3820,12 @@ "node": ">=10" } }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "license": "MIT" + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -4138,6 +4208,18 @@ "node": ">=0.6" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bplist-creator": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", @@ -4321,6 +4403,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001781", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", @@ -4355,6 +4446,42 @@ "node": ">=4" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -4544,6 +4671,19 @@ "node": ">= 10" } }, + "node_modules/comment-json": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.6.2.tgz", + "integrity": "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==", + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -4653,6 +4793,15 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4667,6 +4816,27 @@ "node": ">= 8" } }, + "node_modules/css-in-js-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", + "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", + "license": "MIT", + "dependencies": { + "hyphenate-style-name": "^1.0.3" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -4807,6 +4977,18 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -5067,6 +5249,21 @@ "expo": "*" } }, + "node_modules/expo-font": { + "version": "55.0.4", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-55.0.4.tgz", + "integrity": "sha512-ZKeGTFffPygvY5dM/9ATM2p7QDkhsaHopH7wFAWgP2lKzqUMS9B/RxCvw5CaObr9Ro7x9YptyeRKX2HmgmMfrg==", + "license": "MIT", + "peer": true, + "dependencies": { + "fontfaceobserver": "^2.1.0" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/expo-linking": { "version": "8.0.11", "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.11.tgz", @@ -5928,12 +6125,49 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "license": "MIT" }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -5943,6 +6177,62 @@ "bser": "2.1.1" } }, + "node_modules/fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "license": "MIT", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "license": "MIT" + }, + "node_modules/fbjs/node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/fbjs/node_modules/ua-parser-js": { + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", + "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6192,6 +6482,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/glob/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -6396,6 +6698,12 @@ "node": ">= 14" } }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "license": "BSD-3-Clause" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -6472,6 +6780,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/inline-style-prefixer": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz", + "integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==", + "license": "MIT", + "dependencies": { + "css-in-js-utils": "^3.1.0" + } + }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -6503,6 +6820,18 @@ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", "license": "MIT" }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -6545,6 +6874,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -6573,6 +6911,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -7095,6 +7445,16 @@ "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", "license": "MIT" }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "peer": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7445,6 +7805,18 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -7544,6 +7916,15 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/metro": { "version": "0.83.3", "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.3.tgz", @@ -8044,6 +8425,23 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nativewind": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/nativewind/-/nativewind-4.2.3.tgz", + "integrity": "sha512-HglF1v6A8CqBFpXWs0d3yf4qQGurrreLuyE8FTRI/VDH8b0npZa2SDG5tviTkLiBg0s5j09mQALZOjxuocgMLA==", + "license": "MIT", + "dependencies": { + "comment-json": "^4.2.5", + "debug": "^4.3.7", + "react-native-css-interop": "0.2.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "tailwindcss": ">3.3.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -8059,6 +8457,26 @@ "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-forge": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", @@ -8131,6 +8549,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-is": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", @@ -8420,6 +8847,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", @@ -8480,6 +8916,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", @@ -8489,6 +8926,134 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -8611,6 +9176,26 @@ "inherits": "~2.0.3" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -8640,6 +9225,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -8655,24 +9241,18 @@ } }, "node_modules/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", "peer": true, "dependencies": { - "scheduler": "^0.27.0" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^19.2.4" + "react": "^19.1.0" } }, - "node_modules/react-dom/node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, "node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", @@ -8702,6 +9282,7 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", "license": "MIT", + "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.81.5", @@ -8754,6 +9335,277 @@ } } }, + "node_modules/react-native-css-interop": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.2.3.tgz", + "integrity": "sha512-wc+JI7iUfdFBqnE18HhMTtD0q9vkhuMczToA87UdHGWwMyxdT5sCcNy+i4KInPCE855IY0Ic8kLQqecAIBWz7w==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.3.7", + "lightningcss": "~1.27.0", + "semver": "^7.6.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": ">=18", + "react-native": "*", + "react-native-reanimated": ">=3.6.2", + "tailwindcss": "~3" + }, + "peerDependenciesMeta": { + "react-native-safe-area-context": { + "optional": true + }, + "react-native-svg": { + "optional": true + } + } + }, + "node_modules/react-native-css-interop/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.27.0.tgz", + "integrity": "sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.27.0", + "lightningcss-darwin-x64": "1.27.0", + "lightningcss-freebsd-x64": "1.27.0", + "lightningcss-linux-arm-gnueabihf": "1.27.0", + "lightningcss-linux-arm64-gnu": "1.27.0", + "lightningcss-linux-arm64-musl": "1.27.0", + "lightningcss-linux-x64-gnu": "1.27.0", + "lightningcss-linux-x64-musl": "1.27.0", + "lightningcss-win32-arm64-msvc": "1.27.0", + "lightningcss-win32-x64-msvc": "1.27.0" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-darwin-arm64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz", + "integrity": "sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-darwin-x64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.27.0.tgz", + "integrity": "sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-freebsd-x64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz", + "integrity": "sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz", + "integrity": "sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz", + "integrity": "sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz", + "integrity": "sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.27.0.tgz", + "integrity": "sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-x64-musl": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.27.0.tgz", + "integrity": "sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz", + "integrity": "sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.27.0.tgz", + "integrity": "sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.30.1.tgz", @@ -8823,6 +9675,39 @@ "react-native": "*" } }, + "node_modules/react-native-web": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", + "integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.6", + "@react-native/normalize-colors": "^0.74.1", + "fbjs": "^3.0.4", + "inline-style-prefixer": "^7.0.1", + "memoize-one": "^6.0.0", + "nullthrows": "^1.1.1", + "postcss-value-parser": "^4.2.0", + "styleq": "^0.1.3" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-native-web/node_modules/@react-native/normalize-colors": { + "version": "0.74.89", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz", + "integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==", + "license": "MIT" + }, + "node_modules/react-native-web/node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, "node_modules/react-native-worklets": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.8.1.tgz", @@ -9012,6 +9897,27 @@ } } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -9168,6 +10074,16 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -9227,6 +10143,29 @@ "node": "*" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9416,6 +10355,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -9679,6 +10624,12 @@ "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", "license": "MIT" }, + "node_modules/styleq": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.1.3.tgz", + "integrity": "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", @@ -9768,6 +10719,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tar": { "version": "7.5.13", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", @@ -9990,6 +10979,12 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -10228,6 +11223,12 @@ "which-typed-array": "^1.1.2" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -10499,6 +11500,16 @@ "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", "license": "MIT" }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/whatwg-url-without-unicode": { "version": "8.0.0-3", "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", @@ -10513,6 +11524,12 @@ "node": ">=10" } }, + "node_modules/whatwg-url/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/mobile/package.json b/mobile/package.json index 1e67c6a..c638d6d 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,14 +1,17 @@ { - "name": "mobile", + "name": "task-team-mobile", "version": "1.0.0", "main": "index.ts", "scripts": { "start": "expo start", "android": "expo start --android", "ios": "expo start --ios", - "web": "expo start --web" + "web": "expo start --web", + "export:web": "expo export --platform web", + "lint": "expo lint" }, "dependencies": { + "@expo/vector-icons": "^14.0.4", "@react-navigation/native": "^7.2.2", "expo": "~54.0.33", "expo-constants": "~18.0.13", @@ -18,12 +21,17 @@ "expo-router": "~6.0.23", "expo-secure-store": "~15.0.8", "expo-status-bar": "~3.0.9", + "nativewind": "^4.1.23", "react": "19.1.0", + "react-dom": "19.1.0", "react-native": "0.81.5", + "react-native-css-interop": "^0.2.3", "react-native-gesture-handler": "^2.30.1", "react-native-reanimated": "^4.3.0", "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.16.0" + "react-native-screens": "~4.16.0", + "react-native-web": "^0.21.0", + "tailwindcss": "^3.4.19" }, "devDependencies": { "@types/react": "~19.1.0", diff --git a/mobile/tsconfig.json b/mobile/tsconfig.json index b9567f6..afc4d25 100644 --- a/mobile/tsconfig.json +++ b/mobile/tsconfig.json @@ -1,6 +1,11 @@ { "extends": "expo/tsconfig.base", "compilerOptions": { - "strict": true - } + "strict": true, + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + } + }, + "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] }