feat: Update assets and improve code comments across multiple components
- Updated favicon and various image assets. - Enhanced comments. - Adjusted styles and functionality in several components for improved user experience. - Updated package-lock.json to reflect dependency updates.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Text, TouchableOpacity, View, TextInput, ActivityIndicator } from 'react-native';
|
||||
import { X, Upload, FileText, Trash2 } from 'lucide-react-native';
|
||||
import * as DocumentPicker from 'expo-document-picker'; // TODO: Testare in ambiente iOS
|
||||
import * as DocumentPicker from 'expo-document-picker'; // TODO: Test on iOS environment
|
||||
|
||||
interface AddDocumentModalProps {
|
||||
visible: boolean;
|
||||
@@ -15,7 +15,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
|
||||
const [customTitle, setCustomTitle] = useState('');
|
||||
const [fileExtension, setFileExtension] = useState('');
|
||||
|
||||
// Reset dello stato quando il modale si apre/chiude
|
||||
// Reset of state when modal is closed
|
||||
useEffect(() => {
|
||||
if (!visible) {
|
||||
setSelectedFile(null);
|
||||
@@ -24,11 +24,11 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
// TODO: Considerare selezione multipla?
|
||||
// TODO: Need to handle multiple file selection?
|
||||
const pickDocument = async () => {
|
||||
try {
|
||||
const result = await DocumentPicker.getDocumentAsync({
|
||||
type: '*/*', // Puoi limitare a 'application/pdf', 'image/*', ecc.
|
||||
type: '*/*', // You can limit to 'application/pdf', 'image/*', etc.
|
||||
copyToCacheDirectory: true,
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
|
||||
if (!selectedFile) return;
|
||||
const fullTitle = customTitle ? `${customTitle}${fileExtension}` : selectedFile.name;
|
||||
|
||||
// Se il titolo custom è vuoto, usiamo il nome originale
|
||||
// If customTitle is empty, we use the original file name
|
||||
onUpload(selectedFile, fullTitle);
|
||||
};
|
||||
|
||||
@@ -65,8 +65,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
|
||||
setFileExtension('');
|
||||
};
|
||||
|
||||
// Formatta dimensione file
|
||||
// TODO: Spostare in utils?
|
||||
// Format file size in a human-readable format
|
||||
const formatSize = (size?: number) => {
|
||||
if (!size) return '0 B';
|
||||
const k = 1024;
|
||||
@@ -93,10 +92,8 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Body */}
|
||||
<View className="gap-5">
|
||||
|
||||
{/* Area Selezione File */}
|
||||
{/* File Selection Area */}
|
||||
{!selectedFile ? (
|
||||
<TouchableOpacity
|
||||
onPress={pickDocument}
|
||||
@@ -109,7 +106,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
|
||||
<Text className="text-gray-400 text-sm mt-1">PDF, Immagini, Word</Text>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
// Visualizzazione File Selezionato
|
||||
// Selected File View
|
||||
<View className="bg-[#E6F4F4] p-4 rounded-2xl border border-[#099499]/20 flex-row items-center gap-4">
|
||||
<View className="bg-white p-3 rounded-xl">
|
||||
<FileText size={24} color="#099499" />
|
||||
@@ -128,7 +125,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Campo Rinomina (Visibile solo se c'è un file) */}
|
||||
{/* Rename field (Visible only if a file is selected) */}
|
||||
{selectedFile && (
|
||||
<View>
|
||||
<Text className="text-gray-700 font-bold mb-2 ml-1 text-sm">Rinomina File</Text>
|
||||
|
||||
@@ -11,6 +11,7 @@ interface AlertContextData {
|
||||
|
||||
const AlertContext = createContext<AlertContextData>({} as AlertContextData);
|
||||
|
||||
// TODO: Move this config to a separate file
|
||||
const ALERT_CONFIG = {
|
||||
success: {
|
||||
icon: CheckCircle,
|
||||
@@ -57,33 +58,33 @@ export const AlertProvider = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
const { icon: Icon, color, bgColor, btnColor } = ALERT_CONFIG[type];
|
||||
|
||||
// TODO: Need to refactor component styles
|
||||
return (
|
||||
<AlertContext.Provider value={{ showAlert, hideAlert }}>
|
||||
{children}
|
||||
|
||||
{/* IL COMPONENTE ALERT MODALE */}
|
||||
<Modal
|
||||
transparent
|
||||
visible={visible}
|
||||
animationType="fade"
|
||||
onRequestClose={hideAlert}
|
||||
>
|
||||
{/* Backdrop scuro */}
|
||||
{/* Dark Backdrop */}
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
onPress={hideAlert} // Chiude se clicchi fuori (opzionale)
|
||||
onPress={hideAlert} // Closes if you click outside (optional)
|
||||
className="flex-1 bg-black/60 justify-center items-center px-6"
|
||||
>
|
||||
{/* Contenitore Alert */}
|
||||
{/* Alert Container */}
|
||||
<TouchableWithoutFeedback>
|
||||
<View className="bg-white w-full max-w-sm rounded-3xl p-6 items-center shadow-2xl">
|
||||
|
||||
{/* Icona Cerchiata */}
|
||||
{/* Icon Circle */}
|
||||
<View className={`${bgColor} p-4 rounded-full mb-4`}>
|
||||
<Icon size={32} className={color} strokeWidth={2.5} />
|
||||
</View>
|
||||
|
||||
{/* Testi */}
|
||||
{/* Texts */}
|
||||
<Text className="text-xl font-bold text-gray-900 text-center mb-2">
|
||||
{title}
|
||||
</Text>
|
||||
@@ -92,10 +93,10 @@ export const AlertProvider = ({ children }: { children: ReactNode }) => {
|
||||
{message}
|
||||
</Text>
|
||||
|
||||
{/* Pulsante OK */}
|
||||
{/* OK Button */}
|
||||
<TouchableOpacity
|
||||
onPress={hideAlert}
|
||||
className={`w-full py-3.5 rounded-xl ${btnColor} active:opacity-90 shadow-sm`}
|
||||
className={`w-full py-3.5 rounded-3xl ${btnColor} active:opacity-90 shadow-sm`}
|
||||
>
|
||||
<Text className="text-white text-center font-bold text-lg">
|
||||
Ok, ho capito
|
||||
|
||||
@@ -12,7 +12,7 @@ interface CalendarWidgetProps {
|
||||
export default function CalendarWidget({ events, types, onMonthChange }: CalendarWidgetProps) {
|
||||
const [currentDate, setCurrentDate] = useState(new Date());
|
||||
|
||||
// Helpers per il calendario
|
||||
// Calendar helpers
|
||||
const daysInMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getDate();
|
||||
const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1).getDay(); // 0 = Sun
|
||||
const adjustedFirstDay = firstDayOfMonth === 0 ? 6 : firstDayOfMonth - 1; // 0 = Mon
|
||||
@@ -40,8 +40,8 @@ export default function CalendarWidget({ events, types, onMonthChange }: Calenda
|
||||
const dateStr = `${year}-${month}-${dayStr}`;
|
||||
|
||||
return events.find(event => {
|
||||
// Logica semplice: controlla se la data cade nel range
|
||||
// Nota: per una logica perfetta sui range lunghi, servirebbe un controllo più approfondito
|
||||
// Simple logic: check if the date falls within the range
|
||||
// Note: for perfect logic on long ranges, a more thorough check would be needed
|
||||
if (!event.start_date) return false;
|
||||
if (event.timeOffRequestType.name === 'Permesso') return event.start_date === dateStr;
|
||||
const end = event.end_date || event.start_date;
|
||||
@@ -51,7 +51,7 @@ export default function CalendarWidget({ events, types, onMonthChange }: Calenda
|
||||
|
||||
return (
|
||||
<View className="bg-white rounded-[2rem] p-6 shadow-sm border border-gray-100">
|
||||
{/* Header Mese */}
|
||||
{/* Month Header */}
|
||||
<View className="flex-row justify-between items-center mb-6">
|
||||
<TouchableOpacity
|
||||
onPress={() => changeMonth(-1)}
|
||||
@@ -106,7 +106,7 @@ export default function CalendarWidget({ events, types, onMonthChange }: Calenda
|
||||
})}
|
||||
</View>
|
||||
|
||||
{/* Legenda */}
|
||||
{/* Legend */}
|
||||
<View className="flex-row flex-wrap justify-center gap-4 mt-8 pt-4 border-t border-gray-100">
|
||||
{types.map((type) => (
|
||||
<View key={type.id} className="flex-row items-center" >
|
||||
|
||||
@@ -4,6 +4,7 @@ import React from 'react';
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
const DeviceCard = ({ device, onToggle }: { device: HaEntity; onToggle: (entityId: string) => void; }) => {
|
||||
// Icon mapping based on entity domain
|
||||
const getIcon = () => {
|
||||
const domain = device.entity_id.split('.')[0];
|
||||
|
||||
@@ -30,6 +31,7 @@ const DeviceCard = ({ device, onToggle }: { device: HaEntity; onToggle: (entityI
|
||||
const isToggleable = ['light', 'switch', 'fan', 'input_boolean'].includes(device.entity_id.split('.')[0]);
|
||||
const isOn = device.state === 'on';
|
||||
|
||||
// State mapping
|
||||
const getDeviceState = (state: string) => {
|
||||
switch (state) {
|
||||
case 'on':
|
||||
|
||||
@@ -9,8 +9,9 @@ interface NfcScanModalProps {
|
||||
onScan: (data: string) => void;
|
||||
}
|
||||
|
||||
// TODO: Needs to be tested on a real device
|
||||
export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalProps) {
|
||||
// Animazione per l'effetto "pulsante" (Breathing)
|
||||
// Breathing animation effect
|
||||
const scaleAnim = useRef(new Animated.Value(1)).current;
|
||||
const opacityAnim = useRef(new Animated.Value(0.3)).current;
|
||||
|
||||
@@ -44,7 +45,7 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
|
||||
}
|
||||
};
|
||||
|
||||
// Loop infinito di espansione e contrazione
|
||||
// Animation loop for the pulsing effect
|
||||
const animationLoop = () => {
|
||||
Animated.loop(
|
||||
Animated.parallel([
|
||||
@@ -97,11 +98,9 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
|
||||
statusBarTranslucent
|
||||
>
|
||||
<View className="flex-1 bg-black/80 items-center justify-center p-6">
|
||||
|
||||
{/* Card Principale */}
|
||||
<View className="bg-white w-full max-w-sm rounded-[2.5rem] p-8 items-center shadow-2xl relative overflow-hidden">
|
||||
|
||||
{/* Bottone Chiudi (Alto Destra) */}
|
||||
{/* Close Button (Top Right) */}
|
||||
<TouchableOpacity
|
||||
onPress={onClose}
|
||||
className="absolute top-6 right-6 z-10 p-2 bg-gray-50 rounded-full"
|
||||
@@ -109,9 +108,9 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
|
||||
<X size={20} color="#9ca3af" />
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* Area Animata NFC */}
|
||||
{/* NFC Animated Area */}
|
||||
<View className="mt-6 mb-10 items-center justify-center h-40 w-40">
|
||||
{/* Cerchio Pulsante (Sfondo) */}
|
||||
{/* Pulsing Circle (Background) */}
|
||||
<Animated.View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@@ -124,13 +123,13 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Cerchio Fisso (Primo piano) */}
|
||||
{/* Fixed Circle (Foreground) */}
|
||||
<View className="bg-[#E6F4F4] p-6 rounded-full border-4 border-white shadow-sm z-10">
|
||||
<SmartphoneNfc size={64} color="#099499" />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Testi */}
|
||||
{/* Texts */}
|
||||
<Text className="text-2xl font-bold text-gray-800 text-center mb-2">
|
||||
Pronto alla scansione
|
||||
</Text>
|
||||
@@ -139,7 +138,7 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
|
||||
Avvicina il retro del tuo smartphone al <Text className="font-bold text-gray-700">Tag NFC</Text> per registrare la presenza.
|
||||
</Text>
|
||||
|
||||
{/* Indicatore Visivo (Simulazione onde) */}
|
||||
{/* Indicator (Wave Simulation) */}
|
||||
<View className="flex-row gap-2 mt-8 items-center opacity-60">
|
||||
<Radio size={20} color="#099499" />
|
||||
<Text className="text-[#099499] font-medium text-sm uppercase tracking-widest">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { View, Text, TouchableOpacity } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { WifiOff, RefreshCw } from 'lucide-react-native';
|
||||
import { WifiOff } from 'lucide-react-native';
|
||||
|
||||
interface OfflineScreenProps {
|
||||
onRetry: () => void;
|
||||
@@ -12,7 +12,7 @@ export default function OfflineScreen({ onRetry, isRetrying = false }: OfflineSc
|
||||
return (
|
||||
<SafeAreaView className="flex-1 bg-white">
|
||||
<View className="flex-1 items-center justify-center px-8">
|
||||
{/* Icona con cerchio di sfondo */}
|
||||
{/* Icon */}
|
||||
<View className="bg-gray-100 p-6 rounded-full mb-6">
|
||||
<WifiOff size={64} className="text-gray-400" />
|
||||
</View>
|
||||
@@ -25,7 +25,7 @@ export default function OfflineScreen({ onRetry, isRetrying = false }: OfflineSc
|
||||
Sembra che non ci sia connessione a internet.{'\n'}Controlla il Wi-Fi o i dati mobili e riprova.
|
||||
</Text>
|
||||
|
||||
{/* Pulsante Riprova */}
|
||||
{/* Retry Button */}
|
||||
<TouchableOpacity
|
||||
onPress={onRetry}
|
||||
disabled={isRetrying}
|
||||
@@ -33,11 +33,6 @@ export default function OfflineScreen({ onRetry, isRetrying = false }: OfflineSc
|
||||
isRetrying ? 'bg-gray-300' : 'bg-[#099499] active:bg-[#077f83]'
|
||||
}`}
|
||||
>
|
||||
<RefreshCw
|
||||
size={20}
|
||||
color="white"
|
||||
className={`${isRetrying ? 'animate-spin' : ''}`} // TODO: animate-spin richiede config tailwind o gestiscilo manualmente
|
||||
/>
|
||||
<Text className="text-white font-bold text-lg">
|
||||
{isRetrying ? 'Controllo...' : 'Riprova'}
|
||||
</Text>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { View, Text, Modal, TouchableOpacity, Vibration, StyleSheet } from 'react-native';
|
||||
import { View, Text, Modal, TouchableOpacity, Vibration, StyleSheet, Dimensions } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { CameraView, useCameraPermissions } from 'expo-camera';
|
||||
import { X, ScanLine } from 'lucide-react-native';
|
||||
@@ -13,8 +13,10 @@ interface QrScanModalProps {
|
||||
export default function QrScanModal({ visible, onClose, onScan }: QrScanModalProps) {
|
||||
const [permission, requestPermission] = useCameraPermissions();
|
||||
const [scanned, setScanned] = useState(false);
|
||||
const { width, height } = Dimensions.get('window');
|
||||
const squareSize = Math.min(width * 0.8, height * 0.8, 400);
|
||||
|
||||
// Gestione Permessi e Reset Stato
|
||||
// Permission Handling and Reset Scanned State on Modal Open
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setScanned(false);
|
||||
@@ -59,26 +61,25 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Overlay Oscuro con "buco" trasparente (Simulato visivamente con bordi o opacity) */}
|
||||
<View className="flex-1">
|
||||
|
||||
{/* Dark Overlay with Transparent "Hole" (Visually Simulated with Borders or Opacity) */}
|
||||
<SafeAreaView className="flex-1">
|
||||
{/* Header Overlay */}
|
||||
<SafeAreaView className="flex-1 bg-black/60 items-center pt-8">
|
||||
<View className="flex-1 bg-black/60 items-center pt-8">
|
||||
<Text className="text-white text-xl font-bold">Scansiona QR Code</Text>
|
||||
<Text className="text-gray-300 text-base mt-1">Inquadra il codice nel riquadro</Text>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
|
||||
{/* Area Centrale (Trasparente per la camera) */}
|
||||
<View className="flex-row h-[400px]">
|
||||
{/* Central Area (Transparent for the camera) */}
|
||||
<View className="flex-row" style={{ height: squareSize }}>
|
||||
<View className="flex-1 bg-black/60" />
|
||||
<View className="w-[400px] h-[400px] border-2 border-[#099499] bg-transparent relative justify-center items-center">
|
||||
{/* Angoli decorativi */}
|
||||
<View style={{ width: squareSize, height: squareSize }} className="border-2 border-[#099499] bg-transparent relative justify-center items-center">
|
||||
{/* Decorative Corners */}
|
||||
<View className="absolute top-0 left-0 w-6 h-6 border-l-4 border-t-4 border-[#099499]" />
|
||||
<View className="absolute top-0 right-0 w-6 h-6 border-r-4 border-t-4 border-[#099499]" />
|
||||
<View className="absolute bottom-0 left-0 w-6 h-6 border-l-4 border-b-4 border-[#099499]" />
|
||||
<View className="absolute bottom-0 right-0 w-6 h-6 border-r-4 border-b-4 border-[#099499]" />
|
||||
|
||||
{/* Linea scansione animata o icona */}
|
||||
{/* Animated Scan Line or Icon */}
|
||||
{!scanned && <ScanLine color="#099499" size={40} className="opacity-50" />}
|
||||
</View>
|
||||
<View className="flex-1 bg-black/60" />
|
||||
@@ -94,7 +95,7 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
|
||||
</TouchableOpacity>
|
||||
<Text className="text-white mt-4 font-medium">Chiudi</Text>
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import DateTimePicker, { DateType, useDefaultStyles } from 'react-native-ui-date
|
||||
import { Check, X } from 'lucide-react-native';
|
||||
|
||||
export const RangePickerModal = ({ visible, onClose, onApply }: any) => {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const defaultStyles = useDefaultStyles('light');
|
||||
const [range, setRange] = useState<{
|
||||
startDate: DateType;
|
||||
endDate: DateType;
|
||||
|
||||
@@ -16,7 +16,7 @@ interface RequestPermitModalProps {
|
||||
}
|
||||
|
||||
export default function RequestPermitModal({ visible, types, onClose, onSubmit }: RequestPermitModalProps) {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const defaultStyles = useDefaultStyles('light');
|
||||
const alert = useAlert();
|
||||
const [type, setType] = useState<TimeOffRequestType>(types[0]); // Default to first type
|
||||
const [date, setDate] = useState<string | null>();
|
||||
@@ -31,7 +31,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
const [endTime, setEndTime] = useState('');
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
// Funzione per resettare le selezioni di data
|
||||
// Clean up function to reset all fields
|
||||
const clearCalendar = () => {
|
||||
setDate(null);
|
||||
setRange({ startDate: null, endDate: null });
|
||||
@@ -39,7 +39,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
setType(types[0]);
|
||||
};
|
||||
|
||||
// Funzione per validare la richiesta
|
||||
// Function to validate the request
|
||||
function validateRequest(type: TimeOffRequestType, date: string | null | undefined, range: { startDate: string | null; endDate: string | null }, startTime: string, endTime: string, message: string): string | null {
|
||||
if (!type) return "Seleziona una tipologia di assenza.";
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
return null;
|
||||
}
|
||||
|
||||
// Funzione per inviare la richiesta alla API
|
||||
// Function to send the request to the API
|
||||
const saveRequest = async (requestData: any) => {
|
||||
try {
|
||||
const response = await api.post('/time-off-request/save-request', requestData);
|
||||
@@ -73,7 +73,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
}
|
||||
};
|
||||
|
||||
// Funzione per inviare la richiesta
|
||||
// Function to submit the request
|
||||
const handleSubmit = async () => {
|
||||
const error = validateRequest(type, date, range, startTime, endTime, message);
|
||||
if (error) {
|
||||
@@ -92,7 +92,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
|
||||
try {
|
||||
await saveRequest(requestData);
|
||||
onSubmit(requestData); // TODO: Gestire risposta e controllare fetch in index?
|
||||
onSubmit(requestData);
|
||||
onClose();
|
||||
} catch (e) {
|
||||
alert.showAlert("error", "Errore", "Impossibile inviare la richiesta.");
|
||||
@@ -108,7 +108,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
>
|
||||
<View className="flex-1 bg-black/60 justify-end sm:justify-center">
|
||||
<View className="bg-white w-full rounded-t-[2.5rem] p-6 shadow-2xl h-[85%] sm:h-auto">
|
||||
{/* Header Modale */}
|
||||
{/* Modal Header */}
|
||||
<View className="flex-row justify-between items-center mb-6">
|
||||
<Text className="text-2xl font-bold text-gray-800">Nuova Richiesta</Text>
|
||||
<TouchableOpacity onPress={onClose} className="p-2 bg-gray-100 rounded-full">
|
||||
@@ -118,7 +118,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
|
||||
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ paddingBottom: 40 }}>
|
||||
<View className="space-y-6">
|
||||
{/* Tipologia */}
|
||||
{/* Permit Type */}
|
||||
<View className='mb-6'>
|
||||
<Text className="text-lg font-bold text-gray-700 mb-3">Tipologia Assenza</Text>
|
||||
<ScrollView
|
||||
@@ -156,7 +156,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
locale='it'
|
||||
styles={{
|
||||
...defaultStyles,
|
||||
selected: { backgroundColor: '#099499' }
|
||||
selected: { backgroundColor: '#099499' },
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@@ -218,7 +218,8 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{/* TODO: Trasformare message in una select? - Predefinito per alcuni tipi */}
|
||||
|
||||
{/* Reason field */}
|
||||
<View className="p-4">
|
||||
<Text className="text-sm font-bold text-orange-800 mb-2 uppercase">Motivo</Text>
|
||||
<TextInput
|
||||
@@ -231,7 +232,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Azioni */}
|
||||
{/* Actions */}
|
||||
<View className="flex-row gap-4">
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
|
||||
@@ -13,7 +13,7 @@ interface TimePickerModalProps {
|
||||
}
|
||||
|
||||
export const TimePickerModal = ({ visible, initialDate, title, onConfirm, onClose }: TimePickerModalProps) => {
|
||||
const defaultStyles = useDefaultStyles();
|
||||
const defaultStyles = useDefaultStyles('light');
|
||||
const [selectedDate, setSelectedDate] = useState<DateType>(initialDate || new Date());
|
||||
|
||||
const formatTime = (date?: DateType | null) => {
|
||||
@@ -36,7 +36,7 @@ export const TimePickerModal = ({ visible, initialDate, title, onConfirm, onClos
|
||||
<View className="flex-1 justify-center items-center bg-black/50">
|
||||
<View className="bg-white rounded-xl p-4 w-[90%] max-h-[400px]">
|
||||
|
||||
{/* Header con chiusura */}
|
||||
{/* Header */}
|
||||
<View className="flex-row justify-between items-center mb-4">
|
||||
<Text className="text-lg font-bold text-gray-800">{title}</Text>
|
||||
<TouchableOpacity onPress={onClose} className="p-2 bg-gray-100 rounded-full">
|
||||
@@ -56,7 +56,7 @@ export const TimePickerModal = ({ visible, initialDate, title, onConfirm, onClos
|
||||
onChange={(d) => setSelectedDate(d.date || new Date())}
|
||||
/>
|
||||
|
||||
{/* Bottone conferma */}
|
||||
{/* Confirm Button */}
|
||||
<TouchableOpacity
|
||||
onPress={handleConfirm}
|
||||
className="mt-4 w-full py-3 bg-[#099499] rounded-xl shadow-lg active:scale-[0.98]"
|
||||
|
||||
Reference in New Issue
Block a user