chore: fix UI

This commit is contained in:
2026-04-26 19:07:26 +02:00
parent a120ad6391
commit d39c49cce6
10 changed files with 172 additions and 147 deletions
@@ -8,6 +8,7 @@ import {
TouchableOpacity,
Linking,
Alert,
useWindowDimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme";
@@ -35,6 +36,7 @@ interface StatCard {
export default function DashboardScreen() {
const { colors } = useTheme();
const { username } = useAuth();
const { width: screenWidth } = useWindowDimensions();
const [stats, setStats] = useState<StatCard[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
@@ -137,54 +139,54 @@ export default function DashboardScreen() {
container: {
flex: 1,
backgroundColor: colors.bgPrimary,
padding: spacing.l,
padding: screenWidth < 380 ? spacing.m : spacing.l,
},
welcome: {
fontSize: fontSize.xl,
fontSize: screenWidth < 380 ? fontSize.lg : fontSize.xl,
fontWeight: "bold",
color: colors.textWhite,
marginBottom: spacing.xs,
},
subtitle: {
fontSize: fontSize.md,
fontSize: screenWidth < 380 ? fontSize.sm : fontSize.md,
color: colors.textSecondary,
marginBottom: spacing.xl,
marginBottom: spacing.l,
},
grid: {
flexDirection: "row",
flexWrap: "wrap",
gap: spacing.m,
gap: screenWidth < 380 ? spacing.s : spacing.m,
},
card: {
width: "47%",
width: screenWidth < 360 ? "100%" : "47%",
backgroundColor: colors.bgCard,
borderRadius: borderRadius.md,
padding: spacing.l,
padding: screenWidth < 380 ? spacing.m : spacing.l,
borderWidth: 1,
borderColor: colors.borderLight,
alignItems: "center",
},
iconCircle: {
width: 48,
height: 48,
borderRadius: 24,
width: screenWidth < 380 ? 40 : 48,
height: screenWidth < 380 ? 40 : 48,
borderRadius: screenWidth < 380 ? 20 : 24,
justifyContent: "center",
alignItems: "center",
marginBottom: spacing.s,
},
cardValue: {
fontSize: fontSize.xxl,
fontSize: screenWidth < 380 ? fontSize.xl : fontSize.xxl,
fontWeight: "bold",
color: colors.textWhite,
},
cardLabel: {
fontSize: fontSize.sm,
fontSize: screenWidth < 380 ? fontSize.xs : fontSize.sm,
color: colors.textSecondary,
marginTop: spacing.xs,
textAlign: "center",
},
}),
[colors],
[colors, screenWidth],
);
if (loading) return <LoadingSpinner message="Chargement..." />;
@@ -14,6 +14,7 @@ import {
TouchableOpacity,
Modal,
StatusBar,
useWindowDimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme";
@@ -34,10 +35,10 @@ import TomTomMap, {
TomTomMarker,
} from "../../components/TomTomMap";
const MAP_HEIGHT = 280;
export default function DeliveryScreen() {
const { colors } = useTheme();
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
const MAP_HEIGHT = screenHeight < 700 ? 200 : screenWidth < 380 ? 220 : 280;
const statusColors = getStatusColors(colors);
const [livreurs, setLivreurs] = useState<DeliveryPerson[]>([]);
const [stats, setStats] = useState({
@@ -185,8 +186,8 @@ export default function DeliveryScreen() {
summaryRow: {
flexDirection: "row",
padding: spacing.l,
gap: spacing.s,
padding: screenWidth < 380 ? spacing.m : spacing.l,
gap: screenWidth < 380 ? spacing.xs : spacing.s,
},
summaryCard: {
flex: 1,
@@ -197,13 +198,14 @@ export default function DeliveryScreen() {
alignItems: "center",
},
summaryValue: {
fontSize: fontSize.xl,
fontSize: screenWidth < 380 ? fontSize.lg : fontSize.xl,
fontWeight: "bold",
color: colors.textWhite,
},
summaryLabel: {
fontSize: fontSize.xs,
fontSize: screenWidth < 380 ? 10 : fontSize.xs,
color: colors.textMuted,
textAlign: "center",
},
mapContainer: {
@@ -311,7 +313,8 @@ export default function DeliveryScreen() {
},
statsRow: {
flexDirection: "row",
gap: spacing.l,
flexWrap: "wrap",
gap: screenWidth < 380 ? spacing.s : spacing.l,
marginTop: spacing.s,
},
stat: {
@@ -425,8 +428,9 @@ export default function DeliveryScreen() {
},
legendRow: {
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "center",
gap: spacing.l,
gap: screenWidth < 380 ? spacing.m : spacing.l,
},
legendItem: {
flexDirection: "row",
@@ -436,7 +440,7 @@ export default function DeliveryScreen() {
legendDot: { width: 10, height: 10, borderRadius: 5 },
legendText: { color: colors.white, fontSize: fontSize.sm },
}),
[colors],
[colors, screenWidth, screenHeight, MAP_HEIGHT],
);
// --------------------------------------------------
@@ -8,6 +8,7 @@ import {
Modal,
StatusBar,
Alert,
useWindowDimensions,
} from "react-native";
import {
useRoute,
@@ -41,10 +42,10 @@ import { useAlert } from "../../hooks/useAlert";
type Route = RouteProp<AdminStackParamList, "OrderDetail">;
const MAP_HEIGHT = 240;
export default function OrderDetailScreen() {
const { colors } = useTheme();
const { height: screenHeight } = useWindowDimensions();
const MAP_HEIGHT = screenHeight < 700 ? 180 : 240;
const route = useRoute<Route>();
const navigation = useNavigation();
const { orderId } = route.params;
@@ -472,7 +473,7 @@ export default function OrderDetailScreen() {
fontWeight: "700",
},
}),
[colors],
[colors, MAP_HEIGHT],
);
if (loading) return <LoadingSpinner message="Chargement..." />;
@@ -9,6 +9,7 @@ import {
ActivityIndicator,
TextInput,
Keyboard,
useWindowDimensions,
} from "react-native";
import { useNavigation } from "@react-navigation/native";
import { Ionicons } from "@expo/vector-icons";
@@ -101,7 +102,7 @@ function DeliveryScheduleSection({
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.s, marginTop: spacing.xs }}>
<Text style={s.thresholdSep}>Ouverture</Text>
<TextInput
style={[s.thresholdInput, { width: 72 }]}
style={[s.thresholdInput, { width: screenWidth < 380 ? 58 : 72 }]}
value={day.open_time}
onChangeText={(v) => updateDay(key, { open_time: v })}
placeholder="09:00"
@@ -112,7 +113,7 @@ function DeliveryScheduleSection({
<Text style={s.thresholdSep}>→</Text>
<Text style={s.thresholdSep}>Fermeture</Text>
<TextInput
style={[s.thresholdInput, { width: 72 }]}
style={[s.thresholdInput, { width: screenWidth < 380 ? 58 : 72 }]}
value={day.close_time}
onChangeText={(v) => updateDay(key, { close_time: v })}
placeholder="20:00"
@@ -240,7 +241,7 @@ function PostalZonesSection({
<View style={{ paddingHorizontal: spacing.l, paddingBottom: spacing.m, gap: spacing.m }}>
{/* Nom */}
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.s }}>
<Text style={[s.thresholdSep, { width: 48 }]}>Nom</Text>
<Text style={[s.thresholdSep, { width: screenWidth < 380 ? 38 : 48 }]}>Nom</Text>
<TextInput
style={[s.thresholdInput, { flex: 1, width: undefined, textAlign: "left", paddingHorizontal: spacing.m }]}
value={zone.name}
@@ -251,9 +252,9 @@ function PostalZonesSection({
</View>
{/* Minimum */}
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.s }}>
<Text style={[s.thresholdSep, { width: 48 }]}>Min €</Text>
<Text style={[s.thresholdSep, { width: screenWidth < 380 ? 38 : 48 }]}>Min €</Text>
<TextInput
style={[s.thresholdInput, { width: 80 }]}
style={[s.thresholdInput, { width: inputMd }]}
value={String(zone.min_amount)}
onChangeText={(v) => {
const n = parseFloat(v);
@@ -332,7 +333,7 @@ function PostalZonesSection({
<View style={{ gap: 4 }}>
<Text style={s.thresholdSep}>Montant minimum de commande (€) *</Text>
<TextInput
style={[s.thresholdInput, { width: 100, textAlign: "left", paddingHorizontal: spacing.m }]}
style={[s.thresholdInput, { width: inputLg, textAlign: "left", paddingHorizontal: spacing.m }]}
value={form.min_amount}
onChangeText={(v) => { setForm((f) => ({ ...f, min_amount: v })); setFormError(""); }}
keyboardType="decimal-pad"
@@ -564,9 +565,9 @@ function TiersSection({
{/* En-tête colonnes */}
<View style={{ flexDirection: "row", paddingHorizontal: spacing.l, paddingBottom: spacing.xs, gap: spacing.xs }}>
<Text style={[s.thresholdSep, { width: 56, textAlign: "center" }]}>Min €</Text>
<Text style={[s.thresholdSep, { width: 56, textAlign: "center" }]}>Max €</Text>
<Text style={[s.thresholdSep, { width: 44, textAlign: "center" }]}>Pts</Text>
<Text style={[s.thresholdSep, { width: inputSm, textAlign: "center" }]}>Min €</Text>
<Text style={[s.thresholdSep, { width: inputSm, textAlign: "center" }]}>Max €</Text>
<Text style={[s.thresholdSep, { width: screenWidth < 380 ? 36 : 44, textAlign: "center" }]}>Pts</Text>
</View>
{tiers.map((tier, i) => (
@@ -583,14 +584,14 @@ function TiersSection({
}}
>
<TextInput
style={[s.thresholdInput, { width: 56 }]}
style={[s.thresholdInput, { width: inputSm }]}
keyboardType="decimal-pad"
value={String(tier.min)}
onChangeText={(v) => updateTier(i, "min", v)}
/>
<Text style={s.thresholdSep}>→</Text>
<TextInput
style={[s.thresholdInput, { width: 56 }]}
style={[s.thresholdInput, { width: inputSm }]}
keyboardType="decimal-pad"
value={String(tier.max)}
onChangeText={(v) => updateTier(i, "max", v)}
@@ -599,7 +600,7 @@ function TiersSection({
/>
<Text style={s.thresholdSep}>=</Text>
<TextInput
style={[s.thresholdInput, { width: 44 }]}
style={[s.thresholdInput, { width: screenWidth < 380 ? 36 : 44 }]}
keyboardType="number-pad"
value={String(tier.points)}
onChangeText={(v) => updateTier(i, "points", v)}
@@ -640,6 +641,10 @@ export default function SettingsScreen() {
const { colors } = useTheme();
const { alert, showError, showSuccess, hideAlert } = useAlert();
const navigation = useNavigation();
const { width: screenWidth } = useWindowDimensions();
const inputSm = screenWidth < 380 ? 46 : 56;
const inputMd = screenWidth < 380 ? 64 : 80;
const inputLg = screenWidth < 380 ? 80 : 100;
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
@@ -811,7 +816,7 @@ export default function SettingsScreen() {
const s = StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bgPrimary },
content: { padding: spacing.l, paddingBottom: spacing.xl * 2 },
content: { padding: screenWidth < 380 ? spacing.m : spacing.l, paddingBottom: spacing.xl * 2 },
section: {
backgroundColor: colors.bgSecondary,
borderRadius: borderRadius.lg,
@@ -988,7 +993,7 @@ export default function SettingsScreen() {
</View>
<View style={{ flexDirection: "row", width: "100%", marginBottom: 2 }}>
<Text style={[s.rowDesc, { flex: 1 }]}>À partir de (annul.)</Text>
<Text style={[s.rowDesc, { width: 80, textAlign: "center" }]}>Montant</Text>
<Text style={[s.rowDesc, { width: inputMd, textAlign: "center" }]}>Montant</Text>
<View style={{ width: 28 }} />
</View>
{settings.penalty_tiers.map((tier, i) => (
@@ -1008,7 +1013,7 @@ export default function SettingsScreen() {
placeholderTextColor={colors.textMuted}
/>
<TextInput
style={[s.thresholdInput, { width: 80, textAlign: "center" }]}
style={[s.thresholdInput, { width: inputMd, textAlign: "center" }]}
value={String(tier.amount)}
onChangeText={(v) => {
const n = parseInt(v, 10);
@@ -9,6 +9,7 @@ import {
TouchableOpacity,
TextInput as RNTextInput,
ActivityIndicator,
useWindowDimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme";
@@ -58,6 +59,7 @@ type RoleFilter = "all" | "client" | "livreur" | "cabine" | "admin";
// ==================================================
export default function UsersScreen() {
const { colors } = useTheme();
const { width: screenWidth } = useWindowDimensions();
const ROLE_TABS: {
key: RoleFilter;
@@ -487,6 +489,8 @@ export default function UsersScreen() {
setAmendeReason("");
if (res.compensated) {
showSuccess("Compensation", "Amende annulée — solde parrainage débité en compensation");
} else {
showSuccess("Validé", `Amende de ${pts} € appliquée à "${sanctionModal.client!.username}"`);
}
await loadData();
},
@@ -501,6 +505,7 @@ export default function UsersScreen() {
async () => {
const res = await resetClientPenalties(sanctionModal.client!.username, false);
if (!res.success) { showError("Erreur", res.error || "Echec"); return; }
showSuccess("Validé", `Amende de "${sanctionModal.client!.username}" remise à zéro`);
await loadData();
},
"Remettre à zéro",
@@ -523,6 +528,7 @@ export default function UsersScreen() {
setPointsLoading((prev) => ({ ...prev, [poolKey]: false }));
if (!res.success) { showError("Erreur", res.error || "Echec"); return; }
setPointsInputs((prev) => ({ ...prev, [poolKey]: "" }));
showSuccess("Validé", `${pts} points "${poolName}" ajoutés à "${sanctionModal.client!.username}"`);
await loadData();
},
);
@@ -544,6 +550,7 @@ export default function UsersScreen() {
setSubtractLoading((prev) => ({ ...prev, [poolKey]: false }));
if (!res.success) { showError("Erreur", res.error || "Echec"); return; }
setSubtractInputs((prev) => ({ ...prev, [poolKey]: "" }));
showSuccess("Validé", `${pts} points "${poolName}" retirés à "${sanctionModal.client!.username}"`);
await loadData();
},
);
@@ -557,6 +564,7 @@ export default function UsersScreen() {
async () => {
const res = await resetClientPoints(sanctionModal.client!.username, poolIdx);
if (!res.success) { showError("Erreur", res.error || "Echec"); return; }
showSuccess("Validé", `Points "${poolName}" de "${sanctionModal.client!.username}" remis à zéro`);
await loadData();
},
"Remettre à zéro",
@@ -675,36 +683,38 @@ export default function UsersScreen() {
// Filter grid
filterGrid: {
flexDirection: "row",
paddingHorizontal: spacing.m,
paddingVertical: spacing.m,
gap: spacing.s,
paddingHorizontal: screenWidth < 380 ? spacing.s : spacing.m,
paddingVertical: spacing.s,
gap: screenWidth < 380 ? spacing.xs : spacing.s,
},
filterCard: {
flex: 1,
alignItems: "center",
paddingVertical: spacing.m,
paddingVertical: spacing.s,
paddingHorizontal: 2,
borderRadius: borderRadius.md,
borderWidth: 1,
borderColor: colors.border,
backgroundColor: colors.bgSecondary,
},
filterIconCircle: {
width: 36,
height: 36,
borderRadius: 18,
width: screenWidth < 380 ? 28 : 36,
height: screenWidth < 380 ? 28 : 36,
borderRadius: screenWidth < 380 ? 14 : 18,
justifyContent: "center",
alignItems: "center",
marginBottom: 4,
marginBottom: 2,
},
filterCount: {
color: colors.textWhite,
fontSize: fontSize.lg,
fontSize: screenWidth < 380 ? fontSize.sm : fontSize.md,
fontWeight: "700",
},
filterLabel: {
color: colors.textMuted,
fontSize: fontSize.sm,
fontSize: screenWidth < 380 ? 10 : fontSize.xs,
fontWeight: "600",
textAlign: "center",
},
// Card
@@ -754,17 +764,23 @@ export default function UsersScreen() {
},
statsRow: {
flexDirection: "row",
marginTop: spacing.m,
gap: spacing.l,
flexWrap: "wrap",
marginTop: spacing.s,
gap: spacing.s,
marginLeft: 52,
},
stat: { alignItems: "center" },
stat: {
alignItems: "center",
minWidth: 52,
paddingVertical: spacing.xs,
},
statValue: {
fontSize: fontSize.md,
fontSize: fontSize.sm,
fontWeight: "bold",
color: colors.textWhite,
textAlign: "center",
},
statLabel: { fontSize: fontSize.xs, color: colors.textMuted },
statLabel: { fontSize: fontSize.xs, color: colors.textMuted, textAlign: "center" },
// Non-client
nonClientInfo: {
@@ -846,7 +862,7 @@ export default function UsersScreen() {
fontWeight: "600",
},
}),
[colors],
[colors, screenWidth],
);
// --------------------------------------------------
@@ -991,10 +1007,10 @@ export default function UsersScreen() {
</View>
<View style={styles.statsRow}>
<View style={styles.stat}>
<Text style={styles.statValue}>
<Text style={styles.statValue} numberOfLines={1}>
{item.clientData.command}
</Text>
<Text style={styles.statLabel}>Cmd</Text>
<Text style={styles.statLabel} numberOfLines={1}>Cmd</Text>
</View>
{pointsEnabled && poolNames.map((name, i) => {
const key = poolKeys[i] ?? "";
@@ -1002,46 +1018,40 @@ export default function UsersScreen() {
const color = i === 0 ? colors.success : i === 1 ? colors.info : colors.warning;
return (
<View key={i} style={styles.stat}>
<Text style={[styles.statValue, { color }]}>{value}</Text>
<Text style={styles.statLabel}>{name}</Text>
<Text style={[styles.statValue, { color }]} numberOfLines={1}>{value}</Text>
<Text style={styles.statLabel} numberOfLines={1}>{name}</Text>
</View>
);
})}
{amendeEnabled && (
<View style={styles.stat}>
<Text
style={[
styles.statValue,
{ color: colors.warning },
]}
style={[styles.statValue, { color: colors.warning }]}
numberOfLines={1}
>
{item.clientData.amende}
{item.clientData.amende}
</Text>
<Text style={styles.statLabel}>Amende</Text>
<Text style={styles.statLabel} numberOfLines={1}>Amende</Text>
</View>
)}
<View style={styles.stat}>
<Text
style={[
styles.statValue,
{ color: colors.danger },
]}
style={[styles.statValue, { color: colors.danger }]}
numberOfLines={1}
>
{item.clientData.cancellations_count}
</Text>
<Text style={styles.statLabel}>Annul.</Text>
<Text style={styles.statLabel} numberOfLines={1}>Annul.</Text>
</View>
{referralEnabled && (item.clientData.referral_balance ?? 0) > 0 && (
<View style={styles.stat}>
<Text
style={[
styles.statValue,
{ color: colors.success },
]}
style={[styles.statValue, { color: colors.success }]}
numberOfLines={1}
>
{(item.clientData.referral_balance ?? 0).toFixed(0)}
</Text>
<Text style={styles.statLabel}>Parrain</Text>
<Text style={styles.statLabel} numberOfLines={1}>Parrain</Text>
</View>
)}
</View>
@@ -8,7 +8,7 @@ import {
TouchableOpacity,
TextInput as RNTextInput,
Animated,
Dimensions,
useWindowDimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useNavigation } from "@react-navigation/native";
@@ -19,10 +19,9 @@ import { loginAdmin } from "../../api/api_admin";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
const { width } = Dimensions.get("window");
export default function AdminLoginScreen() {
const { colors } = useTheme();
const { width: screenWidth } = useWindowDimensions();
const ACCENT = colors.accent;
const [username, setUsername] = useState("");
@@ -41,36 +40,36 @@ export default function AdminLoginScreen() {
accentBar: { height: 4, width: "100%" },
heroSection: {
alignItems: "center",
paddingTop: 60,
paddingBottom: spacing.xl,
paddingTop: screenWidth < 380 ? 36 : 60,
paddingBottom: spacing.l,
},
heroBg: {
width: 120,
height: 120,
borderRadius: 60,
width: screenWidth < 380 ? 96 : 120,
height: screenWidth < 380 ? 96 : 120,
borderRadius: screenWidth < 380 ? 48 : 60,
justifyContent: "center",
alignItems: "center",
},
heroInner: {
width: 88,
height: 88,
borderRadius: 44,
width: screenWidth < 380 ? 68 : 88,
height: screenWidth < 380 ? 68 : 88,
borderRadius: screenWidth < 380 ? 34 : 44,
justifyContent: "center",
alignItems: "center",
},
heroTitle: {
fontSize: fontSize.xxl,
fontSize: screenWidth < 380 ? fontSize.xl : fontSize.xxl,
fontWeight: "800",
color: colors.textWhite,
marginTop: spacing.l,
marginTop: spacing.m,
letterSpacing: 0.5,
},
heroSubtitle: {
fontSize: fontSize.md,
fontSize: screenWidth < 380 ? fontSize.sm : fontSize.md,
color: colors.textMuted,
marginTop: spacing.xs,
},
formSection: { paddingHorizontal: spacing.xl, flex: 1 },
formSection: { paddingHorizontal: screenWidth < 380 ? spacing.l : spacing.xl, flex: 1 },
inputContainer: {
flexDirection: "row",
alignItems: "center",
@@ -121,7 +120,7 @@ export default function AdminLoginScreen() {
marginLeft: spacing.xs,
},
}),
[colors],
[colors, screenWidth],
);
const handleLogin = async () => {
@@ -8,7 +8,7 @@ import {
TouchableOpacity,
TextInput as RNTextInput,
Animated,
Dimensions,
useWindowDimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useNavigation } from "@react-navigation/native";
@@ -19,10 +19,9 @@ import { loginAdmin } from "../../api/api_admin";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
const { width } = Dimensions.get("window");
export default function CabineLoginScreen() {
const { colors } = useTheme();
const { width: screenWidth } = useWindowDimensions();
const ACCENT = colors.info;
const [username, setUsername] = useState("");
@@ -41,36 +40,36 @@ export default function CabineLoginScreen() {
accentBar: { height: 4, width: "100%" },
heroSection: {
alignItems: "center",
paddingTop: 60,
paddingBottom: spacing.xl,
paddingTop: screenWidth < 380 ? 36 : 60,
paddingBottom: spacing.l,
},
heroBg: {
width: 120,
height: 120,
borderRadius: 60,
width: screenWidth < 380 ? 96 : 120,
height: screenWidth < 380 ? 96 : 120,
borderRadius: screenWidth < 380 ? 48 : 60,
justifyContent: "center",
alignItems: "center",
},
heroInner: {
width: 88,
height: 88,
borderRadius: 44,
width: screenWidth < 380 ? 68 : 88,
height: screenWidth < 380 ? 68 : 88,
borderRadius: screenWidth < 380 ? 34 : 44,
justifyContent: "center",
alignItems: "center",
},
heroTitle: {
fontSize: fontSize.xxl,
fontSize: screenWidth < 380 ? fontSize.xl : fontSize.xxl,
fontWeight: "800",
color: colors.textWhite,
marginTop: spacing.l,
marginTop: spacing.m,
letterSpacing: 0.5,
},
heroSubtitle: {
fontSize: fontSize.md,
fontSize: screenWidth < 380 ? fontSize.sm : fontSize.md,
color: colors.textMuted,
marginTop: spacing.xs,
},
formSection: { paddingHorizontal: spacing.xl, flex: 1 },
formSection: { paddingHorizontal: screenWidth < 380 ? spacing.l : spacing.xl, flex: 1 },
inputContainer: {
flexDirection: "row",
alignItems: "center",
@@ -121,7 +120,7 @@ export default function CabineLoginScreen() {
marginLeft: spacing.xs,
},
}),
[colors],
[colors, screenWidth],
);
const handleLogin = async () => {
@@ -8,7 +8,7 @@ import {
TouchableOpacity,
TextInput as RNTextInput,
Animated,
Dimensions,
useWindowDimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useNavigation } from "@react-navigation/native";
@@ -19,10 +19,9 @@ import { loginAdmin } from "../../api/api_admin";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
const { width } = Dimensions.get("window");
export default function DeliveryLoginScreen() {
const { colors } = useTheme();
const { width: screenWidth } = useWindowDimensions();
const ACCENT = colors.success;
const [username, setUsername] = useState("");
@@ -41,36 +40,36 @@ export default function DeliveryLoginScreen() {
accentBar: { height: 4, width: "100%" },
heroSection: {
alignItems: "center",
paddingTop: 60,
paddingBottom: spacing.xl,
paddingTop: screenWidth < 380 ? 36 : 60,
paddingBottom: spacing.l,
},
heroBg: {
width: 120,
height: 120,
borderRadius: 60,
width: screenWidth < 380 ? 96 : 120,
height: screenWidth < 380 ? 96 : 120,
borderRadius: screenWidth < 380 ? 48 : 60,
justifyContent: "center",
alignItems: "center",
},
heroInner: {
width: 88,
height: 88,
borderRadius: 44,
width: screenWidth < 380 ? 68 : 88,
height: screenWidth < 380 ? 68 : 88,
borderRadius: screenWidth < 380 ? 34 : 44,
justifyContent: "center",
alignItems: "center",
},
heroTitle: {
fontSize: fontSize.xxl,
fontSize: screenWidth < 380 ? fontSize.xl : fontSize.xxl,
fontWeight: "800",
color: colors.textWhite,
marginTop: spacing.l,
marginTop: spacing.m,
letterSpacing: 0.5,
},
heroSubtitle: {
fontSize: fontSize.md,
fontSize: screenWidth < 380 ? fontSize.sm : fontSize.md,
color: colors.textMuted,
marginTop: spacing.xs,
},
formSection: { paddingHorizontal: spacing.xl, flex: 1 },
formSection: { paddingHorizontal: screenWidth < 380 ? spacing.l : spacing.xl, flex: 1 },
inputContainer: {
flexDirection: "row",
alignItems: "center",
@@ -121,7 +120,7 @@ export default function DeliveryLoginScreen() {
marginLeft: spacing.xs,
},
}),
[colors],
[colors, screenWidth],
);
const handleLogin = async () => {
@@ -8,6 +8,7 @@ import {
TouchableOpacity,
Linking,
Alert,
useWindowDimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme";
@@ -26,6 +27,7 @@ import LoadingSpinner from "../../components/ui/LoadingSpinner";
export default function DashboardScreen() {
const { colors } = useTheme();
const { username } = useAuth();
const { width: screenWidth } = useWindowDimensions();
const [tgLinked, setTgLinked] = useState(false);
const [tgEnabled, setTgEnabled] = useState(false);
@@ -133,54 +135,54 @@ export default function DashboardScreen() {
container: {
flex: 1,
backgroundColor: colors.bgPrimary,
padding: spacing.l,
padding: screenWidth < 380 ? spacing.m : spacing.l,
},
welcome: {
fontSize: fontSize.xl,
fontSize: screenWidth < 380 ? fontSize.lg : fontSize.xl,
fontWeight: "bold",
color: colors.textWhite,
marginBottom: spacing.xs,
},
subtitle: {
fontSize: fontSize.md,
fontSize: screenWidth < 380 ? fontSize.sm : fontSize.md,
color: colors.textSecondary,
marginBottom: spacing.xl,
marginBottom: spacing.l,
},
grid: {
flexDirection: "row",
flexWrap: "wrap",
gap: spacing.m,
gap: screenWidth < 380 ? spacing.s : spacing.m,
},
card: {
width: "47%",
width: screenWidth < 360 ? "100%" : "47%",
backgroundColor: colors.bgCard,
borderRadius: borderRadius.md,
padding: spacing.l,
padding: screenWidth < 380 ? spacing.m : spacing.l,
borderWidth: 1,
borderColor: colors.borderLight,
alignItems: "center",
},
iconCircle: {
width: 48,
height: 48,
borderRadius: 24,
width: screenWidth < 380 ? 40 : 48,
height: screenWidth < 380 ? 40 : 48,
borderRadius: screenWidth < 380 ? 20 : 24,
justifyContent: "center",
alignItems: "center",
marginBottom: spacing.s,
},
cardValue: {
fontSize: fontSize.xxl,
fontSize: screenWidth < 380 ? fontSize.xl : fontSize.xxl,
fontWeight: "bold",
color: colors.textWhite,
},
cardLabel: {
fontSize: fontSize.sm,
fontSize: screenWidth < 380 ? fontSize.xs : fontSize.sm,
color: colors.textSecondary,
marginTop: spacing.xs,
textAlign: "center",
},
}),
[colors],
[colors, screenWidth],
);
if (loading) return <LoadingSpinner message="Chargement..." />;
@@ -14,12 +14,12 @@ import {
RefreshControl,
TouchableOpacity,
AppState,
Dimensions,
Linking,
Modal,
StatusBar,
ScrollView,
TextInput,
useWindowDimensions,
} from "react-native";
import Geolocation from "@react-native-community/geolocation";
import { Ionicons } from "@expo/vector-icons";
@@ -55,8 +55,6 @@ import TomTomMap, {
} from "../../components/TomTomMap";
import DetailsModal from "../../components/ui/Modal";
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const MAP_HEIGHT = 260;
const LOCATION_INTERVAL_MS = 15000;
const STATUS_LABELS: Record<string, string> = {
@@ -79,6 +77,8 @@ interface EnrichedDelivery extends DeliveryItem {
export default function DashboardScreen() {
const { colors } = useTheme();
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
const MAP_HEIGHT = screenHeight < 700 ? 180 : screenWidth < 380 ? 200 : 260;
const [status, setStatus] = useState<DeliveryStatus | null>(null);
const [deliveries, setDeliveries] = useState<EnrichedDelivery[]>([]);
const [queue, setQueue] = useState<QueueInfo | null>(null);
@@ -1067,17 +1067,19 @@ export default function DashboardScreen() {
gap: spacing.m,
},
instructionIconBox: {
width: 42,
height: 42,
borderRadius: 12,
width: screenWidth < 380 ? 34 : 42,
height: screenWidth < 380 ? 34 : 42,
borderRadius: 10,
backgroundColor: colors.accent,
justifyContent: "center",
alignItems: "center",
flexShrink: 0,
},
instructionText: {
color: colors.textWhite,
fontSize: fontSize.md,
fontSize: screenWidth < 380 ? fontSize.sm : fontSize.md,
fontWeight: "600",
flex: 1,
},
instructionStreet: {
color: colors.textMuted,
@@ -1259,7 +1261,7 @@ export default function DashboardScreen() {
address: {
flex: 1,
color: colors.textSecondary,
fontSize: fontSize.sm,
fontSize: screenWidth < 380 ? fontSize.xs : fontSize.sm,
},
itemsSection: {
@@ -1285,10 +1287,12 @@ export default function DashboardScreen() {
paddingVertical: spacing.xs,
borderBottomWidth: 1,
borderBottomColor: colors.borderSubtle,
flexWrap: "nowrap",
},
itemName: {
flex: 1,
color: colors.textWhite,
fontSize: fontSize.sm,
fontSize: screenWidth < 380 ? fontSize.xs : fontSize.sm,
fontWeight: "600",
},
itemQty: {
@@ -1547,7 +1551,7 @@ export default function DashboardScreen() {
fontSize: fontSize.md,
},
}),
[colors],
[colors, screenWidth, screenHeight, MAP_HEIGHT],
);
if (loading) return <LoadingSpinner message="Chargement..." />;