From efc23ab244ba74deecce0c0757b778e2fba2ad86 Mon Sep 17 00:00:00 2001 From: TargetMisser <52361977+TargetMisser@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:12:53 +0200 Subject: [PATCH 1/3] refactor: design & code quality refresh - Fix DrawerMenu: icon color hardcoded #2563EB -> colors.primary (theme-aware) - Fix DrawerMenu: version label 'v1.0' -> 'v1.1.0' (matches package.json) - Remove CalendarScreen: module-level const PRIMARY = '#2563EB', replaced with colors.primary - Fix NotepadScreen: saveBtnDim hardcoded backgroundColor -> opacity: 0.55 (works in all themes) - Replace all makeStyles(c: any) -> makeStyles(c: ThemeColors) across 8 files (14 occurrences): DrawerMenu, ShiftTimeline, NotepadScreen, FlightScreen, HomeScreen, CalendarScreen, PhonebookScreen (x3), PasswordScreen (x3), ManualsScreen (x3) - Add 'type ThemeColors' to imports in all affected files - Wrap all console.error/warn in if (__DEV__) guard (20+ occurrences): CalendarScreen (x6), FlightScreen (x5), HomeScreen (x4), PasswordScreen (x3), autoNotifications.ts (x3), useDynamicTheme.ts (x1) - App.tsx: guard auto-notification count log with __DEV__ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- App.tsx | 2 +- src/components/DrawerMenu.tsx | 8 ++++---- src/components/ShiftTimeline.tsx | 4 ++-- src/hooks/useDynamicTheme.ts | 2 +- src/screens/CalendarScreen.tsx | 19 +++++++++---------- src/screens/FlightScreen.tsx | 14 +++++++------- src/screens/HomeScreen.tsx | 12 ++++++------ src/screens/ManualsScreen.tsx | 8 ++++---- src/screens/NotepadScreen.tsx | 6 +++--- src/screens/PasswordScreen.tsx | 14 +++++++------- src/screens/PhonebookScreen.tsx | 8 ++++---- src/utils/autoNotifications.ts | 6 +++--- 12 files changed, 51 insertions(+), 52 deletions(-) diff --git a/App.tsx b/App.tsx index 4798824..3047a94 100644 --- a/App.tsx +++ b/App.tsx @@ -87,7 +87,7 @@ function AppInner() { // ─── Auto-schedule flight notifications on startup ───────────────────────── useEffect(() => { autoScheduleNotifications().then(count => { - if (count > 0) console.log(`Auto-scheduled ${count} notifications`); + if (count > 0 && __DEV__) console.log(`Auto-scheduled ${count} notifications`); }).catch(() => {}); }, []); diff --git a/src/components/DrawerMenu.tsx b/src/components/DrawerMenu.tsx index 932d200..ebb6384 100644 --- a/src/components/DrawerMenu.tsx +++ b/src/components/DrawerMenu.tsx @@ -3,7 +3,7 @@ import { Animated, Modal, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; type DrawerItem = { id: string; @@ -89,7 +89,7 @@ export default function DrawerMenu({ visible, onClose, onSelect }: Props) { activeOpacity={0.7} > - + {item.label} @@ -103,14 +103,14 @@ export default function DrawerMenu({ visible, onClose, onSelect }: Props) { {/* Divider */} - AeroStaff Pro · v1.0 + AeroStaff Pro · v1.1.0 ); } -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ root: { flex: 1, flexDirection: 'row' }, overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(15,23,42,0.5)' }, diff --git a/src/components/ShiftTimeline.tsx b/src/components/ShiftTimeline.tsx index 902c665..b989dd3 100644 --- a/src/components/ShiftTimeline.tsx +++ b/src/components/ShiftTimeline.tsx @@ -4,7 +4,7 @@ import { ActivityIndicator, Dimensions, LayoutAnimation, Platform, UIManager, } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; import { useAirport } from '../context/AirportContext'; import { getAirlineOps, getAirlineColor } from '../utils/airlineOps'; import { fetchAirportScheduleRaw } from '../utils/fr24api'; @@ -300,7 +300,7 @@ export default function ShiftTimeline({ visible, onClose, shiftStart, shiftEnd, ); } -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'flex-end' }, sheet: { diff --git a/src/hooks/useDynamicTheme.ts b/src/hooks/useDynamicTheme.ts index b32b0eb..8570ed4 100644 --- a/src/hooks/useDynamicTheme.ts +++ b/src/hooks/useDynamicTheme.ts @@ -118,7 +118,7 @@ async function resolveTheme(): Promise { globalCachedTheme = selected; return selected; } catch (err) { - console.warn('Errore caricamento tema dinamico:', err); + if (__DEV__) console.warn('Errore caricamento tema dinamico:', err); return themes.default; } finally { pendingFetch = null; diff --git a/src/screens/CalendarScreen.tsx b/src/screens/CalendarScreen.tsx index 1266625..ae72df3 100644 --- a/src/screens/CalendarScreen.tsx +++ b/src/screens/CalendarScreen.tsx @@ -10,7 +10,7 @@ import * as FileSystem from 'expo-file-system/legacy'; import { WebView } from 'react-native-webview'; import { MaterialIcons } from '@expo/vector-icons'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; import { useAirport } from '../context/AirportContext'; import { fetchAirportScheduleRaw } from '../utils/fr24api'; import { @@ -23,7 +23,6 @@ import { type ParsedSchedule, type ParsedEmployee, type ParsedShift, } from '../utils/pdfShiftParser'; -const PRIMARY = '#2563EB'; const STORAGE_KEY = '@shift_import_name'; type ShiftEvent = { @@ -198,7 +197,7 @@ export default function CalendarScreen() { localData[iso].push({ id: e.id, title: e.title, startDate: e.startDate, endDate: e.endDate }); // Lavoro has priority over Riposo for dot color if (e.title.includes('Lavoro') || !dots[iso]) { - dots[iso] = e.title.includes('Riposo') ? '#10b981' : PRIMARY; + dots[iso] = e.title.includes('Riposo') ? '#10b981' : colors.primary; } } }); @@ -206,7 +205,7 @@ export default function CalendarScreen() { setEventsData(localData); setLoading(false); fetchWeatherAndFlights(start, end, localData); - } catch (e) { console.error(e); setLoading(false); } + } catch (e) { if (__DEV__) console.error(e); setLoading(false); } }; const fetchWeatherAndFlights = async (start: Date, end: Date, localData: Record) => { @@ -224,7 +223,7 @@ export default function CalendarScreen() { dict[date] = { weatherText: m.text, weatherIcon: m.icon, flightCount: 0 }; }); } - } catch (e) { console.warn('[calWeather]', e); } + } catch (e) { if (__DEV__) console.warn('[calWeather]', e); } try { const { arrivals, departures } = await fetchAirportScheduleRaw(airportCode); const allF = [...arrivals, ...departures]; @@ -240,7 +239,7 @@ export default function CalendarScreen() { if (dict[iso]) dict[iso].flightCount = cnt; else dict[iso] = { weatherText: 'N/A', weatherIcon: '❓', flightCount: cnt }; } }); - } catch (e) { console.warn('[calFlights]', e); } + } catch (e) { if (__DEV__) console.warn('[calFlights]', e); } setDailyStats(dict); }; @@ -270,7 +269,7 @@ export default function CalendarScreen() { setImportModalVisible(true); setPdfHtml(getPdfExtractorHtml(base64)); } catch (e: any) { - console.error(`Import error at step=${step}:`, e); + if (__DEV__) console.error(`Import error at step=${step}:`, e); Alert.alert('Errore', `Errore (${step}): ${e?.message || e}`); } }; @@ -311,7 +310,7 @@ export default function CalendarScreen() { setImportStep('pickName'); } catch (e) { - console.error(e); + if (__DEV__) console.error(e); Alert.alert('Errore', 'Errore nel parsing del PDF'); setImportModalVisible(false); setImportStep('idle'); @@ -357,7 +356,7 @@ export default function CalendarScreen() { Alert.alert('Importazione completata', `${saved} turni salvati nel calendario`); }, 800); } catch (e) { - console.error(e); + if (__DEV__) console.error(e); Alert.alert('Errore', 'Errore durante il salvataggio'); setImportStep('idle'); } @@ -730,7 +729,7 @@ export default function CalendarScreen() { ); } -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ pageHeader: { backgroundColor: c.card, paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: c.border }, pageTitle: { fontSize: 22, fontWeight: 'bold', color: c.primaryDark }, diff --git a/src/screens/FlightScreen.tsx b/src/screens/FlightScreen.tsx index 155fb94..cce1020 100644 --- a/src/screens/FlightScreen.tsx +++ b/src/screens/FlightScreen.tsx @@ -8,7 +8,7 @@ import * as Calendar from 'expo-calendar'; import * as Notifications from 'expo-notifications'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { MaterialIcons } from '@expo/vector-icons'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; import { useAirport } from '../context/AirportContext'; import { getAirlineOps, getAirlineColor } from '../utils/airlineOps'; import { fetchAirportScheduleRaw } from '../utils/fr24api'; @@ -34,7 +34,7 @@ try { Notifications.setNotificationHandler({ shouldShowBanner: true, shouldShowList: true, }), -}); } catch (e) { console.warn('[notifHandler]', e); } +}); } catch (e) { if (__DEV__) console.warn('[notifHandler]', e); } function LogoPill({ iataCode, airlineName, color }: { iataCode: string; airlineName: string; color: string }) { @@ -396,7 +396,7 @@ export default function FlightScreen() { await cancelPreviousNotifications(); setScheduledCount(0); } - } catch (e) { console.error('[fetchAll]', e); } finally { setLoading(false); setRefreshing(false); } + } catch (e) { if (__DEV__) console.error('[fetchAll]', e); } finally { setLoading(false); setRefreshing(false); } }, [airportCode, airportLoading]); useEffect(() => { @@ -461,7 +461,7 @@ export default function FlightScreen() { const tab = activeTab; await AsyncStorage.setItem(PINNED_FLIGHT_KEY, JSON.stringify({ ...item, _pinTab: tab, _pinnedAt: Date.now() })); setPinnedFlightId(id); - try { await schedulePinnedNotifications(item, tab); } catch (e) { console.warn('[pinnedNotif]', e); } + try { await schedulePinnedNotifications(item, tab); } catch (e) { if (__DEV__) console.warn('[pinnedNotif]', e); } // Send to watch if (WearDataSender) { const payload = JSON.stringify({ @@ -488,10 +488,10 @@ export default function FlightScreen() { const unpinFlight = useCallback(async () => { try { await AsyncStorage.removeItem(PINNED_FLIGHT_KEY); - try { await cancelPinnedNotifications(); } catch (e) { console.warn('[cancelPinNotif]', e); } + try { await cancelPinnedNotifications(); } catch (e) { if (__DEV__) console.warn('[cancelPinNotif]', e); } setPinnedFlightId(null); if (WearDataSender) WearDataSender.clearPinnedFlight(); - } catch (e) { console.error('[unpin]', e); } + } catch (e) { if (__DEV__) console.error('[unpin]', e); } }, []); const userShift = activeDay === 'today' ? shifts.today : shifts.tomorrow; @@ -726,7 +726,7 @@ export default function FlightScreen() { ); } -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ pageHeader: { backgroundColor: c.card, paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: c.border, flexDirection: 'row', alignItems: 'center' }, notifBtn: { width: 42, height: 42, borderRadius: 21, backgroundColor: c.cardSecondary, justifyContent: 'center', alignItems: 'center' }, diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 466c5ef..696690a 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -10,7 +10,7 @@ import { WebView } from 'react-native-webview'; import * as ImagePicker from 'expo-image-picker'; import * as Calendar from 'expo-calendar'; import * as Location from 'expo-location'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; import ShiftTimeline from '../components/ShiftTimeline'; import { getAirlineOps, getAirlineColor } from '../utils/airlineOps'; @@ -253,7 +253,7 @@ export default function HomeScreen() { const events = await Calendar.getEventsAsync([cal.id], d, dEnd); const shift = events.find(e => e.title.includes('Lavoro') || e.title.includes('Riposo')); setShiftEvent(shift || null); - } catch (e) { console.error('[shift]', e); } finally { setLoadingShift(false); } + } catch (e) { if (__DEV__) console.error('[shift]', e); } finally { setLoadingShift(false); } }; const fetchWeather = async () => { @@ -267,7 +267,7 @@ export default function HomeScreen() { const temp = Math.round(json.current?.temperature_2m ?? 0); const w = weatherMap[code] || { text: 'Sereno', icon: '☀️' }; setWeather({ ...w, temp }); - } catch (e) { console.warn('[weather]', e); } + } catch (e) { if (__DEV__) console.warn('[weather]', e); } }; const pickImage = async () => { @@ -291,7 +291,7 @@ export default function HomeScreen() { true; `); } - } catch (e) { console.error('[imagePicker]', e); setProcessing(false); } + } catch (e) { if (__DEV__) console.error('[imagePicker]', e); setProcessing(false); } }; const handleWebViewMessage = (event: any) => { @@ -299,7 +299,7 @@ export default function HomeScreen() { const r = JSON.parse(event.nativeEvent.data); if (r.success) setOcrText(r.text); else Alert.alert('Errore riconoscimento testo', r.error || 'Prova con un\'immagine più nitida o meglio illuminata.'); - } catch (e) { console.error('[ocrMessage]', e); } finally { setProcessing(false); } + } catch (e) { if (__DEV__) console.error('[ocrMessage]', e); } finally { setProcessing(false); } }; const parseAndSave = async () => { @@ -429,7 +429,7 @@ export default function HomeScreen() { ); } -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ hiddenWV: { height: 1, width: 1, opacity: 0, position: 'absolute', top: -100 }, topRow: { flexDirection: 'row', gap: 12, padding: 16, paddingBottom: 8 }, diff --git a/src/screens/ManualsScreen.tsx b/src/screens/ManualsScreen.tsx index f20e5b3..ad9d783 100644 --- a/src/screens/ManualsScreen.tsx +++ b/src/screens/ManualsScreen.tsx @@ -5,7 +5,7 @@ import { LayoutAnimation, Platform, UIManager, TextInput, Modal, Alert, KeyboardAvoidingView, } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; const STORAGE_KEY = 'manuals_data_v2'; @@ -416,7 +416,7 @@ function CommandsTab({ commands, colors }: { commands: DCSCommand[]; colors: any } // ─── Item component ─────────────────────────────────────────────────────────── -function makeItemStyles(c: any) { +function makeItemStyles(c: ThemeColors) { return StyleSheet.create({ wrapper: { backgroundColor: c.card, @@ -485,7 +485,7 @@ function ManualItemRow({ } // ─── Section component ──────────────────────────────────────────────────────── -function makeSectionStyles(c: any) { +function makeSectionStyles(c: ThemeColors) { return StyleSheet.create({ wrapper: { marginBottom: 12, @@ -583,7 +583,7 @@ const modalStyles = StyleSheet.create({ }); // ─── Main Screen ────────────────────────────────────────────────────────────── -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ root: { flex: 1, backgroundColor: c.bg }, header: { diff --git a/src/screens/NotepadScreen.tsx b/src/screens/NotepadScreen.tsx index 4a71441..158ee78 100644 --- a/src/screens/NotepadScreen.tsx +++ b/src/screens/NotepadScreen.tsx @@ -5,11 +5,11 @@ import { } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { MaterialIcons } from '@expo/vector-icons'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; const STORAGE_KEY = 'aerostaff_notepad_v1'; -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ root: { flex: 1, backgroundColor: c.bg }, toolbar: { @@ -27,7 +27,7 @@ function makeStyles(c: any) { backgroundColor: c.primary, borderRadius: 10, paddingHorizontal: 14, paddingVertical: 8, }, - saveBtnDim: { backgroundColor: '#93C5FD' }, + saveBtnDim: { opacity: 0.55 }, saveTxt: { color: '#fff', fontWeight: '600', fontSize: 13 }, statusBar: { flexDirection: 'row', alignItems: 'center', gap: 6, diff --git a/src/screens/PasswordScreen.tsx b/src/screens/PasswordScreen.tsx index 54ed239..cef8710 100644 --- a/src/screens/PasswordScreen.tsx +++ b/src/screens/PasswordScreen.tsx @@ -6,7 +6,7 @@ import { import AsyncStorage from '@react-native-async-storage/async-storage'; import * as SecureStore from 'expo-secure-store'; import { MaterialIcons } from '@expo/vector-icons'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; const PASSWORDS_KEY = 'aerostaff_passwords_v1'; const PIN_KEY = 'aerostaff_pin_v1'; @@ -167,7 +167,7 @@ export default function PasswordScreen() { setPinEnabled(false); await AsyncStorage.setItem(PIN_ENABLED_KEY, 'false'); await deleteSecurePin(); - } catch (e) { console.error('[pin] disable error', e); } + } catch (e) { if (__DEV__) console.error('[pin] disable error', e); } }}, ]); } else { @@ -183,7 +183,7 @@ export default function PasswordScreen() { setPinMode(null); Alert.alert('PIN impostato', 'La schermata password è ora protetta.'); } catch (e) { - console.error('[pin] setup error', e); + if (__DEV__) console.error('[pin] setup error', e); Alert.alert('Errore', 'Impossibile impostare il PIN. Riprova.'); } }, []); @@ -197,7 +197,7 @@ export default function PasswordScreen() { Alert.alert('PIN errato', 'Riprova.'); } } catch (e) { - console.error('[pin] unlock error', e); + if (__DEV__) console.error('[pin] unlock error', e); Alert.alert('Errore', 'Impossibile verificare il PIN. Riprova.'); } }, []); @@ -349,7 +349,7 @@ export default function PasswordScreen() { } // ─── Styles ─────────────────────────────────────────────────────────────────── -function makePinStyles(c: any) { +function makePinStyles(c: ThemeColors) { return StyleSheet.create({ overlay: { flex: 1, backgroundColor: c.bg, justifyContent: 'center', alignItems: 'center' }, box: { alignItems: 'center', padding: 32, width: '100%', maxWidth: 320 }, @@ -364,7 +364,7 @@ function makePinStyles(c: any) { }); } -function makeRowStyles(c: any) { +function makeRowStyles(c: ThemeColors) { return StyleSheet.create({ card: { backgroundColor: c.card, borderRadius: 14, padding: 14, marginBottom: 10, flexDirection: 'row', alignItems: 'flex-start', borderWidth: 1, borderColor: c.border, shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.05, shadowRadius: 6, elevation: c.isDark ? 0 : 2 }, cardLeft:{ flex: 1 }, @@ -380,7 +380,7 @@ function makeRowStyles(c: any) { }); } -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ root: { flex: 1, backgroundColor: c.bg }, toolbar: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 12, backgroundColor: c.card, borderBottomWidth: 1, borderBottomColor: c.border }, diff --git a/src/screens/PhonebookScreen.tsx b/src/screens/PhonebookScreen.tsx index 5f2cf27..5770032 100644 --- a/src/screens/PhonebookScreen.tsx +++ b/src/screens/PhonebookScreen.tsx @@ -5,7 +5,7 @@ import { } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { MaterialIcons } from '@expo/vector-icons'; -import { useAppTheme } from '../context/ThemeContext'; +import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; const STORAGE_KEY = 'aerostaff_phonebook_v1'; @@ -40,7 +40,7 @@ interface EditModalProps { onClose: () => void; } -function makeModalStyles(c: any) { +function makeModalStyles(c: ThemeColors) { return StyleSheet.create({ overlay: { flex: 1, justifyContent: 'flex-end', @@ -205,7 +205,7 @@ function EditModal({ visible, contact, onSave, onClose }: EditModalProps) { // ─── Riga contatto ──────────────────────────────────────────────────────────── -function makeRowStyles(c: any) { +function makeRowStyles(c: ThemeColors) { return StyleSheet.create({ card: { flexDirection: 'row', alignItems: 'center', @@ -285,7 +285,7 @@ function ContactRow({ contact, onEdit, onDelete }: ContactRowProps) { // ─── Main Screen ────────────────────────────────────────────────────────────── -function makeStyles(c: any) { +function makeStyles(c: ThemeColors) { return StyleSheet.create({ root: { flex: 1, backgroundColor: c.bg }, header: { diff --git a/src/utils/autoNotifications.ts b/src/utils/autoNotifications.ts index 4c44518..3f0e7f2 100644 --- a/src/utils/autoNotifications.ts +++ b/src/utils/autoNotifications.ts @@ -93,7 +93,7 @@ export async function autoScheduleNotifications(): Promise { }); newIds.push(id); } catch (err) { - console.error('Failed to schedule arrival notification:', err); + if (__DEV__) console.error('Failed to schedule arrival notification:', err); } } @@ -146,7 +146,7 @@ export async function autoScheduleNotifications(): Promise { newIds.push(id); } } catch (err) { - console.error('Failed to schedule departure notification:', err); + if (__DEV__) console.error('Failed to schedule departure notification:', err); } } @@ -170,7 +170,7 @@ export async function autoScheduleNotifications(): Promise { await AsyncStorage.setItem(LAST_SCHEDULE_KEY, todayKey); return newIds.length; } catch (e) { - console.error('autoScheduleNotifications error:', e); + if (__DEV__) console.error('autoScheduleNotifications error:', e); return 0; } } From 7e0cf9b638b5ff5c93af25ff28d0b658d7852412 Mon Sep 17 00:00:00 2001 From: TargetMisser <52361977+TargetMisser@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:19:48 +0200 Subject: [PATCH 2/3] fix: address code review blockers - DrawerMenu: import version from package.json instead of hardcoded string - eliminates version drift between package.json and UI - requires resolveJsonModule (included in expo/tsconfig.base) - NotepadScreen: add comment explaining intentional opacity cascade on saveBtnDim - opacity: 0.55 dims entire button (icon + label + bg) for 'already saved' state - intentional UX: full-button fade signals inactive state - Add src/utils/devLog.ts utility (devLog/devWarn/devError) - centralises __DEV__ guards for future use - Metro eliminates dead branches in production bundles Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/components/DrawerMenu.tsx | 3 ++- src/screens/NotepadScreen.tsx | 2 ++ src/utils/devLog.ts | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/utils/devLog.ts diff --git a/src/components/DrawerMenu.tsx b/src/components/DrawerMenu.tsx index ebb6384..ef577d6 100644 --- a/src/components/DrawerMenu.tsx +++ b/src/components/DrawerMenu.tsx @@ -1,3 +1,4 @@ +import { version } from '../../package.json'; import React, { useEffect, useRef, useState, useMemo } from 'react'; import { Animated, Modal, StyleSheet, Text, TouchableOpacity, View, @@ -103,7 +104,7 @@ export default function DrawerMenu({ visible, onClose, onSelect }: Props) { {/* Divider */} - AeroStaff Pro · v1.1.0 + AeroStaff Pro · v{version} diff --git a/src/screens/NotepadScreen.tsx b/src/screens/NotepadScreen.tsx index 158ee78..f33ea00 100644 --- a/src/screens/NotepadScreen.tsx +++ b/src/screens/NotepadScreen.tsx @@ -27,6 +27,8 @@ function makeStyles(c: ThemeColors) { backgroundColor: c.primary, borderRadius: 10, paddingHorizontal: 14, paddingVertical: 8, }, + // Dims the entire save button (background + icon + label) when content is + // already saved — intentional: the full-button fade signals an inactive state. saveBtnDim: { opacity: 0.55 }, saveTxt: { color: '#fff', fontWeight: '600', fontSize: 13 }, statusBar: { diff --git a/src/utils/devLog.ts b/src/utils/devLog.ts new file mode 100644 index 0000000..8ae1dae --- /dev/null +++ b/src/utils/devLog.ts @@ -0,0 +1,20 @@ +/** + * Development-only logging helpers. + * In production builds Metro eliminates the dead `if (false)` branches, + * so these calls compile away entirely — no performance cost in prod. + */ + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const devWarn = (...args: any[]): void => { + if (__DEV__) console.warn(...args); +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const devError = (...args: any[]): void => { + if (__DEV__) console.error(...args); +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const devLog = (...args: any[]): void => { + if (__DEV__) console.log(...args); +}; From 571c760c38db63864612095199d3c938f0430025 Mon Sep 17 00:00:00 2001 From: TargetMisser <52361977+TargetMisser@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:33:10 +0200 Subject: [PATCH 3/3] feat: liquid glass + orange overhaul - ThemeContext: full orange palette (primary #F47B16), glass tokens (glass/glassBorder/glassStrong) - New AeroStaffLogo component: orange gradient icon + AEROSTAFF PRO wordmark, monochrome variant - New GlassCard component: BlurView shimmer card, 3 variants, Android fallback - App.tsx: glass AppBar (BlurView), orange gradient avatar, orange tab bar (tabIconActive) - DrawerMenu: BlurView bg + orange gradient header, new AeroStaffLogo - HomeScreen: orange shadows, glass borders, primaryLight ops badges - FlightScreen/CalendarScreen: orange-tinted shadows, glassBorder - PasswordScreen: glassBorder, orange shadow - SettingsScreen: orange previewAccent, orange card shadow - ShiftScreen: replaced hardcoded blue (#2563EB/#1E3A8A) with orange (#F47B16/#C2520A) - PhonebookScreen: Ops category color updated to primary orange Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- App.tsx | 50 +++++--- src/components/AeroStaffLogo.tsx | 197 +++++++++++++++++++++++++++++++ src/components/DrawerMenu.tsx | 142 +++++++++++++--------- src/components/GlassCard.tsx | 160 +++++++++++++++++++++++++ src/context/ThemeContext.tsx | 93 ++++++++------- src/screens/CalendarScreen.tsx | 2 +- src/screens/FlightScreen.tsx | 2 +- src/screens/HomeScreen.tsx | 44 +++---- src/screens/PasswordScreen.tsx | 2 +- src/screens/PhonebookScreen.tsx | 2 +- src/screens/SettingsScreen.tsx | 8 +- src/screens/ShiftScreen.tsx | 12 +- 12 files changed, 566 insertions(+), 148 deletions(-) create mode 100644 src/components/AeroStaffLogo.tsx create mode 100644 src/components/GlassCard.tsx diff --git a/App.tsx b/App.tsx index 3047a94..4a2b3bc 100644 --- a/App.tsx +++ b/App.tsx @@ -175,8 +175,13 @@ function AppInner() { backgroundColor={colors.appBar} /> - {/* Top App Bar */} - + {/* Top App Bar — liquid glass */} + + {overlay ? ( @@ -187,15 +192,20 @@ function AppInner() { )} - {appBarTitle} + {appBarTitle} {isWeather && ( {colors.weatherIcon} {colors.weatherLabel} )} - - MR - - + + MR + + {/* Screen Content */} {isWeather ? ( @@ -237,8 +247,8 @@ function AppInner() { icon={tab.icon} label={tab.label} focused={active} - activeColor={colors.primary} - inactiveColor={colors.isDark ? '#94A3B8' : '#64748B'} + activeColor={colors.tabIconActive} + inactiveColor={colors.tabIconInactive} onPress={() => { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); goToTab(TABS.findIndex(t => t.id === tab.id)); @@ -282,10 +292,11 @@ const styles = StyleSheet.create({ paddingHorizontal: 12, paddingVertical: 10, borderBottomWidth: 1, + overflow: 'hidden', }, iconBtn: { padding: 6, borderRadius: 8, marginRight: 6 }, titleRow: { flex: 1, flexDirection: 'row', alignItems: 'center', gap: 8 }, - appBarTitle: { fontSize: 18, fontWeight: 'bold', letterSpacing: 0.3 }, + appBarTitle: { fontSize: 18, fontWeight: '700', letterSpacing: 0.3 }, weatherChip: { fontSize: 11, color: 'rgba(255,255,255,0.8)', backgroundColor: 'rgba(255,255,255,0.15)', @@ -295,8 +306,9 @@ const styles = StyleSheet.create({ avatar: { width: 34, height: 34, borderRadius: 17, justifyContent: 'center', alignItems: 'center', + overflow: 'hidden', }, - avatarText: { fontSize: 12, fontWeight: 'bold' }, + avatarText: { fontSize: 12, fontWeight: '700', color: '#FFFFFF' }, content: { flex: 1 }, // ─── Glassmorphic floating tab bar ─── tabBarWrapper: { @@ -307,19 +319,19 @@ const styles = StyleSheet.create({ }, tabBarBlur: { flexDirection: 'row', - height: 64, - borderRadius: 32, + height: 66, + borderRadius: 33, justifyContent: 'space-around', alignItems: 'center', overflow: 'hidden', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.15)', + borderWidth: 0.75, + borderColor: 'rgba(255,255,255,0.22)', }, glassTab: { alignItems: 'center', justifyContent: 'center', - width: 64, - height: 54, + width: 68, + height: 56, }, glassLabel: { fontSize: 10, @@ -329,8 +341,8 @@ const styles = StyleSheet.create({ }, glassIndicator: { position: 'absolute', - bottom: 0, - width: 20, + bottom: 4, + width: 18, height: 3, borderRadius: 999, }, diff --git a/src/components/AeroStaffLogo.tsx b/src/components/AeroStaffLogo.tsx new file mode 100644 index 0000000..86a5f2a --- /dev/null +++ b/src/components/AeroStaffLogo.tsx @@ -0,0 +1,197 @@ +import React from 'react'; +import { View, Text, StyleSheet, Platform } from 'react-native'; +import { LinearGradient } from 'expo-linear-gradient'; + +type Size = 'small' | 'large'; + +interface Props { + /** 'large' = icon + wordmark (drawer header); 'small' = icon only */ + variant?: Size; + /** White-only mode for use inside orange/dark headers */ + monochrome?: boolean; +} + +export default function AeroStaffLogo({ variant = 'large', monochrome = false }: Props) { + return ( + + + {variant === 'large' && ( + + AERO + + {!monochrome && ( + + )} + STAFF + + + PRO + + + )} + + ); +} + +function AeroIconMark({ small, monochrome }: { small: boolean; monochrome: boolean }) { + const S = small ? 36 : 44; + const R = small ? 9 : 11; + const scale = S / 44; + + const fuselageW = Math.round(22 * scale); + const fuselageH = Math.round(4 * scale); + const wingW = Math.round(20 * scale); + const wingH = Math.round(2.5 * scale); + const tailW = Math.round(8 * scale); + const tailH = Math.round(2 * scale); + + const white = '#FFFFFF'; + const orange = '#F47B16'; + const fg = monochrome ? orange : white; + + return ( + + {monochrome ? ( + + ) : ( + + )} + {/* Specular highlight */} + + {/* Aircraft mark */} + + {/* Upper wing */} + + {/* Fuselage */} + + {/* Lower wing */} + + {/* Tail fin */} + + + + ); +} + +const FONT = Platform.select({ ios: undefined, android: 'Roboto', default: undefined }); + +const styles = StyleSheet.create({ + root: { + flexDirection: 'row', + alignItems: 'center', + gap: 10, + }, + rootSmall: { gap: 0 }, + wordmarkWrapper: { + flexDirection: 'row', + alignItems: 'center', + }, + wordmarkAero: { + fontSize: 19, + fontWeight: '700', + letterSpacing: 2.5, + color: '#FFFFFF', + fontFamily: FONT, + }, + wordmarkMono: { + color: '#FFFFFF', + }, + staffWrapper: { + overflow: 'hidden', + borderRadius: 2, + }, + wordmarkStaff: { + fontSize: 19, + fontWeight: '700', + letterSpacing: 2.5, + color: '#1C1C1E', + fontFamily: FONT, + }, + proBadge: { + marginLeft: 6, + backgroundColor: '#F47B16', + borderRadius: 4, + paddingHorizontal: 5, + paddingVertical: 2, + alignSelf: 'center', + marginBottom: 1, + }, + proBadgeMono: { + backgroundColor: '#FFFFFF', + }, + proBadgeText: { + fontSize: 8, + fontWeight: '800', + letterSpacing: 0.8, + color: '#FFFFFF', + fontFamily: FONT, + }, +}); + +const iconStyles = StyleSheet.create({ + container: { + overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#F47B16', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.45, + shadowRadius: 10, + elevation: 8, + }, + aircraft: { + alignItems: 'center', + justifyContent: 'center', + }, + fuselage: { + alignSelf: 'center', + }, + wing: { + alignSelf: 'flex-end', + borderRadius: 1, + }, + tail: { + alignSelf: 'flex-start', + marginTop: 1, + borderRadius: 1, + transform: [{ skewX: '-18deg' }], + }, +}); diff --git a/src/components/DrawerMenu.tsx b/src/components/DrawerMenu.tsx index ef577d6..fbcc5b2 100644 --- a/src/components/DrawerMenu.tsx +++ b/src/components/DrawerMenu.tsx @@ -4,7 +4,10 @@ import { Animated, Modal, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; +import { BlurView } from 'expo-blur'; +import { LinearGradient } from 'expo-linear-gradient'; import { useAppTheme, type ThemeColors } from '../context/ThemeContext'; +import AeroStaffLogo from './AeroStaffLogo'; type DrawerItem = { id: string; @@ -62,49 +65,58 @@ export default function DrawerMenu({ visible, onClose, onSelect }: Props) { {/* Drawer */} - - {/* Header */} - - - - - - AeroStaff Pro - Strumenti - - - - - + + + {/* Glass overlay tint */} + - {/* Section label */} - STRUMENTI - - {/* Menu items */} - - {ITEMS.map(item => ( - { onSelect(item.id); onClose(); }} - activeOpacity={0.7} - > - - - - - {item.label} - {item.sublabel} - - + {/* Orange gradient header */} + + + + - ))} - + - {/* Divider */} - + {/* Section label */} + STRUMENTI - AeroStaff Pro · v{version} + {/* Menu items */} + + {ITEMS.map(item => ( + { onSelect(item.id); onClose(); }} + activeOpacity={0.7} + > + + + + + {item.label} + {item.sublabel} + + + + ))} + + + {/* Divider */} + + + AeroStaff Pro · v{version} + @@ -114,24 +126,48 @@ export default function DrawerMenu({ visible, onClose, onSelect }: Props) { function makeStyles(c: ThemeColors) { return StyleSheet.create({ root: { flex: 1, flexDirection: 'row' }, - overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(15,23,42,0.5)' }, - drawer: { - width: DRAWER_WIDTH, backgroundColor: c.card === 'transparent' ? c.bg : c.card, height: '100%', paddingTop: 52, - shadowColor: '#000', shadowOffset: { width: 6, height: 0 }, shadowOpacity: c.isDark ? 0 : 0.18, shadowRadius: 20, elevation: c.isDark ? 0 : 24, + overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(10,5,0,0.55)' }, + drawerWrapper: { + width: DRAWER_WIDTH, + height: '100%', + overflow: 'hidden', + // Subtle warm glow shadow + shadowColor: '#F97316', + shadowOffset: { width: 6, height: 0 }, + shadowOpacity: 0.12, + shadowRadius: 24, + elevation: 20, }, - header: { - flexDirection: 'row', alignItems: 'center', gap: 12, - paddingHorizontal: 18, paddingBottom: 20, - borderBottomWidth: 1, borderBottomColor: c.border, + blurFill: { + ...StyleSheet.absoluteFillObject, + }, + glassTint: { + ...StyleSheet.absoluteFillObject, + }, + headerGradient: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 18, + paddingTop: 56, + paddingBottom: 22, }, - logoCircle: { width: 42, height: 42, borderRadius: 21, backgroundColor: c.primary, justifyContent: 'center', alignItems: 'center' }, - appName: { fontSize: 15, fontWeight: '700', color: c.primaryDark }, - appSub: { fontSize: 11, color: c.textMuted, marginTop: 1 }, closeIconBtn: { padding: 6 }, - sectionLabel: { fontSize: 10, fontWeight: '700', color: c.textMuted, letterSpacing: 1.2, paddingHorizontal: 20, paddingTop: 20, paddingBottom: 8 }, + sectionLabel: { + fontSize: 10, fontWeight: '700', color: c.textMuted, + letterSpacing: 1.4, paddingHorizontal: 20, paddingTop: 20, paddingBottom: 8, + }, items: { paddingHorizontal: 10 }, - item: { flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 13, paddingHorizontal: 10, borderRadius: 14, marginBottom: 2 }, - itemIcon: { width: 40, height: 40, borderRadius: 12, backgroundColor: c.primaryLight, justifyContent: 'center', alignItems: 'center' }, + item: { + flexDirection: 'row', alignItems: 'center', gap: 12, + paddingVertical: 13, paddingHorizontal: 10, + borderRadius: 16, marginBottom: 2, + }, + itemIcon: { + width: 42, height: 42, borderRadius: 14, + backgroundColor: c.primaryLight, + justifyContent: 'center', alignItems: 'center', + }, itemLabel: { fontSize: 14, fontWeight: '600', color: c.text }, itemSub: { fontSize: 11, color: c.textMuted, marginTop: 1 }, divider: { height: 1, backgroundColor: c.border, marginHorizontal: 18, marginTop: 16 }, diff --git a/src/components/GlassCard.tsx b/src/components/GlassCard.tsx new file mode 100644 index 0000000..295c2cf --- /dev/null +++ b/src/components/GlassCard.tsx @@ -0,0 +1,160 @@ +import React from 'react'; +import { View, StyleSheet, Platform, ViewStyle } from 'react-native'; +import { BlurView } from 'expo-blur'; +import { LinearGradient } from 'expo-linear-gradient'; +import { useAppTheme } from '../context/ThemeContext'; + +type Variant = 'default' | 'strong' | 'subtle'; + +interface GlassCardProps { + children: React.ReactNode; + style?: ViewStyle; + variant?: Variant; + /** Force blur on/off. Defaults to iOS-only for performance. */ + useBlur?: boolean; +} + +const RADIUS = 20; + +export default function GlassCard({ + children, + style, + variant = 'default', + useBlur = Platform.OS === 'ios', +}: GlassCardProps) { + const { colors } = useAppTheme(); + + type VariantConfig = { + bgOverlay: string; + blurIntensity: number; + borderColor: string; + shimmer: [string, string]; + }; + + const variantMap: Record = { + default: { + bgOverlay: colors.glass, + blurIntensity: colors.isDark ? 80 : 70, + borderColor: colors.glassBorder, + shimmer: colors.isDark + ? ['rgba(255,255,255,0.07)', 'rgba(255,255,255,0.00)'] + : ['rgba(255,255,255,0.55)', 'rgba(255,255,255,0.00)'], + }, + strong: { + bgOverlay: colors.glassStrong, + blurIntensity: colors.isDark ? 90 : 80, + borderColor: colors.isDark ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,1.00)', + shimmer: colors.isDark + ? ['rgba(255,255,255,0.10)', 'rgba(255,255,255,0.00)'] + : ['rgba(255,255,255,0.70)', 'rgba(255,255,255,0.00)'], + }, + subtle: { + bgOverlay: colors.cardSecondary, + blurIntensity: 50, + borderColor: colors.border, + shimmer: ['rgba(255,255,255,0.04)', 'rgba(255,255,255,0.00)'], + }, + }; + + const v = variantMap[variant]; + + const shadowStyle: ViewStyle = colors.isDark + ? { + shadowColor: '#000000', + shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.45, + shadowRadius: 24, + elevation: 12, + } + : { + shadowColor: colors.primary, + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.12, + shadowRadius: 20, + elevation: 6, + }; + + // Android fallback: no BlurView, opaque background + const androidFallback: ViewStyle = { + backgroundColor: colors.isDark + ? 'rgba(28,28,32,0.96)' + : 'rgba(255,255,255,0.97)', + }; + + if (!useBlur) { + return ( + + + + + {children} + + + ); + } + + return ( + + + {/* Solid color overlay to control tint */} + + {/* Inner shimmer — specular top highlight */} + + {/* Border ring */} + + {/* Content */} + {children} + + + ); +} + +const styles = StyleSheet.create({ + outerShadow: { + borderRadius: RADIUS, + }, + blurContainer: { + borderRadius: RADIUS, + overflow: 'hidden', + }, + shimmer: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + height: '55%', + borderTopLeftRadius: RADIUS, + borderTopRightRadius: RADIUS, + }, + border: { + borderRadius: RADIUS, + borderWidth: 0.75, + }, + content: { + padding: 16, + }, +}); diff --git a/src/context/ThemeContext.tsx b/src/context/ThemeContext.tsx index f56c9b1..6d99c9e 100644 --- a/src/context/ThemeContext.tsx +++ b/src/context/ThemeContext.tsx @@ -18,6 +18,10 @@ export type ThemeColors = { primary: string; primaryDark: string; primaryLight: string; + // Glass tokens (liquid glass aesthetic) + glass: string; + glassBorder: string; + glassStrong: string; // UI border: string; appBar: string; @@ -37,44 +41,50 @@ export type ThemeColors = { // ─── Tema Chiaro ────────────────────────────────────────────────────────────── const LIGHT: ThemeColors = { - bg: '#F3F4F6', - card: '#FFFFFF', - cardSecondary: '#F8FAFC', - text: '#1E293B', - textSub: '#64748B', - textMuted: '#94A3B8', - primary: '#2563EB', - primaryDark: '#1E3A8A', - primaryLight: '#DBEAFE', - border: '#E5E7EB', - appBar: '#FFFFFF', - tabBar: '#FFFFFF', - tabIconActive: '#FFFFFF', - tabIconInactive:'#94A3B8', - tabLabelActive: '#2563EB', - pillActive: '#2563EB', + bg: '#F2F2F7', + card: 'rgba(255,255,255,0.72)', + cardSecondary: 'rgba(242,242,247,0.80)', + text: '#1C1C1E', + textSub: '#48484A', + textMuted: 'rgba(60,60,67,0.45)', + primary: '#F47B16', + primaryDark: '#C2520A', + primaryLight: '#FFEDD5', + glass: 'rgba(255,255,255,0.58)', + glassBorder: 'rgba(255,255,255,0.88)', + glassStrong: 'rgba(255,255,255,0.84)', + border: 'rgba(60,60,67,0.18)', + appBar: 'rgba(242,242,247,0.85)', + tabBar: 'rgba(255,255,255,0.90)', + tabIconActive: '#F47B16', + tabIconInactive:'rgba(60,60,67,0.38)', + tabLabelActive: '#F47B16', + pillActive: 'rgba(244,123,22,0.14)', statusBar: 'dark-content', isDark: false, }; // ─── Tema Scuro ─────────────────────────────────────────────────────────────── const DARK: ThemeColors = { - bg: '#0F172A', - card: '#1E293B', - cardSecondary: '#0F172A', - text: '#F1F5F9', - textSub: '#94A3B8', - textMuted: '#475569', - primary: '#3B82F6', - primaryDark: '#93C5FD', - primaryLight: '#1E3A5F', - border: '#334155', - appBar: '#1E293B', - tabBar: '#1E293B', - tabIconActive: '#FFFFFF', - tabIconInactive:'#475569', - tabLabelActive: '#60A5FA', - pillActive: '#3B82F6', + bg: '#0A0A0C', + card: 'rgba(255,255,255,0.07)', + cardSecondary: 'rgba(255,255,255,0.04)', + text: '#FFFFFF', + textSub: 'rgba(235,235,245,0.75)', + textMuted: 'rgba(235,235,245,0.38)', + primary: '#FF9A42', + primaryDark: '#F47B16', + primaryLight: 'rgba(255,154,66,0.20)', + glass: 'rgba(255,255,255,0.06)', + glassBorder: 'rgba(255,255,255,0.13)', + glassStrong: 'rgba(28,28,30,0.84)', + border: 'rgba(255,255,255,0.11)', + appBar: 'rgba(10,10,12,0.82)', + tabBar: 'rgba(10,10,12,0.90)', + tabIconActive: '#FF9A42', + tabIconInactive:'rgba(235,235,245,0.35)', + tabLabelActive: '#FF9A42', + pillActive: 'rgba(255,154,66,0.18)', statusBar: 'light-content', isDark: true, }; @@ -86,19 +96,22 @@ function weatherToColors(gradient: readonly [string, string, ...string[]], icon: bg: dominant, card: 'transparent', cardSecondary: 'transparent', - text: '#E8F4FD', - textSub: '#A8C5E8', - textMuted: '#7BA3CA', - primary: '#136DEC', - primaryDark: '#93C5FD', - primaryLight: 'rgba(255,255,255,0.12)', + text: '#FFFFFF', + textSub: 'rgba(235,235,245,0.75)', + textMuted: 'rgba(235,235,245,0.45)', + primary: '#FF9A42', + primaryDark: '#F47B16', + primaryLight: 'rgba(255,154,66,0.20)', + glass: 'rgba(255,255,255,0.15)', + glassBorder: 'rgba(255,255,255,0.25)', + glassStrong: 'rgba(255,255,255,0.22)', border: 'rgba(255,255,255,0.15)', appBar: dominant, tabBar: gradient[gradient.length - 1], - tabIconActive: '#FFFFFF', + tabIconActive: '#FF9A42', tabIconInactive:'rgba(255,255,255,0.45)', tabLabelActive: '#FFFFFF', - pillActive: 'rgba(255,255,255,0.25)', + pillActive: 'rgba(255,255,255,0.30)', statusBar: 'light-content', isDark: true, gradient, diff --git a/src/screens/CalendarScreen.tsx b/src/screens/CalendarScreen.tsx index ae72df3..7203b5b 100644 --- a/src/screens/CalendarScreen.tsx +++ b/src/screens/CalendarScreen.tsx @@ -751,7 +751,7 @@ function makeStyles(c: ThemeColors) { backgroundColor: c.card, borderRadius: 14, marginHorizontal: 16, marginTop: 16, padding: 20, - shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.07, shadowRadius: 10, elevation: c.isDark ? 0 : 4, borderWidth: c.isDark ? 1 : 0, borderColor: c.border, + shadowColor: c.primary, shadowOpacity: c.isDark ? 0 : 0.08, shadowRadius: 10, elevation: c.isDark ? 0 : 4, borderWidth: c.isDark ? 1 : 0, borderColor: c.glassBorder, minHeight: 160, }, weatherBadge: { diff --git a/src/screens/FlightScreen.tsx b/src/screens/FlightScreen.tsx index cce1020..7853d1a 100644 --- a/src/screens/FlightScreen.tsx +++ b/src/screens/FlightScreen.tsx @@ -741,7 +741,7 @@ function makeStyles(c: ThemeColors) { segBtnActive: { backgroundColor: c.card, borderWidth: 1, borderColor: c.primaryLight }, segBtnText: { fontSize: 12, fontWeight: '500', color: c.textSub }, segBtnTextActive: { color: c.primary, fontWeight: '700' }, - card: { backgroundColor: c.card, borderRadius: 14, marginBottom: 10, overflow: 'hidden', shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.06, shadowRadius: 8, elevation: c.isDark ? 0 : 3, borderWidth: c.isDark ? 1 : 0, borderColor: c.border }, + card: { backgroundColor: c.card, borderRadius: 16, marginBottom: 10, overflow: 'hidden', shadowColor: c.primary, shadowOpacity: c.isDark ? 0 : 0.08, shadowRadius: 10, elevation: c.isDark ? 0 : 3, borderWidth: c.isDark ? 1 : 0, borderColor: c.glassBorder }, cardShift: { borderWidth: 1.5, borderColor: '#F59E0B' }, shiftBanner: { backgroundColor: '#F59E0B', paddingVertical: 5, paddingHorizontal: 12 }, shiftBannerText: { color: '#fff', fontWeight: 'bold', fontSize: 11, letterSpacing: 0.5 }, diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 696690a..7623aec 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -83,7 +83,7 @@ function PinnedFlightCard({ item, colors }: { item: any; colors: any }) { marginHorizontal: 16, marginTop: 16, borderRadius: 16, overflow: 'hidden', backgroundColor: colors.card, - shadowColor: '#000', shadowOpacity: 0.15, shadowRadius: 12, elevation: 6, + shadowColor: colors.primary, shadowOpacity: 0.15, shadowRadius: 12, elevation: 6, borderWidth: colors.isDark ? 1 : 0, borderColor: colors.border, }}> {/* Compact header: airline color bar + flight info */} @@ -111,14 +111,14 @@ function PinnedFlightCard({ item, colors }: { item: any; colors: any }) { {tab === 'departures' ? ( - + CHECK-IN {fmt(ops.checkInOpen)} – {fmt(ops.checkInClose)} - + GATE @@ -433,48 +433,48 @@ function makeStyles(c: ThemeColors) { return StyleSheet.create({ hiddenWV: { height: 1, width: 1, opacity: 0, position: 'absolute', top: -100 }, topRow: { flexDirection: 'row', gap: 12, padding: 16, paddingBottom: 8 }, - weatherCard: { flex: 1, backgroundColor: c.card, borderRadius: 14, padding: 16, alignItems: 'center', shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.06, shadowRadius: 8, elevation: c.isDark ? 0 : 3, borderWidth: c.isDark ? 1 : 0, borderColor: c.border }, + weatherCard: { flex: 1, backgroundColor: c.card, borderRadius: 18, padding: 16, alignItems: 'center', shadowColor: c.primary, shadowOpacity: 0.12, shadowRadius: 12, elevation: 4, borderWidth: 1, borderColor: c.glassBorder }, weatherEmoji: { fontSize: 28, marginBottom: 4 }, - weatherTemp: { fontSize: 28, fontWeight: 'bold', color: c.primaryDark }, + weatherTemp: { fontSize: 28, fontWeight: '700', color: c.primaryDark }, weatherDesc: { fontSize: 11, color: c.textSub, textAlign: 'center', marginTop: 2 }, - dateCard: { width: 90, backgroundColor: c.primaryDark, borderRadius: 14, padding: 14, alignItems: 'center', justifyContent: 'center' }, + dateCard: { width: 90, backgroundColor: c.primaryDark, borderRadius: 18, padding: 14, alignItems: 'center', justifyContent: 'center', shadowColor: c.primary, shadowOpacity: 0.30, shadowRadius: 12, elevation: 6 }, dateToday: { fontSize: 10, color: 'rgba(255,255,255,0.6)', letterSpacing: 1.5, fontWeight: '700' }, - dateNum: { fontSize: 36, fontWeight: 'bold', color: '#fff', lineHeight: 40 }, + dateNum: { fontSize: 36, fontWeight: '700', color: '#fff', lineHeight: 40 }, dateMonth: { fontSize: 12, color: 'rgba(255,255,255,0.7)', marginTop: 2 }, sectionTitle: { fontSize: 13, fontWeight: '700', color: c.textSub, letterSpacing: 0.5, marginHorizontal: 16, marginTop: 16, marginBottom: 8 }, - shiftCard: { backgroundColor: c.card, borderRadius: 14, marginHorizontal: 16, padding: 16, flexDirection: 'row', gap: 14, shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.06, shadowRadius: 8, elevation: c.isDark ? 0 : 3, minHeight: 90, borderWidth: c.isDark ? 1 : 0, borderColor: c.border }, - shiftStrip: { width: 4, borderRadius: 2, backgroundColor: GOLD, marginRight: 2 }, + shiftCard: { backgroundColor: c.card, borderRadius: 18, marginHorizontal: 16, padding: 16, flexDirection: 'row', gap: 14, shadowColor: c.primary, shadowOpacity: 0.10, shadowRadius: 12, elevation: 4, minHeight: 90, borderWidth: 1, borderColor: c.glassBorder }, + shiftStrip: { width: 4, borderRadius: 2, backgroundColor: c.primary, marginRight: 2 }, shiftBadgeRow: { flexDirection: 'row', marginBottom: 8 }, inProgressBadge: { backgroundColor: '#D1FAE5', paddingHorizontal: 10, paddingVertical: 3, borderRadius: 20 }, inProgressText: { fontSize: 10, fontWeight: '700', color: '#059669' }, - shiftTitle: { fontSize: 17, fontWeight: 'bold', color: c.primaryDark, marginBottom: 4 }, - shiftTime: { fontSize: 22, fontWeight: 'bold', color: c.primary, marginBottom: 4 }, - timelineCard: { backgroundColor: c.card, borderRadius: 14, marginHorizontal: 16, marginTop: 12, padding: 16, shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.06, shadowRadius: 8, elevation: c.isDark ? 0 : 3, borderWidth: c.isDark ? 1 : 0, borderColor: c.border }, + shiftTitle: { fontSize: 17, fontWeight: '700', color: c.primaryDark, marginBottom: 4 }, + shiftTime: { fontSize: 22, fontWeight: '700', color: c.primary, marginBottom: 4 }, + timelineCard: { backgroundColor: c.card, borderRadius: 18, marginHorizontal: 16, marginTop: 12, padding: 16, shadowColor: c.primary, shadowOpacity: 0.08, shadowRadius: 10, elevation: 3, borderWidth: 1, borderColor: c.glassBorder }, restRow: { flexDirection: 'row', alignItems: 'center' }, - restText: { fontSize: 18, fontWeight: 'bold', color: '#10b981' }, + restText: { fontSize: 18, fontWeight: '700', color: '#10b981' }, emptyShift: { color: c.textSub, fontSize: 15, lineHeight: 24, textAlign: 'center', flex: 1 }, - uploadToggle: { flexDirection: 'row', alignItems: 'center', gap: 10, marginHorizontal: 16, marginTop: 16, backgroundColor: c.card, borderRadius: 14, paddingHorizontal: 16, paddingVertical: 14, shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.05, shadowRadius: 6, elevation: c.isDark ? 0 : 2, borderWidth: c.isDark ? 1 : 0, borderColor: c.border }, + uploadToggle: { flexDirection: 'row', alignItems: 'center', gap: 10, marginHorizontal: 16, marginTop: 16, backgroundColor: c.card, borderRadius: 18, paddingHorizontal: 16, paddingVertical: 14, shadowColor: c.primary, shadowOpacity: 0.08, shadowRadius: 8, elevation: 3, borderWidth: 1, borderColor: c.glassBorder }, uploadToggleText: { flex: 1, fontSize: 15, fontWeight: '600', color: c.primaryDark }, - uploadSection: { marginHorizontal: 16, backgroundColor: c.card, borderRadius: 14, padding: 16, marginTop: 2, shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.04, shadowRadius: 4, elevation: c.isDark ? 0 : 1, borderWidth: c.isDark ? 1 : 0, borderColor: c.border }, + uploadSection: { marginHorizontal: 16, backgroundColor: c.card, borderRadius: 18, padding: 16, marginTop: 2, shadowColor: c.primary, shadowOpacity: 0.06, shadowRadius: 6, elevation: 2, borderWidth: 1, borderColor: c.glassBorder }, uploadDesc: { fontSize: 13, color: c.textSub, lineHeight: 19, marginBottom: 14 }, scanBtn: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: c.primaryDark, borderRadius: 12, paddingVertical: 13, paddingHorizontal: 20 }, - scanBtnText: { color: '#fff', fontWeight: 'bold', fontSize: 15 }, + scanBtnText: { color: '#fff', fontWeight: '700', fontSize: 15 }, imagesRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 12 }, thumb: { width: '47%', height: 120, borderRadius: 10, resizeMode: 'cover' }, - ocrResult: { backgroundColor: c.cardSecondary, borderRadius: 10, padding: 12, marginTop: 12 }, + ocrResult: { backgroundColor: c.cardSecondary, borderRadius: 12, padding: 12, marginTop: 12 }, ocrTitle: { fontSize: 12, fontWeight: '700', color: c.textSub, marginBottom: 6 }, ocrText: { fontSize: 12, color: c.text, lineHeight: 18 }, syncBtn: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: c.primary, borderRadius: 12, paddingVertical: 13, marginTop: 12 }, - syncBtnText: { color: '#fff', fontWeight: 'bold', fontSize: 15 }, - modalOverlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'center', alignItems: 'center', padding: 20 }, - modalContent: { backgroundColor: c.isDark ? c.bg : c.card, width: '100%', borderRadius: 16, padding: 20, shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.1, shadowRadius: 10, elevation: c.isDark ? 0 : 5, borderWidth: c.isDark ? 1 : 0, borderColor: c.border }, - modalTitle: { fontSize: 17, fontWeight: 'bold', color: c.primaryDark, marginBottom: 14 }, + syncBtnText: { color: '#fff', fontWeight: '700', fontSize: 15 }, + modalOverlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.55)', justifyContent: 'center', alignItems: 'center', padding: 20 }, + modalContent: { backgroundColor: c.isDark ? c.bg : c.card, width: '100%', borderRadius: 20, padding: 20, shadowColor: '#000', shadowOpacity: 0.15, shadowRadius: 14, elevation: 8, borderWidth: 1, borderColor: c.glassBorder }, + modalTitle: { fontSize: 17, fontWeight: '700', color: c.primaryDark, marginBottom: 14 }, modalLabel: { fontSize: 12, fontWeight: '700', color: c.textSub, marginBottom: 8 }, modalInput: { borderWidth: 1, borderColor: c.border, borderRadius: 10, padding: 12, marginBottom: 10, fontSize: 14, color: c.text }, modalBtn: { flex: 1, padding: 14, borderRadius: 10, alignItems: 'center' }, typeBtn: { flex: 1, padding: 12, borderRadius: 10, backgroundColor: c.bg, alignItems: 'center' }, inputLabel: { fontSize: 11, color: c.textSub, fontWeight: '700', marginBottom: 4, letterSpacing: 0.5 }, - modeBtn: { flex: 1, backgroundColor: c.primary, borderRadius: 12, paddingVertical: 20, alignItems: 'center', justifyContent: 'center', gap: 8, shadowColor: '#000', shadowOpacity: 0.05, shadowRadius: 4, elevation: 1 }, + modeBtn: { flex: 1, backgroundColor: c.primary, borderRadius: 14, paddingVertical: 20, alignItems: 'center', justifyContent: 'center', gap: 8, shadowColor: c.primary, shadowOpacity: 0.25, shadowRadius: 8, elevation: 4 }, modeBtnText: { color: '#fff', fontWeight: '700', fontSize: 13 }, }); } diff --git a/src/screens/PasswordScreen.tsx b/src/screens/PasswordScreen.tsx index cef8710..07b470c 100644 --- a/src/screens/PasswordScreen.tsx +++ b/src/screens/PasswordScreen.tsx @@ -366,7 +366,7 @@ function makePinStyles(c: ThemeColors) { function makeRowStyles(c: ThemeColors) { return StyleSheet.create({ - card: { backgroundColor: c.card, borderRadius: 14, padding: 14, marginBottom: 10, flexDirection: 'row', alignItems: 'flex-start', borderWidth: 1, borderColor: c.border, shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.05, shadowRadius: 6, elevation: c.isDark ? 0 : 2 }, + card: { backgroundColor: c.card, borderRadius: 16, padding: 14, marginBottom: 10, flexDirection: 'row', alignItems: 'flex-start', borderWidth: 1, borderColor: c.glassBorder, shadowColor: c.primary, shadowOpacity: c.isDark ? 0 : 0.08, shadowRadius: 8, elevation: c.isDark ? 0 : 3 }, cardLeft:{ flex: 1 }, name: { fontSize: 15, fontWeight: '700', color: c.primaryDark, marginBottom: 2 }, username:{ fontSize: 12, color: c.textSub, marginBottom: 4 }, diff --git a/src/screens/PhonebookScreen.tsx b/src/screens/PhonebookScreen.tsx index 5770032..d941b0d 100644 --- a/src/screens/PhonebookScreen.tsx +++ b/src/screens/PhonebookScreen.tsx @@ -20,7 +20,7 @@ type Contact = { const CATEGORIES = ['Ops', 'Handling', 'Compagnia', 'Aeroporto', 'Hotel', 'Altro']; const CATEGORY_COLORS: Record = { - 'Ops': '#2563EB', + 'Ops': '#F47B16', 'Handling': '#16A34A', 'Compagnia': '#FF6600', 'Aeroporto': '#7C3AED', diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index 80414ea..d431702 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -32,7 +32,7 @@ const THEME_OPTIONS: ThemeOption[] = [ sublabel: 'Tema standard, sfondo bianco', icon: 'light-mode', previewBg: '#F3F4F6', - previewAccent: '#2563EB', + previewAccent: '#F47B16', }, { id: 'dark', @@ -40,7 +40,7 @@ const THEME_OPTIONS: ThemeOption[] = [ sublabel: 'Ideale di notte, riduce affaticamento', icon: 'dark-mode', previewBg: '#0F172A', - previewAccent: '#3B82F6', + previewAccent: '#FF9A42', }, { id: 'weather', @@ -394,8 +394,8 @@ const styles = StyleSheet.create({ // Generic rows card: { borderRadius: 16, marginBottom: 20, - shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.04, shadowRadius: 4, elevation: 2, overflow: 'hidden', + shadowColor: '#F47B16', shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.06, shadowRadius: 6, elevation: 2, overflow: 'hidden', }, divider: { height: 1, marginLeft: 56 }, row: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 13, gap: 12 }, diff --git a/src/screens/ShiftScreen.tsx b/src/screens/ShiftScreen.tsx index 102709b..f7da582 100644 --- a/src/screens/ShiftScreen.tsx +++ b/src/screens/ShiftScreen.tsx @@ -4,8 +4,8 @@ import * as ImagePicker from 'expo-image-picker'; import { WebView } from 'react-native-webview'; import * as Calendar from 'expo-calendar'; -const PRIMARY = '#2563EB'; -const DARK_BLUE = '#1E3A8A'; +const PRIMARY = '#F47B16'; +const DARK_ORANGE = '#C2520A'; const BG = '#F3F4F6'; export default function ShiftScreen() { @@ -296,7 +296,7 @@ const styles = StyleSheet.create({ paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: '#E5E7EB', }, - pageTitle: { fontSize: 24, fontWeight: 'bold', color: DARK_BLUE }, + pageTitle: { fontSize: 24, fontWeight: 'bold', color: DARK_ORANGE }, pageSub: { fontSize: 13, color: '#6B7280', marginTop: 4 }, infoCard: { backgroundColor: '#fff', @@ -309,10 +309,10 @@ const styles = StyleSheet.create({ infoDesc: { fontSize: 13, color: '#6B7280', lineHeight: 20 }, buttonsContainer: { margin: 16, marginBottom: 0 }, button: { - backgroundColor: DARK_BLUE, + backgroundColor: DARK_ORANGE, padding: 16, borderRadius: 14, alignItems: 'center', - shadowColor: DARK_BLUE, shadowOpacity: 0.3, shadowRadius: 8, elevation: 5, + shadowColor: DARK_ORANGE, shadowOpacity: 0.3, shadowRadius: 8, elevation: 5, }, buttonText: { color: '#fff', fontSize: 16, fontWeight: 'bold' }, imagesPreview: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', margin: 16, gap: 10 }, @@ -324,7 +324,7 @@ const styles = StyleSheet.create({ backgroundColor: '#fff', borderRadius: 14, shadowColor: '#000', shadowOpacity: 0.06, shadowRadius: 8, elevation: 3, }, - resultTitle: { fontSize: 15, fontWeight: 'bold', color: DARK_BLUE, marginBottom: 10, borderBottomWidth: 1, borderBottomColor: '#E5E7EB', paddingBottom: 8 }, + resultTitle: { fontSize: 15, fontWeight: 'bold', color: DARK_ORANGE, marginBottom: 10, borderBottomWidth: 1, borderBottomColor: '#E5E7EB', paddingBottom: 8 }, resultText: { fontSize: 13, color: '#374151', lineHeight: 20, marginBottom: 16 }, saveButton: { backgroundColor: PRIMARY,