chore: fix UI
This commit is contained in:
@@ -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..." />;
|
||||
|
||||
Reference in New Issue
Block a user