chore: add select
This commit is contained in:
@@ -28,7 +28,6 @@ import type { AdminStackParamList } from "../../navigation/types";
|
||||
import StatusBadge from "../../components/StatusBadge";
|
||||
import LoadingSpinner from "../../components/ui/LoadingSpinner";
|
||||
import Modal from "../../components/ui/Modal";
|
||||
import Button from "../../components/ui/Button";
|
||||
import Card from "../../components/ui/Card";
|
||||
import AlertModal from "../../components/ui/AlertModal";
|
||||
import { useAlert } from "../../hooks/useAlert";
|
||||
@@ -55,6 +54,7 @@ export default function OrdersScreen() {
|
||||
const [filter, setFilter] = useState("all");
|
||||
const { alert, showError, showSuccess, showConfirm, hideAlert } = useAlert();
|
||||
|
||||
const [openMenuId, setOpenMenuId] = useState<number | null>(null);
|
||||
const [assignModal, setAssignModal] = useState<{
|
||||
visible: boolean;
|
||||
commandId: number | null;
|
||||
@@ -243,11 +243,47 @@ export default function OrdersScreen() {
|
||||
alignItems: "center",
|
||||
marginBottom: spacing.s,
|
||||
},
|
||||
actions: {
|
||||
selectBtn: {
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
gap: spacing.s,
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
marginTop: spacing.m,
|
||||
paddingHorizontal: spacing.m,
|
||||
paddingVertical: spacing.s,
|
||||
backgroundColor: colors.bgPrimary,
|
||||
borderRadius: borderRadius.sm,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
selectBtnText: {
|
||||
color: colors.textSecondary,
|
||||
fontSize: fontSize.sm,
|
||||
},
|
||||
dropdown: {
|
||||
marginTop: 2,
|
||||
backgroundColor: colors.bgCard,
|
||||
borderRadius: borderRadius.sm,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
overflow: "hidden",
|
||||
},
|
||||
dropdownItem: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: spacing.s,
|
||||
paddingHorizontal: spacing.m,
|
||||
paddingVertical: spacing.m,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.border,
|
||||
},
|
||||
dropdownItemLast: { borderBottomWidth: 0 },
|
||||
dropdownText: {
|
||||
color: colors.textWhite,
|
||||
fontSize: fontSize.sm,
|
||||
},
|
||||
dropdownTextDanger: {
|
||||
color: colors.error,
|
||||
fontSize: fontSize.sm,
|
||||
},
|
||||
empty: {
|
||||
color: colors.textMuted,
|
||||
@@ -313,80 +349,117 @@ export default function OrdersScreen() {
|
||||
[colors],
|
||||
);
|
||||
|
||||
const renderOrder = ({ item }: { item: CommandResponse }) => (
|
||||
<Card style={{ marginBottom: spacing.m }}>
|
||||
<TouchableOpacity
|
||||
onPress={() =>
|
||||
navigation.navigate("OrderDetail", { orderId: item.id })
|
||||
}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.orderId}>#{item.id}</Text>
|
||||
<StatusBadge status={item.status} />
|
||||
</View>
|
||||
<Text style={styles.cardText}>Client: {item.username}</Text>
|
||||
<Text style={styles.cardText}>Adresse: {item.adresse}</Text>
|
||||
<Text style={styles.cardText}>
|
||||
Total: {item.total_prix.toFixed(2)} €
|
||||
</Text>
|
||||
{item.livreur_assign && (
|
||||
const renderOrder = ({ item }: { item: CommandResponse }) => {
|
||||
const isOpen = openMenuId === item.id;
|
||||
const isDone = ["approved", "cancelled"].includes(item.status);
|
||||
|
||||
type Action = {
|
||||
label: string;
|
||||
icon: keyof typeof Ionicons.glyphMap;
|
||||
onPress: () => void;
|
||||
danger?: boolean;
|
||||
condition?: boolean;
|
||||
};
|
||||
|
||||
const actions: Action[] = [
|
||||
{
|
||||
label: "Voir items",
|
||||
icon: "receipt-outline",
|
||||
onPress: () => { setOpenMenuId(null); openItems(item.id); },
|
||||
},
|
||||
{
|
||||
label: "Le client est là",
|
||||
icon: "notifications-outline",
|
||||
onPress: () => { setOpenMenuId(null); handleNotifyClient(item.id); },
|
||||
condition: !isDone,
|
||||
},
|
||||
{
|
||||
label: "Assigner livreur",
|
||||
icon: "bicycle-outline",
|
||||
onPress: () => { setOpenMenuId(null); openAssignModal(item.id); },
|
||||
condition: !isDone,
|
||||
},
|
||||
{
|
||||
label: "Passer en route",
|
||||
icon: "car-outline",
|
||||
onPress: () => { setOpenMenuId(null); handleStatusUpdate(item.id, "en_route"); },
|
||||
condition: item.status === "assigned",
|
||||
},
|
||||
{
|
||||
label: "Confirmer réception",
|
||||
icon: "checkmark-circle-outline",
|
||||
onPress: () => { setOpenMenuId(null); handleConfirmReception(item.id); },
|
||||
condition: item.status === "livre",
|
||||
},
|
||||
{
|
||||
label: "Supprimer",
|
||||
icon: "trash-outline",
|
||||
onPress: () => { setOpenMenuId(null); handleDelete(item.id); },
|
||||
danger: true,
|
||||
},
|
||||
].filter((a) => a.condition !== false);
|
||||
|
||||
return (
|
||||
<Card style={{ marginBottom: spacing.m }}>
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.navigate("OrderDetail", { orderId: item.id })}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.orderId}>#{item.id}</Text>
|
||||
<StatusBadge status={item.status} />
|
||||
</View>
|
||||
<Text style={styles.cardText}>Client: {item.username}</Text>
|
||||
<Text style={styles.cardText}>Adresse: {item.adresse}</Text>
|
||||
<Text style={styles.cardText}>
|
||||
Livreur: {item.livreur_assign}
|
||||
Total: {item.total_prix.toFixed(2)} €
|
||||
</Text>
|
||||
)}
|
||||
<Text style={styles.cardDate}>
|
||||
{new Date(item.created_at).toLocaleString("fr-FR")}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.actions}>
|
||||
<Button
|
||||
title="Voir items"
|
||||
onPress={() => openItems(item.id)}
|
||||
size="sm"
|
||||
variant="primary"
|
||||
/>
|
||||
{!["approved", "cancelled"].includes(item.status) && (
|
||||
<Button
|
||||
title="Le client est là"
|
||||
onPress={() => handleNotifyClient(item.id)}
|
||||
size="sm"
|
||||
variant="warning"
|
||||
{item.livreur_assign && (
|
||||
<Text style={styles.cardText}>Livreur: {item.livreur_assign}</Text>
|
||||
)}
|
||||
<Text style={styles.cardDate}>
|
||||
{new Date(item.created_at).toLocaleString("fr-FR")}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.selectBtn}
|
||||
onPress={() => setOpenMenuId(isOpen ? null : item.id)}
|
||||
>
|
||||
<Text style={styles.selectBtnText}>Actions</Text>
|
||||
<Ionicons
|
||||
name={isOpen ? "chevron-up" : "chevron-down"}
|
||||
size={14}
|
||||
color={colors.textSecondary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{isOpen && (
|
||||
<View style={styles.dropdown}>
|
||||
{actions.map((action, index) => (
|
||||
<TouchableOpacity
|
||||
key={action.label}
|
||||
style={[
|
||||
styles.dropdownItem,
|
||||
index === actions.length - 1 && styles.dropdownItemLast,
|
||||
]}
|
||||
onPress={action.onPress}
|
||||
>
|
||||
<Ionicons
|
||||
name={action.icon}
|
||||
size={16}
|
||||
color={action.danger ? colors.error : colors.accent}
|
||||
/>
|
||||
<Text style={action.danger ? styles.dropdownTextDanger : styles.dropdownText}>
|
||||
{action.label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
{!["approved", "cancelled"].includes(item.status) && (
|
||||
<Button
|
||||
title="Assigner"
|
||||
onPress={() => openAssignModal(item.id)}
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
/>
|
||||
)}
|
||||
{item.status === "assigned" && (
|
||||
<Button
|
||||
title="En route"
|
||||
onPress={() => handleStatusUpdate(item.id, "en_route")}
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
/>
|
||||
)}
|
||||
{item.status === "livre" && (
|
||||
<Button
|
||||
title="Confirmer"
|
||||
onPress={() => handleConfirmReception(item.id)}
|
||||
size="sm"
|
||||
variant="success"
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
title="Supprimer"
|
||||
onPress={() => handleDelete(item.id)}
|
||||
size="sm"
|
||||
variant="danger"
|
||||
/>
|
||||
</View>
|
||||
</Card>
|
||||
);
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
if (loading) return <LoadingSpinner message="Chargement commandes..." />;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user