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