Update tabBarIcon sizes, use FlatList for invoices and refactor QrScanModal layout
This commit is contained in:
@@ -43,35 +43,35 @@ export default function ProtectedLayout() {
|
||||
name="index"
|
||||
options={{
|
||||
title: 'Home',
|
||||
tabBarIcon: ({ color, size }) => <Home pointerEvents="none" color={color} size={24} />,
|
||||
tabBarIcon: ({ color, size }) => <Home pointerEvents="none" color={color} size={28} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="attendance/index"
|
||||
options={{
|
||||
title: 'Presenze',
|
||||
tabBarIcon: ({ color, size }) => <Clock pointerEvents="none" color={color} size={24} />,
|
||||
tabBarIcon: ({ color, size }) => <Clock pointerEvents="none" color={color} size={28} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="journal"
|
||||
options={{
|
||||
title: 'Giornale',
|
||||
tabBarIcon: ({ color, size }) => <ImageIcon pointerEvents="none" color={color} size={24} />,
|
||||
tabBarIcon: ({ color, size }) => <ImageIcon pointerEvents="none" color={color} size={28} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="permits/index"
|
||||
options={{
|
||||
title: 'Ferie',
|
||||
tabBarIcon: ({ color, size }) => <CalendarIcon pointerEvents="none" color={color} size={24} />,
|
||||
tabBarIcon: ({ color, size }) => <CalendarIcon pointerEvents="none" color={color} size={28} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="invoice"
|
||||
options={{
|
||||
title: 'Fatture',
|
||||
tabBarIcon: ({ color, size }) => <FileText pointerEvents="none" color={color} size={24} />,
|
||||
tabBarIcon: ({ color, size }) => <FileText pointerEvents="none" color={color} size={28} />,
|
||||
href: authState.user?.isAdmin ? undefined : null,
|
||||
}}
|
||||
/>
|
||||
@@ -79,7 +79,7 @@ export default function ProtectedLayout() {
|
||||
name="machine/index"
|
||||
options={{
|
||||
title: 'Macchine',
|
||||
tabBarIcon: ({ color, size }) => <Car pointerEvents="none" color={color} size={24} />,
|
||||
tabBarIcon: ({ color, size }) => <Car pointerEvents="none" color={color} size={28} />,
|
||||
href: !authState.user?.isAdmin ? undefined : null,
|
||||
}}
|
||||
/>
|
||||
@@ -88,6 +88,7 @@ export default function ProtectedLayout() {
|
||||
options={{
|
||||
href: null,
|
||||
title: 'Profilo',
|
||||
tabBarStyle: { display: 'none' },
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { View, Text, ScrollView, TouchableOpacity, RefreshControl } from 'react-native';
|
||||
import { View, Text, FlatList, TouchableOpacity, RefreshControl } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
@@ -112,22 +112,26 @@ export default function InvoiceScreen() {
|
||||
|
||||
<View className="flex-1 bg-gray-50 rounded-t-[2.5rem] overflow-hidden">
|
||||
{/* List */}
|
||||
<ScrollView
|
||||
<FlatList
|
||||
data={invoices}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
className="flex-1"
|
||||
contentContainerStyle={{ padding: 20, paddingBottom: 100, gap: 16 }}
|
||||
showsVerticalScrollIndicator={true}
|
||||
scrollIndicatorInsets={{ right: 1 }}
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={['#1071C2']} />}
|
||||
>
|
||||
{invoices.length === 0 ? (
|
||||
initialNumToRender={10}
|
||||
maxToRenderPerBatch={10}
|
||||
windowSize={5}
|
||||
renderItem={({ item }) => (
|
||||
<InvoiceCard item={item} onPress={handleInvoicePress} />
|
||||
)}
|
||||
ListEmptyComponent={
|
||||
<View className="bg-white p-8 rounded-3xl border border-gray-100 items-center justify-center border-dashed mt-4">
|
||||
<Text className="text-gray-400 font-medium text-center">Nessuna fattura trovata</Text>
|
||||
</View>
|
||||
) : (
|
||||
invoices.map((invoice) => (
|
||||
<InvoiceCard key={invoice.id} item={invoice} onPress={handleInvoicePress} />
|
||||
)))}
|
||||
</ScrollView>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* FAB Filter */}
|
||||
<TouchableOpacity
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 473 KiB After Width: | Height: | Size: 121 KiB |
+46
-30
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { View, Text, Modal, TouchableOpacity, Vibration, StyleSheet, Dimensions } from 'react-native';
|
||||
import { View, Text, Modal, TouchableOpacity, Vibration, StyleSheet, useWindowDimensions } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { CameraView, useCameraPermissions } from 'expo-camera';
|
||||
import { X, ScanLine } from 'lucide-react-native';
|
||||
@@ -14,18 +14,17 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
|
||||
const [permission, requestPermission] = useCameraPermissions();
|
||||
const [scanned, setScanned] = useState(false);
|
||||
const scanInProgress = useRef(false);
|
||||
const { width, height } = Dimensions.get('window');
|
||||
const { width, height } = useWindowDimensions();
|
||||
const squareSize = Math.min(width * 0.8, height * 0.8, 400);
|
||||
|
||||
// Permission Handling and Reset Scanned State on Modal Open
|
||||
// Permission handling and scanned state reset on modal open
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
if (!visible) return;
|
||||
setScanned(false);
|
||||
scanInProgress.current = false;
|
||||
if (permission && !permission.granted && permission.canAskAgain) {
|
||||
requestPermission();
|
||||
}
|
||||
}
|
||||
}, [visible, permission]);
|
||||
|
||||
const handleBarCodeScanned = ({ type, data }: { type: string; data: string }) => {
|
||||
@@ -43,10 +42,6 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
|
||||
return <View />;
|
||||
}
|
||||
|
||||
if (!permission.granted && visible) {
|
||||
requestPermission();
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
@@ -61,39 +56,60 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
|
||||
facing="back"
|
||||
onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
|
||||
barcodeScannerSettings={{
|
||||
barcodeTypes: ["qr"],
|
||||
barcodeTypes: ['qr'],
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Dark Overlay with Transparent "Hole" (Visually Simulated with Borders or Opacity) */}
|
||||
<SafeAreaView className="flex-1 justify-between bg-black/60 pt-8">
|
||||
{/* Header Overlay */}
|
||||
<View className="items-center">
|
||||
<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>
|
||||
</View>
|
||||
{/* Mask Overlay with transparent cutout */}
|
||||
<View style={StyleSheet.absoluteFillObject} pointerEvents="none">
|
||||
<View className="flex-1 bg-black/60" />
|
||||
<View style={{ height: squareSize }} className="flex-row">
|
||||
<View className="flex-1 bg-black/60" />
|
||||
|
||||
{/* Central Area (Transparent for the camera) */}
|
||||
<View className="items-center justify-center" style={{ height: squareSize }}>
|
||||
<View style={{ width: squareSize, height: squareSize }}
|
||||
className="border-2 border-[#1071C2] bg-transparent relative justify-center items-center">
|
||||
{/* Decorative Corners */}
|
||||
{/* Scanner Target Frame */}
|
||||
<View
|
||||
style={{ width: squareSize, height: squareSize }}
|
||||
className="border-2 border-[#1071C2] justify-center items-center relative"
|
||||
>
|
||||
<View className="absolute top-0 left-0 w-6 h-6 border-l-4 border-t-4 border-[#1071C2]" />
|
||||
<View className="absolute top-0 right-0 w-6 h-6 border-r-4 border-t-4 border-[#1071C2]" />
|
||||
<View className="absolute bottom-0 left-0 w-6 h-6 border-l-4 border-b-4 border-[#1071C2]" />
|
||||
<View className="absolute bottom-0 right-0 w-6 h-6 border-r-4 border-b-4 border-[#1071C2]" />
|
||||
|
||||
{/* Animated Scan Line or Icon */}
|
||||
{!scanned && <ScanLine color="#1071C2" size={40} className="opacity-50" pointerEvents="none" />}
|
||||
</View>
|
||||
{!scanned && <ScanLine color="#1071C2" size={40} />}
|
||||
</View>
|
||||
|
||||
{/* Footer Overlay */}
|
||||
<View className="items-center justify-end pb-12">
|
||||
<TouchableOpacity onPress={onClose} className="bg-white/20 p-4 rounded-full">
|
||||
<X color="white" size={32} pointerEvents="none" />
|
||||
<View className="flex-1 bg-black/60" />
|
||||
</View>
|
||||
<View className="flex-1 bg-black/60" />
|
||||
</View>
|
||||
|
||||
{/* UI Overlay */}
|
||||
<SafeAreaView style={StyleSheet.absoluteFillObject} pointerEvents="box-none">
|
||||
<View className="flex-1 justify-between pt-8">
|
||||
{/* Header */}
|
||||
<View className="items-center">
|
||||
<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>
|
||||
</View>
|
||||
|
||||
{/* Footer */}
|
||||
<View className="items-center pb-12">
|
||||
<TouchableOpacity
|
||||
onPress={onClose}
|
||||
className="bg-white/20 p-4 rounded-full"
|
||||
>
|
||||
<X color="white" size={32} />
|
||||
</TouchableOpacity>
|
||||
<Text className="text-white mt-4 font-medium">Chiudi</Text>
|
||||
|
||||
<Text className="text-white mt-4 font-medium">
|
||||
Chiudi
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user