feat: update app configuration and enhance UI components
This commit is contained in:
6
app.json
6
app.json
@@ -58,6 +58,12 @@
|
|||||||
"experiments": {
|
"experiments": {
|
||||||
"typedRoutes": true,
|
"typedRoutes": true,
|
||||||
"reactCompiler": true
|
"reactCompiler": true
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"router": {},
|
||||||
|
"eas": {
|
||||||
|
"projectId": "51cde1ca-e1b5-46c6-b9b4-0f17bf95693c"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { useAlert } from '@/components/AlertComponent';
|
import { useAlert } from '@/components/AlertComponent';
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { View, Text, TouchableOpacity, ScrollView, RefreshControl } from 'react-native';
|
|
||||||
import { QrCode, CheckCircle2, Nfc } from 'lucide-react-native';
|
|
||||||
import QrScanModal from '@/components/QrScanModal';
|
|
||||||
import NfcScanModal from '@/components/NfcScanModal';
|
|
||||||
import LoadingScreen from '@/components/LoadingScreen';
|
import LoadingScreen from '@/components/LoadingScreen';
|
||||||
|
import NfcScanModal from '@/components/NfcScanModal';
|
||||||
|
import QrScanModal from '@/components/QrScanModal';
|
||||||
|
import { AttendanceRecord } from '@/types/types';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import { formatDate, formatTime, parseSecondsToTime } from '@/utils/dateTime';
|
import { formatDate, formatTime, parseSecondsToTime } from '@/utils/dateTime';
|
||||||
import { AttendanceRecord } from '@/types/types';
|
import { CheckCircle2, Nfc, QrCode } from 'lucide-react-native';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import NfcManager from 'react-native-nfc-manager';
|
import NfcManager from 'react-native-nfc-manager';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ export default function AttendanceScreen() {
|
|||||||
<View className="flex-1 bg-gray-50">
|
<View className="flex-1 bg-gray-50">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100">
|
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100">
|
||||||
<SafeAreaView edges={['top']} className='pt-4'>
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
<Text className="text-3xl font-bold text-gray-800 mb-1">Gestione Presenze</Text>
|
<Text className="text-3xl font-bold text-gray-800 mb-1">Gestione Presenze</Text>
|
||||||
<Text className="text-base text-gray-500">Registra i tuoi movimenti</Text>
|
<Text className="text-base text-gray-500">Registra i tuoi movimenti</Text>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useLocalSearchParams, useRouter } from 'expo-router';
|
|||||||
import { ChevronLeft, ServerOff, WifiOff } from 'lucide-react-native';
|
import { ChevronLeft, ServerOff, WifiOff } from 'lucide-react-native';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
export default function AutomationDetailScreen() {
|
export default function AutomationDetailScreen() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -95,7 +96,9 @@ export default function AutomationDetailScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-gray-50">
|
<View className="flex-1 bg-gray-50">
|
||||||
<View className="bg-white p-6 pt-16 shadow-sm flex-row justify-between items-center border-b border-gray-100">
|
<View className="bg-white px-4 pb-6 shadow-sm border-b border-gray-100">
|
||||||
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
|
<View className="flex-row justify-between items-center">
|
||||||
<View className='flex-row items-center gap-4'>
|
<View className='flex-row items-center gap-4'>
|
||||||
<TouchableOpacity onPress={() => router.back()} className='rounded-full active:bg-gray-100'>
|
<TouchableOpacity onPress={() => router.back()} className='rounded-full active:bg-gray-100'>
|
||||||
<ChevronLeft size={28} color="#4b5563" />
|
<ChevronLeft size={28} color="#4b5563" />
|
||||||
@@ -104,6 +107,8 @@ export default function AutomationDetailScreen() {
|
|||||||
</View>
|
</View>
|
||||||
<View className={`ms-auto w-3.5 h-3.5 rounded-full border-2 border-white shadow-sm bg-green-500`} />
|
<View className={`ms-auto w-3.5 h-3.5 rounded-full border-2 border-white shadow-sm bg-green-500`} />
|
||||||
</View>
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
</View>
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerClassName={`flex-grow p-5 ${devices.length > 0 ? 'justify-start' : 'justify-center'}`}
|
contentContainerClassName={`flex-grow p-5 ${devices.length > 0 ? 'justify-start' : 'justify-center'}`}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import LoadingScreen from '@/components/LoadingScreen';
|
||||||
import type { HaArea } from '@/types/types';
|
import type { HaArea } from '@/types/types';
|
||||||
import { getHaAreas, testHaConnection } from '@/utils/haApi';
|
import { getHaAreas, testHaConnection } from '@/utils/haApi';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { Archive, Briefcase, DoorOpen, Grid2X2, Lightbulb, Plus, Users, WifiOff } from 'lucide-react-native';
|
import { Archive, Briefcase, DoorOpen, Grid2X2, Lightbulb, Users, WifiOff } from 'lucide-react-native';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import LoadingScreen from '@/components/LoadingScreen';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
const AREA_STYLES = [
|
const AREA_STYLES = [
|
||||||
{ icon: Users, bgColor: 'bg-indigo-100' },
|
{ icon: Users, bgColor: 'bg-indigo-100' },
|
||||||
@@ -70,11 +71,13 @@ export default function AutomationScreen() {
|
|||||||
return <LoadingScreen />;
|
return <LoadingScreen />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove duplicated code
|
||||||
if (!connectionStatus.success) {
|
if (!connectionStatus.success) {
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-gray-50">
|
<View className="flex-1 bg-gray-50">
|
||||||
<View className="bg-white pt-16 shadow-sm border-b border-gray-100">
|
<View className="bg-white shadow-sm border-b border-gray-100">
|
||||||
<View className="px-6 pb-4 flex-row justify-between items-center">
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
|
<View className="flex-row justify-between items-center px-6 pb-6">
|
||||||
<View>
|
<View>
|
||||||
<Text className="text-3xl font-bold text-gray-800 mb-1">Domotica</Text>
|
<Text className="text-3xl font-bold text-gray-800 mb-1">Domotica</Text>
|
||||||
<Text className="text-base text-gray-500">Controlla gli ambienti</Text>
|
<Text className="text-base text-gray-500">Controlla gli ambienti</Text>
|
||||||
@@ -85,20 +88,9 @@ export default function AutomationScreen() {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{connectionStatus.success && (
|
</SafeAreaView>
|
||||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerClassName="px-5 pb-4 gap-3">
|
|
||||||
{floors.map(floor => (
|
|
||||||
<TouchableOpacity
|
|
||||||
key={floor}
|
|
||||||
onPress={() => setSelectedFloor(floor)}
|
|
||||||
className={`px-5 py-2.5 rounded-xl ${selectedFloor === floor ? 'bg-[#099499]' : 'bg-gray-100 active:bg-gray-200'}`}
|
|
||||||
>
|
|
||||||
<Text className={`font-bold ${selectedFloor === floor ? 'text-white' : 'text-gray-600'}`}>{floor}</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
</ScrollView>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerClassName="flex-grow items-center justify-center pb-[100px]"
|
contentContainerClassName="flex-grow items-center justify-center pb-[100px]"
|
||||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={['#099499']} tintColor={'#099499'} />}
|
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={['#099499']} tintColor={'#099499'} />}
|
||||||
@@ -118,8 +110,9 @@ export default function AutomationScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-gray-50">
|
<View className="flex-1 bg-gray-50">
|
||||||
<View className="bg-white pt-16 shadow-sm border-b border-gray-100">
|
<View className="bg-white shadow-sm border-b border-gray-100">
|
||||||
<View className="px-6 pb-4 flex-row justify-between items-center">
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
|
<View className="flex-row justify-between items-center px-6 pb-6">
|
||||||
<View>
|
<View>
|
||||||
<Text className="text-3xl font-bold text-gray-800 mb-1">Domotica</Text>
|
<Text className="text-3xl font-bold text-gray-800 mb-1">Domotica</Text>
|
||||||
<Text className="text-base text-gray-500">Controlla gli ambienti</Text>
|
<Text className="text-base text-gray-500">Controlla gli ambienti</Text>
|
||||||
@@ -143,6 +136,7 @@ export default function AutomationScreen() {
|
|||||||
))}
|
))}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)}
|
)}
|
||||||
|
</SafeAreaView>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useRouter } from 'expo-router';
|
import LoadingScreen from '@/components/LoadingScreen';
|
||||||
import { AlertTriangle, CalendarDays, CheckCircle2, FileText, QrCode, User, CalendarClock, LayoutDashboard } from 'lucide-react-native';
|
|
||||||
import React, { useState, useContext, useEffect } from 'react';
|
|
||||||
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
|
||||||
import { AuthContext } from '@/utils/authContext';
|
|
||||||
import { ActivityItem } from '@/types/types';
|
import { ActivityItem } from '@/types/types';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import LoadingScreen from '@/components/LoadingScreen';
|
import { AuthContext } from '@/utils/authContext';
|
||||||
|
import { useRouter } from 'expo-router';
|
||||||
|
import { AlertTriangle, CalendarClock, CalendarDays, CheckCircle2, FileText, LayoutDashboard, QrCode, User } from 'lucide-react-native';
|
||||||
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
|
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
@@ -64,7 +64,7 @@ export default function HomeScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-[#099499]">
|
<View className="flex-1 bg-[#099499]">
|
||||||
<SafeAreaView edges={['top']} className='pt-4'>
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
{/* Custom Banner */}
|
{/* Custom Banner */}
|
||||||
<View className="pb-6 px-6 shadow-sm z-10">
|
<View className="pb-6 px-6 shadow-sm z-10">
|
||||||
<View className="flex-row justify-between items-start">
|
<View className="flex-row justify-between items-start">
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { useAlert } from '@/components/AlertComponent';
|
import { useAlert } from '@/components/AlertComponent';
|
||||||
import React, { JSX, useEffect, useMemo, useState } from 'react';
|
|
||||||
import { Calendar as CalendarIcon, CalendarX, Clock, Plus, Thermometer } from 'lucide-react-native';
|
|
||||||
import { ScrollView, Text, TouchableOpacity, View, ActivityIndicator, RefreshControl } from 'react-native';
|
|
||||||
import { TimeOffRequest, TimeOffRequestType } from '@/types/types';
|
|
||||||
import RequestPermitModal from '@/components/RequestPermitModal';
|
|
||||||
import CalendarWidget from '@/components/CalendarWidget';
|
import CalendarWidget from '@/components/CalendarWidget';
|
||||||
import LoadingScreen from '@/components/LoadingScreen';
|
import LoadingScreen from '@/components/LoadingScreen';
|
||||||
|
import RequestPermitModal from '@/components/RequestPermitModal';
|
||||||
|
import { TimeOffRequest, TimeOffRequestType } from '@/types/types';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import { formatDate, formatTime } from '@/utils/dateTime';
|
import { formatDate, formatTime } from '@/utils/dateTime';
|
||||||
|
import { Calendar as CalendarIcon, CalendarX, Clock, Plus, Thermometer } from 'lucide-react-native';
|
||||||
|
import React, { JSX, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
// Icon Mapping
|
// Icon Mapping
|
||||||
@@ -93,7 +93,7 @@ export default function PermitsScreen() {
|
|||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100 flex-row justify-between items-center">
|
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100 flex-row justify-between items-center">
|
||||||
<SafeAreaView edges={['top']} className='pt-4'>
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
<View>
|
<View>
|
||||||
<Text className="text-3xl font-bold text-gray-800 mb-1">Ferie e Permessi</Text>
|
<Text className="text-3xl font-bold text-gray-800 mb-1">Ferie e Permessi</Text>
|
||||||
<Text className="text-base text-gray-500">Gestisci le tue assenze</Text>
|
<Text className="text-base text-gray-500">Gestisci le tue assenze</Text>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
|
import AddDocumentModal from '@/components/AddDocumentModal';
|
||||||
import { useAlert } from '@/components/AlertComponent';
|
import { useAlert } from '@/components/AlertComponent';
|
||||||
import { Download, FileText, MapPin, Plus, Search, CalendarIcon, ChevronLeft } from 'lucide-react-native';
|
import LoadingScreen from '@/components/LoadingScreen';
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { useRouter } from 'expo-router';
|
|
||||||
import { RangePickerModal } from '@/components/RangePickerModal';
|
import { RangePickerModal } from '@/components/RangePickerModal';
|
||||||
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
|
||||||
import { DocumentItem } from '@/types/types';
|
import { DocumentItem } from '@/types/types';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import LoadingScreen from '@/components/LoadingScreen';
|
|
||||||
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
|
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
|
||||||
import AddDocumentModal from '@/components/AddDocumentModal';
|
|
||||||
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
|
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useRouter } from 'expo-router';
|
||||||
|
import { CalendarIcon, ChevronLeft, Download, FileText, MapPin, Plus, Search } from 'lucide-react-native';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
export default function DocumentsScreen() {
|
export default function DocumentsScreen() {
|
||||||
@@ -107,7 +107,7 @@ export default function DocumentsScreen() {
|
|||||||
<View className="flex-1 bg-gray-50">
|
<View className="flex-1 bg-gray-50">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<View className="bg-white px-4 pb-6 shadow-sm border-b border-gray-100">
|
<View className="bg-white px-4 pb-6 shadow-sm border-b border-gray-100">
|
||||||
<SafeAreaView edges={['top']} className='pt-4'>
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
<View className='flex-row items-center gap-4'>
|
<View className='flex-row items-center gap-4'>
|
||||||
<TouchableOpacity onPress={() => router.back()} className="p-2 rounded-full active:bg-gray-100">
|
<TouchableOpacity onPress={() => router.back()} className="p-2 rounded-full active:bg-gray-100">
|
||||||
<ChevronLeft size={28} color="#4b5563" />
|
<ChevronLeft size={28} color="#4b5563" />
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import { AuthContext } from '@/utils/authContext';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { ChevronLeft, FileText, LogOut, Mail, Settings, User } from 'lucide-react-native';
|
import { ChevronLeft, FileText, LogOut, Mail, User } from 'lucide-react-native';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { AuthContext } from '@/utils/authContext';
|
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
export default function ProfileScreen() {
|
export default function ProfileScreen() {
|
||||||
@@ -15,7 +15,7 @@ export default function ProfileScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-[#099499]">
|
<View className="flex-1 bg-[#099499]">
|
||||||
<SafeAreaView edges={['top']} className='pt-4'>
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<View className="pb-6 px-4">
|
<View className="pb-6 px-4">
|
||||||
<View className="flex-row justify-start items-center gap-4">
|
<View className="flex-row justify-start items-center gap-4">
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
|
import AddDocumentModal from '@/components/AddDocumentModal';
|
||||||
import { useAlert } from '@/components/AlertComponent';
|
import { useAlert } from '@/components/AlertComponent';
|
||||||
import { Download, FileText, Plus, Search, Calendar as CalendarIcon, ChevronLeft } from 'lucide-react-native';
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { RangePickerModal } from '@/components/RangePickerModal';
|
|
||||||
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
|
||||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
|
||||||
import { ConstructionSite, DocumentItem } from '@/types/types';
|
|
||||||
import LoadingScreen from '@/components/LoadingScreen';
|
import LoadingScreen from '@/components/LoadingScreen';
|
||||||
|
import { RangePickerModal } from '@/components/RangePickerModal';
|
||||||
|
import { ConstructionSite, DocumentItem } from '@/types/types';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
|
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
|
||||||
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
|
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
|
||||||
import AddDocumentModal from '@/components/AddDocumentModal';
|
import dayjs from 'dayjs';
|
||||||
|
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||||
|
import { Calendar as CalendarIcon, ChevronLeft, Download, FileText, Plus, Search } from 'lucide-react-native';
|
||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
export default function SiteDocumentsScreen() {
|
export default function SiteDocumentsScreen() {
|
||||||
@@ -132,7 +132,7 @@ export default function SiteDocumentsScreen() {
|
|||||||
<View className="flex-1 bg-gray-50">
|
<View className="flex-1 bg-gray-50">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<View className="bg-white px-4 pb-6 shadow-sm border-b border-gray-100">
|
<View className="bg-white px-4 pb-6 shadow-sm border-b border-gray-100">
|
||||||
<SafeAreaView edges={['top']} className='pt-4'>
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
<View className='flex-row items-center gap-4'>
|
<View className='flex-row items-center gap-4'>
|
||||||
<TouchableOpacity onPress={() => router.back()} className="p-2 -ml-2 rounded-full active:bg-gray-100">
|
<TouchableOpacity onPress={() => router.back()} className="p-2 -ml-2 rounded-full active:bg-gray-100">
|
||||||
<ChevronLeft size={28} color="#4b5563" />
|
<ChevronLeft size={28} color="#4b5563" />
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
import { useAlert } from '@/components/AlertComponent';
|
||||||
|
import LoadingScreen from '@/components/LoadingScreen';
|
||||||
|
import { ConstructionSite } from '@/types/types';
|
||||||
|
import api from '@/utils/api';
|
||||||
|
import { useRouter } from 'expo-router';
|
||||||
import { Building2, ChevronRight, MapPin, Search } from 'lucide-react-native';
|
import { Building2, ChevronRight, MapPin, Search } from 'lucide-react-native';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
||||||
import { useRouter } from 'expo-router';
|
|
||||||
import api from '@/utils/api';
|
|
||||||
import LoadingScreen from '@/components/LoadingScreen';
|
|
||||||
import { ConstructionSite } from '@/types/types';
|
|
||||||
import { useAlert } from '@/components/AlertComponent';
|
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
export default function SitesScreen() {
|
export default function SitesScreen() {
|
||||||
@@ -56,7 +56,7 @@ export default function SitesScreen() {
|
|||||||
<View className="flex-1 bg-gray-50">
|
<View className="flex-1 bg-gray-50">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100">
|
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100">
|
||||||
<SafeAreaView edges={['top']} className='pt-4'>
|
<SafeAreaView edges={['top']} className='pt-5'>
|
||||||
<Text className="text-3xl font-bold text-gray-800 mb-1">Cantieri</Text>
|
<Text className="text-3xl font-bold text-gray-800 mb-1">Cantieri</Text>
|
||||||
<Text className="text-base text-gray-500">Seleziona un cantiere per vedere i documenti</Text>
|
<Text className="text-base text-gray-500">Seleziona un cantiere per vedere i documenti</Text>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|||||||
@@ -4,20 +4,23 @@ import { Stack } from 'expo-router';
|
|||||||
import { AlertProvider } from '@/components/AlertComponent';
|
import { AlertProvider } from '@/components/AlertComponent';
|
||||||
import { NetworkProvider } from '@/utils/networkProvider';
|
import { NetworkProvider } from '@/utils/networkProvider';
|
||||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||||
|
import { KeyboardProvider } from "react-native-keyboard-controller";
|
||||||
|
|
||||||
export default function AppLayout() {
|
export default function AppLayout() {
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<SafeAreaProvider>
|
||||||
|
<KeyboardProvider>
|
||||||
<NetworkProvider>
|
<NetworkProvider>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<AlertProvider>
|
<AlertProvider>
|
||||||
<Stack screenOptions={{ headerShown: false }}>
|
<Stack screenOptions={{ headerShown: false, animation: 'flip' }}>
|
||||||
<Stack.Screen name="(protected)" />
|
<Stack.Screen name="(protected)" />
|
||||||
<Stack.Screen name="login" />
|
<Stack.Screen name="login" />
|
||||||
</Stack>
|
</Stack>
|
||||||
</AlertProvider>
|
</AlertProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</NetworkProvider>
|
</NetworkProvider>
|
||||||
|
</KeyboardProvider>
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,8 @@ import api from '@/utils/api';
|
|||||||
import { AuthContext } from '@/utils/authContext';
|
import { AuthContext } from '@/utils/authContext';
|
||||||
import { Eye, EyeOff, Lock, LogIn, Mail } from 'lucide-react-native';
|
import { Eye, EyeOff, Lock, LogIn, Mail } from 'lucide-react-native';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import { Image, KeyboardAvoidingView, Platform, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
import { Image, Platform, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
||||||
|
import { KeyboardAvoidingView } from 'react-native-keyboard-controller';
|
||||||
|
|
||||||
export default function LoginScreen() {
|
export default function LoginScreen() {
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
@@ -74,8 +75,9 @@ export default function LoginScreen() {
|
|||||||
{/* Form Container */}
|
{/* Form Container */}
|
||||||
<View className="flex-1 bg-white rounded-t-[2.5rem] px-8 pt-10 shadow-xl w-full">
|
<View className="flex-1 bg-white rounded-t-[2.5rem] px-8 pt-10 shadow-xl w-full">
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior={"padding"}
|
||||||
className="flex-1"
|
keyboardVerticalOffset={100}
|
||||||
|
className='flex-1 mh-600'
|
||||||
>
|
>
|
||||||
<ScrollView showsVerticalScrollIndicator={false} className="h-full">
|
<ScrollView showsVerticalScrollIndicator={false} className="h-full">
|
||||||
<View className="gap-6 flex flex-col" style={{ gap: '1.5rem' }}>
|
<View className="gap-6 flex flex-col" style={{ gap: '1.5rem' }}>
|
||||||
|
|||||||
26
components/AppDatePicker.tsx
Normal file
26
components/AppDatePicker.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import DateTimePicker, { useDefaultStyles } from 'react-native-ui-datepicker';
|
||||||
|
import { ChevronLeft, ChevronRight } from 'lucide-react-native';
|
||||||
|
|
||||||
|
type AppDatePickerProps = React.ComponentProps<typeof DateTimePicker>;
|
||||||
|
|
||||||
|
export const AppDatePicker = (props: AppDatePickerProps) => {
|
||||||
|
const defaultStyles = useDefaultStyles('light');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DateTimePicker
|
||||||
|
{...props}
|
||||||
|
locale="it"
|
||||||
|
components={{
|
||||||
|
IconPrev: <ChevronLeft size={24} color="#1f2937" />,
|
||||||
|
IconNext: <ChevronRight size={24} color="#1f2937" />,
|
||||||
|
...props.components,
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
...defaultStyles,
|
||||||
|
selected: { backgroundColor: '#099499' },
|
||||||
|
...props.styles,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { Modal, Text, TouchableOpacity, View, Animated, Easing, Vibration, Alert } from 'react-native';
|
import { Modal, Text, TouchableOpacity, View, Animated, Easing, Vibration, Alert } from 'react-native';
|
||||||
import { X, Radio, SmartphoneNfc } from 'lucide-react-native';
|
import { X, Radio, SmartphoneNfc } from 'lucide-react-native';
|
||||||
import NfcManager, { NfcTech } from 'react-native-nfc-manager';
|
import NfcManager, { Ndef, NfcTech } from 'react-native-nfc-manager';
|
||||||
|
|
||||||
interface NfcScanModalProps {
|
interface NfcScanModalProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@@ -31,9 +31,16 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
|
|||||||
const tag = await NfcManager.getTag();
|
const tag = await NfcManager.getTag();
|
||||||
|
|
||||||
if (tag) {
|
if (tag) {
|
||||||
console.log('NFC Tag Found:', tag);
|
const record = tag.ndefMessage?.[0];
|
||||||
|
const payloadBytes = new Uint8Array(record.payload);
|
||||||
|
const text = Ndef.text.decodePayload(payloadBytes);
|
||||||
|
console.log('NFC text:', text || 'No NDEF payload');
|
||||||
Vibration.vibrate();
|
Vibration.vibrate();
|
||||||
// onScan(tag.id || JSON.stringify(tag));
|
if (!text) {
|
||||||
|
Alert.alert('Errore', 'Tag NFC non contiene dati validi.');
|
||||||
|
throw new Error('Empty NFC tag');
|
||||||
|
}
|
||||||
|
onScan(text);
|
||||||
onClose();
|
onClose();
|
||||||
} else {
|
} else {
|
||||||
console.warn('Tag NFC vuoto o non formattato NDEF');
|
console.warn('Tag NFC vuoto o non formattato NDEF');
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Alert, Modal, Text, TouchableOpacity, View } from 'react-native';
|
import { Modal, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import DateTimePicker, { DateType, useDefaultStyles } from 'react-native-ui-datepicker';
|
import { DateType } from 'react-native-ui-datepicker';
|
||||||
import { Check, X } from 'lucide-react-native';
|
import { Check, X } from 'lucide-react-native';
|
||||||
|
import { AppDatePicker } from './AppDatePicker';
|
||||||
|
|
||||||
export const RangePickerModal = ({ visible, onClose, onApply }: any) => {
|
export const RangePickerModal = ({ visible, onClose, onApply }: any) => {
|
||||||
const defaultStyles = useDefaultStyles('light');
|
|
||||||
const [range, setRange] = useState<{
|
const [range, setRange] = useState<{
|
||||||
startDate: DateType;
|
startDate: DateType;
|
||||||
endDate: DateType;
|
endDate: DateType;
|
||||||
@@ -32,17 +32,12 @@ export const RangePickerModal = ({ visible, onClose, onApply }: any) => {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<DateTimePicker
|
<AppDatePicker
|
||||||
mode="range"
|
mode="range"
|
||||||
startDate={range.startDate}
|
startDate={range.startDate}
|
||||||
endDate={range.endDate}
|
endDate={range.endDate}
|
||||||
onChange={(params) => setRange(params)}
|
onChange={(params) => setRange(params)}
|
||||||
timeZone='Europe/Rome'
|
timeZone='Europe/Rome'
|
||||||
locale='it'
|
|
||||||
styles={{
|
|
||||||
...defaultStyles,
|
|
||||||
selected: { backgroundColor: '#099499' }
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { useAlert } from '@/components/AlertComponent';
|
import { useAlert } from '@/components/AlertComponent';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { View, Text, Modal, TouchableOpacity, TextInput, ScrollView } from 'react-native';
|
import { View, Text, Modal, TouchableOpacity, TextInput, ScrollView, Platform } from 'react-native';
|
||||||
import DateTimePicker, { DateType, useDefaultStyles } from 'react-native-ui-datepicker';
|
|
||||||
import { TimeOffRequestType } from '@/types/types';
|
import { TimeOffRequestType } from '@/types/types';
|
||||||
import { X } from 'lucide-react-native';
|
import { X } from 'lucide-react-native';
|
||||||
import { TimePickerModal } from './TimePickerModal';
|
import { TimePickerModal } from './TimePickerModal';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import { formatPickerDate } from '@/utils/dateTime';
|
import { formatPickerDate } from '@/utils/dateTime';
|
||||||
|
import { AppDatePicker } from '@/components/AppDatePicker';
|
||||||
|
import { KeyboardAvoidingView } from 'react-native-keyboard-controller';
|
||||||
|
|
||||||
interface RequestPermitModalProps {
|
interface RequestPermitModalProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@@ -16,7 +17,6 @@ interface RequestPermitModalProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function RequestPermitModal({ visible, types, onClose, onSubmit }: RequestPermitModalProps) {
|
export default function RequestPermitModal({ visible, types, onClose, onSubmit }: RequestPermitModalProps) {
|
||||||
const defaultStyles = useDefaultStyles('light');
|
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
const [type, setType] = useState<TimeOffRequestType>(types[0]); // Default to first type
|
const [type, setType] = useState<TimeOffRequestType>(types[0]); // Default to first type
|
||||||
const [date, setDate] = useState<string | null>();
|
const [date, setDate] = useState<string | null>();
|
||||||
@@ -116,6 +116,11 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<KeyboardAvoidingView
|
||||||
|
behavior={"padding"}
|
||||||
|
keyboardVerticalOffset={100}
|
||||||
|
className='flex-1 mh-600'
|
||||||
|
>
|
||||||
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ paddingBottom: 40 }}>
|
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ paddingBottom: 40 }}>
|
||||||
<View className="space-y-6">
|
<View className="space-y-6">
|
||||||
{/* Permit Type */}
|
{/* Permit Type */}
|
||||||
@@ -143,7 +148,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
|||||||
|
|
||||||
{/* Date and Time Selection */}
|
{/* Date and Time Selection */}
|
||||||
{type?.time_required === 0 ? (
|
{type?.time_required === 0 ? (
|
||||||
<DateTimePicker
|
<AppDatePicker
|
||||||
mode="range"
|
mode="range"
|
||||||
startDate={range.startDate}
|
startDate={range.startDate}
|
||||||
endDate={range.endDate}
|
endDate={range.endDate}
|
||||||
@@ -153,22 +158,12 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
|||||||
endDate: params.endDate ? formatPickerDate(params.endDate) : null
|
endDate: params.endDate ? formatPickerDate(params.endDate) : null
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
locale='it'
|
|
||||||
styles={{
|
|
||||||
...defaultStyles,
|
|
||||||
selected: { backgroundColor: '#099499' },
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<DateTimePicker
|
<AppDatePicker
|
||||||
mode="single"
|
mode="single"
|
||||||
date={date}
|
date={date}
|
||||||
onChange={({ date }) => setDate(date ? formatPickerDate(date) : null)}
|
onChange={({ date }) => setDate(date ? formatPickerDate(date) : null)}
|
||||||
locale='it'
|
|
||||||
styles={{
|
|
||||||
...defaultStyles,
|
|
||||||
selected: { backgroundColor: '#099499' }
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -181,6 +176,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
|||||||
<TouchableOpacity onPress={() => setShowStartPicker(true)}>
|
<TouchableOpacity onPress={() => setShowStartPicker(true)}>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="09:00"
|
placeholder="09:00"
|
||||||
|
placeholderTextColor="#9CA3AF"
|
||||||
className="w-full p-3 bg-white rounded-lg border border-orange-200 font-bold text-gray-800 text-center"
|
className="w-full p-3 bg-white rounded-lg border border-orange-200 font-bold text-gray-800 text-center"
|
||||||
value={startTime}
|
value={startTime}
|
||||||
onChangeText={setStartTime}
|
onChangeText={setStartTime}
|
||||||
@@ -193,6 +189,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
|||||||
<TouchableOpacity onPress={() => setShowEndPicker(true)}>
|
<TouchableOpacity onPress={() => setShowEndPicker(true)}>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="18:00"
|
placeholder="18:00"
|
||||||
|
placeholderTextColor="#9CA3AF"
|
||||||
className="w-full p-3 bg-white rounded-lg border border-orange-200 font-bold text-gray-800 text-center"
|
className="w-full p-3 bg-white rounded-lg border border-orange-200 font-bold text-gray-800 text-center"
|
||||||
value={endTime}
|
value={endTime}
|
||||||
onChangeText={setEndTime}
|
onChangeText={setEndTime}
|
||||||
@@ -224,6 +221,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
|||||||
<Text className="text-sm font-bold text-orange-800 mb-2 uppercase">Motivo</Text>
|
<Text className="text-sm font-bold text-orange-800 mb-2 uppercase">Motivo</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Scrivi qui il motivo..."
|
placeholder="Scrivi qui il motivo..."
|
||||||
|
placeholderTextColor="#9CA3AF"
|
||||||
className="w-full px-3 py-4 bg-white font-bold text-gray-800 rounded-lg border border-orange-200 text-gray-800"
|
className="w-full px-3 py-4 bg-white font-bold text-gray-800 rounded-lg border border-orange-200 text-gray-800"
|
||||||
textAlignVertical="top"
|
textAlignVertical="top"
|
||||||
value={message}
|
value={message}
|
||||||
@@ -253,6 +251,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
21
eas.json
Normal file
21
eas.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"cli": {
|
||||||
|
"version": ">= 16.32.0",
|
||||||
|
"appVersionSource": "remote"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"development": {
|
||||||
|
"developmentClient": true,
|
||||||
|
"distribution": "internal"
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"distribution": "internal"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"autoIncrement": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submit": {
|
||||||
|
"production": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -40,6 +40,7 @@
|
|||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-native": "0.81.5",
|
"react-native": "0.81.5",
|
||||||
"react-native-gesture-handler": "~2.28.0",
|
"react-native-gesture-handler": "~2.28.0",
|
||||||
|
"react-native-keyboard-controller": "1.18.5",
|
||||||
"react-native-nfc-manager": "^3.17.2",
|
"react-native-nfc-manager": "^3.17.2",
|
||||||
"react-native-reanimated": "~4.1.1",
|
"react-native-reanimated": "~4.1.1",
|
||||||
"react-native-safe-area-context": "~5.6.0",
|
"react-native-safe-area-context": "~5.6.0",
|
||||||
@@ -11530,6 +11531,20 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-keyboard-controller": {
|
||||||
|
"version": "1.18.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.18.5.tgz",
|
||||||
|
"integrity": "sha512-wbYN6Tcu3G5a05dhRYBgjgd74KqoYWuUmroLpigRg9cXy5uYo7prTMIvMgvLtARQtUF7BOtFggUnzgoBOgk0TQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-native-is-edge-to-edge": "^1.2.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*",
|
||||||
|
"react-native-reanimated": ">=3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native-nfc-manager": {
|
"node_modules/react-native-nfc-manager": {
|
||||||
"version": "3.17.2",
|
"version": "3.17.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-nfc-manager/-/react-native-nfc-manager-3.17.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-nfc-manager/-/react-native-nfc-manager-3.17.2.tgz",
|
||||||
@@ -11674,7 +11689,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz",
|
||||||
"integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==",
|
"integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
|
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
|
||||||
"@babel/plugin-transform-class-properties": "^7.0.0-0",
|
"@babel/plugin-transform-class-properties": "^7.0.0-0",
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-native": "0.81.5",
|
"react-native": "0.81.5",
|
||||||
"react-native-gesture-handler": "~2.28.0",
|
"react-native-gesture-handler": "~2.28.0",
|
||||||
|
"react-native-keyboard-controller": "1.18.5",
|
||||||
"react-native-nfc-manager": "^3.17.2",
|
"react-native-nfc-manager": "^3.17.2",
|
||||||
"react-native-reanimated": "~4.1.1",
|
"react-native-reanimated": "~4.1.1",
|
||||||
"react-native-safe-area-context": "~5.6.0",
|
"react-native-safe-area-context": "~5.6.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user