chore: add new features cabine & admin

This commit is contained in:
2026-03-06 11:47:19 +01:00
parent 0dff4ea4f0
commit 7c739f0a64
5 changed files with 112 additions and 3 deletions
+15 -1
View File
@@ -119,7 +119,7 @@ export const getCommandByID = async (commandId: number) => {
export const getCommandItems = async (commandId: number) => { export const getCommandItems = async (commandId: number) => {
const { data } = await apiClient.get( const { data } = await apiClient.get(
`${CABINE_URL}/commands/${commandId}/items`, `${V2}/admin/protected/orders/${commandId}/items`,
); );
return { return {
success: true, success: true,
@@ -130,6 +130,13 @@ export const getCommandItems = async (commandId: number) => {
}; };
}; };
export const deleteCommandItem = async (commandId: number, itemId: number) => {
const { data } = await apiClient.delete(
`${V2}/admin/protected/orders/${commandId}/items/${itemId}`,
);
return { success: true, message: data.message };
};
export const updateCommandStatus = async ( export const updateCommandStatus = async (
commandId: number, commandId: number,
newStatus: string, newStatus: string,
@@ -159,6 +166,13 @@ export const validateCommand = async (commandId: number) => {
return { success: true, message: data.message }; return { success: true, message: data.message };
}; };
export const notifyClientToDescend = async (commandId: number) => {
const { data } = await apiClient.post(
`${V2}/admin/protected/orders/${commandId}/notify-client`,
);
return { success: true, message: data.message, client_username: data.client_username };
};
export const confirmReceptionAdmin = async (commandId: number) => { export const confirmReceptionAdmin = async (commandId: number) => {
const { data } = await apiClient.post( const { data } = await apiClient.post(
`${V2}/admin/protected/orders/${commandId}/confirm-reception`, `${V2}/admin/protected/orders/${commandId}/confirm-reception`,
+7
View File
@@ -117,6 +117,13 @@ export const deleteCommand = async (commandId: number) => {
return { success: true, message: data.message }; return { success: true, message: data.message };
}; };
export const notifyClientToDescendCabine = async (commandId: number) => {
const { data } = await apiClient.post(
`${API}/commands/${commandId}/notify-client`,
);
return { success: true, message: data.message, client_username: data.client_username };
};
export const getCabineLivreursList = async (): Promise< export const getCabineLivreursList = async (): Promise<
{ id: number; username: string }[] { id: number; username: string }[]
> => { > => {
@@ -18,6 +18,7 @@ interface ButtonProps {
| "secondary" | "secondary"
| "danger" | "danger"
| "success" | "success"
| "warning"
| "outline" | "outline"
| "ghost"; | "ghost";
size?: "sm" | "md" | "lg"; size?: "sm" | "md" | "lg";
@@ -46,6 +47,7 @@ export default function Button({
secondary: colors.bgCard, secondary: colors.bgCard,
danger: colors.danger, danger: colors.danger,
success: colors.success, success: colors.success,
warning: colors.warning,
outline: "transparent", outline: "transparent",
ghost: "transparent", ghost: "transparent",
}[variant]; }[variant];
@@ -7,6 +7,7 @@ import {
TouchableOpacity, TouchableOpacity,
Modal, Modal,
StatusBar, StatusBar,
Alert,
} from "react-native"; } from "react-native";
import { useRoute, type RouteProp } from "@react-navigation/native"; import { useRoute, type RouteProp } from "@react-navigation/native";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
@@ -19,7 +20,9 @@ import {
updateCommandStatus, updateCommandStatus,
validateCommand, validateCommand,
confirmReceptionAdmin, confirmReceptionAdmin,
notifyClientToDescend,
getDeliveryPersonDetails, getDeliveryPersonDetails,
deleteCommandItem,
} from "../../api/api_admin"; } from "../../api/api_admin";
import { geocodeAddress, calculateRoute } from "../../api/tomtom"; import { geocodeAddress, calculateRoute } from "../../api/tomtom";
import type { RouteInfo, LatLng } from "../../api/tomtom"; import type { RouteInfo, LatLng } from "../../api/tomtom";
@@ -167,6 +170,40 @@ export default function OrderDetailScreen() {
} }
}; };
const handleNotifyClient = async () => {
try {
await notifyClientToDescend(orderId);
showSuccess("Notification envoyée", "Le client a été prévenu de descendre");
} catch (e: any) {
showError("Erreur", e.message);
}
};
const handleDeleteItem = (itemId: number, itemName: string) => {
Alert.alert(
"Supprimer l'article",
`Supprimer "${itemName}" de la commande ?`,
[
{ text: "Annuler", style: "cancel" },
{
text: "Supprimer",
style: "destructive",
onPress: async () => {
try {
await deleteCommandItem(orderId, itemId);
const itemsRes = await getCommandItems(orderId);
setItems(itemsRes.items);
const cmdRes = await getCommandByID(orderId);
setCommand(cmdRes.command);
} catch (e: any) {
showError("Erreur", e.message);
}
},
},
],
);
};
const handleConfirmReception = async () => { const handleConfirmReception = async () => {
try { try {
const res = await confirmReceptionAdmin(orderId); const res = await confirmReceptionAdmin(orderId);
@@ -222,6 +259,11 @@ export default function OrderDetailScreen() {
itemDetails: { itemDetails: {
flexDirection: "row", flexDirection: "row",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "center",
},
itemDeleteBtn: {
padding: spacing.xs,
marginLeft: spacing.s,
}, },
actions: { marginTop: spacing.m, marginBottom: spacing.xxl }, actions: { marginTop: spacing.m, marginBottom: spacing.xxl },
empty: { empty: {
@@ -605,7 +647,26 @@ export default function OrderDetailScreen() {
<Text style={styles.sectionTitle}>Articles ({items.length})</Text> <Text style={styles.sectionTitle}>Articles ({items.length})</Text>
{items.map((item: any) => ( {items.map((item: any) => (
<Card key={item.id} style={{ marginBottom: spacing.s }}> <Card key={item.id} style={{ marginBottom: spacing.s }}>
<Text style={styles.itemName}>{item.product_name}</Text> <View style={styles.row}>
<Text style={[styles.itemName, { flex: 1 }]}>
{item.produit ?? item.product_name}
</Text>
<TouchableOpacity
style={styles.itemDeleteBtn}
onPress={() =>
handleDeleteItem(
item.id,
item.produit ?? item.product_name,
)
}
>
<Ionicons
name="trash-outline"
size={18}
color={colors.danger}
/>
</TouchableOpacity>
</View>
<View style={styles.itemDetails}> <View style={styles.itemDetails}>
<Text style={styles.info}> <Text style={styles.info}>
Quantité: {item.quantite} Quantité: {item.quantite}
@@ -647,6 +708,15 @@ export default function OrderDetailScreen() {
style={{ marginTop: spacing.s }} style={{ marginTop: spacing.s }}
/> />
)} )}
{!["approved", "cancelled"].includes(command.status) && (
<Button
title="Le client est là"
onPress={handleNotifyClient}
variant="warning"
fullWidth
style={{ marginTop: spacing.s }}
/>
)}
{command.status === "livre" && ( {command.status === "livre" && (
<Button <Button
title="Confirmer la réception" title="Confirmer la réception"
@@ -16,6 +16,7 @@ import {
getCommandItems, getCommandItems,
deleteCommand, deleteCommand,
confirmReceptionCabine, confirmReceptionCabine,
notifyClientToDescendCabine,
getCabineLivreursList, getCabineLivreursList,
assignDeliveryPersonByCabine, assignDeliveryPersonByCabine,
} from "../../api/api_cabine"; } from "../../api/api_cabine";
@@ -51,7 +52,7 @@ export default function OrdersScreen() {
const [commands, setCommands] = useState<CommandResponse[]>([]); const [commands, setCommands] = useState<CommandResponse[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const { alert, showError, showConfirm, hideAlert } = useAlert(); const { alert, showError, showSuccess, showConfirm, hideAlert } = useAlert();
const [itemsModal, setItemsModal] = useState<{ const [itemsModal, setItemsModal] = useState<{
visible: boolean; visible: boolean;
commandId: number | null; commandId: number | null;
@@ -135,6 +136,15 @@ export default function OrdersScreen() {
); );
}; };
const handleNotifyClient = async (commandId: number) => {
try {
await notifyClientToDescendCabine(commandId);
showSuccess("Notification envoyée", "Le client a été prévenu de descendre");
} catch (e: any) {
showError("Erreur", e.message);
}
};
const handleDelete = (commandId: number) => { const handleDelete = (commandId: number) => {
showConfirm( showConfirm(
"Supprimer", "Supprimer",
@@ -356,6 +366,12 @@ export default function OrdersScreen() {
size="sm" size="sm"
variant="primary" variant="primary"
/> />
<Button
title="Le client est là"
onPress={() => handleNotifyClient(item.id)}
size="sm"
variant="warning"
/>
<Button <Button
title="Assigner" title="Assigner"
onPress={() => openAssignModal(item.id)} onPress={() => openAssignModal(item.id)}