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,