chore: build
This commit is contained in:
@@ -462,6 +462,36 @@ func (d *Database) UpdateCommandAddress(commandID int, deliveryAddress string) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateOwnCommandAddress permet à un client de corriger l'adresse de SA
|
||||||
|
// PROPRE commande, tant qu'elle n'est pas encore prise en charge par un
|
||||||
|
// livreur (statut "en_route") ni terminée. La vérification d'appartenance et
|
||||||
|
// de statut se fait dans la clause WHERE, atomiquement : impossible de
|
||||||
|
// modifier la commande d'un autre client ou une commande déjà en route.
|
||||||
|
func (d *Database) UpdateOwnCommandAddress(commandID int, clientUsername, deliveryAddress string) error {
|
||||||
|
if err := validateAddress(deliveryAddress); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := d.GDB.Exec(`
|
||||||
|
UPDATE commandes
|
||||||
|
SET adresse = ?, updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = ? AND username = ? AND status IN ('pending', 'assigned')`,
|
||||||
|
deliveryAddress, commandID, clientUsername)
|
||||||
|
if result.Error != nil {
|
||||||
|
return fmt.Errorf("erreur lors de la mise à jour de l'adresse: %w", result.Error)
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return fmt.Errorf("commande introuvable, non modifiable (déjà en livraison ou terminée), ou n'appartenant pas à ce client")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.AddCommandLog(commandID, "address_updated",
|
||||||
|
fmt.Sprintf("Adresse corrigée par le client %s", clientUsername),
|
||||||
|
clientUsername)
|
||||||
|
|
||||||
|
log.Printf("✅ [UPD_OWN_ADDR] Adresse commande %d corrigée par %s", commandID, clientUsername)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ProposeAddressChange propose une nouvelle adresse (admin/cabine) en attente de validation client
|
// ProposeAddressChange propose une nouvelle adresse (admin/cabine) en attente de validation client
|
||||||
func (d *Database) ProposeAddressChange(commandID int, proposedAddress, proposedBy string) error {
|
func (d *Database) ProposeAddressChange(commandID int, proposedAddress, proposedBy string) error {
|
||||||
if err := validateAddress(proposedAddress); err != nil {
|
if err := validateAddress(proposedAddress); err != nil {
|
||||||
|
|||||||
@@ -263,6 +263,62 @@ func RespondToAddressProposal(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateOwnCommandAddress permet à un client de corriger l'adresse de sa
|
||||||
|
// propre commande (ex: suite à un échec de géocodage bloquant l'assignation
|
||||||
|
// auto). Refusé si la commande est déjà en_route ou terminée (voir requête
|
||||||
|
// SQL dans db.UpdateOwnCommandAddress).
|
||||||
|
func UpdateOwnCommandAddress(c *gin.Context) {
|
||||||
|
database := c.MustGet("database").(*db.Database)
|
||||||
|
geoService := c.MustGet("geoService").(*services.GeoService)
|
||||||
|
|
||||||
|
userRole := c.GetString("role")
|
||||||
|
if !utils.CheckRoleClient(c, userRole) {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clientUsername, err := safeGetUsername(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentification requise"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rateLimitKey := fmt.Sprintf("update_own_addr:%s", clientUsername)
|
||||||
|
if !checkRateLimit(rateLimitKey) {
|
||||||
|
c.JSON(http.StatusTooManyRequests, gin.H{"error": "Trop de requêtes, réessayez plus tard"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
commandID, err := strconv.Atoi(c.Param("id"))
|
||||||
|
if err != nil || commandID <= 0 {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req struct {
|
||||||
|
DeliveryAddress string `json:"delivery_address" binding:"required"`
|
||||||
|
}
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !geoService.IsValidAddress(req.DeliveryAddress) {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Adresse introuvable, vérifiez l'orthographe ou le code postal"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.UpdateOwnCommandAddress(commandID, clientUsername, req.DeliveryAddress); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("✅ [UPD_OWN_ADDR] Commande %d mise à jour par %s", commandID, clientUsername)
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"message": "Adresse mise à jour",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func ExportApprovedCommandsCSV(c *gin.Context) {
|
func ExportApprovedCommandsCSV(c *gin.Context) {
|
||||||
database := c.MustGet("database").(*db.Database)
|
database := c.MustGet("database").(*db.Database)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gestion/db"
|
"gestion/db"
|
||||||
|
"gestion/services"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -30,6 +32,7 @@ const (
|
|||||||
// POST /api/v1/deliveries/:id/start
|
// POST /api/v1/deliveries/:id/start
|
||||||
func StartDelivery(c *gin.Context) {
|
func StartDelivery(c *gin.Context) {
|
||||||
database := c.MustGet("database").(*db.Database)
|
database := c.MustGet("database").(*db.Database)
|
||||||
|
geoService := c.MustGet("geoService").(*services.GeoService)
|
||||||
|
|
||||||
username, exists := c.Get("username")
|
username, exists := c.Get("username")
|
||||||
if !exists || c.GetString("role") != "livreur" {
|
if !exists || c.GetString("role") != "livreur" {
|
||||||
@@ -114,11 +117,47 @@ func StartDelivery(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if etaMinutes == 0 && req.Latitude != 0 && req.Longitude != 0 {
|
if etaMinutes == 0 {
|
||||||
destLat, _ := command["dest_latitude"].(float64)
|
destLat, _ := command["dest_latitude"].(float64)
|
||||||
destLon, _ := command["dest_longitude"].(float64)
|
destLon, _ := command["dest_longitude"].(float64)
|
||||||
|
|
||||||
|
// Fallback 1 : cache Redis (géocodage déjà fait à l'assignation
|
||||||
|
// mais pas encore persisté en DB — cf. goroutine async dans
|
||||||
|
// handlers/commands.go AssignCommandToDeliveryman).
|
||||||
|
if destLat == 0 || destLon == 0 {
|
||||||
|
destCacheKey := fmt.Sprintf("command:destination:%d", commandID)
|
||||||
|
if destData, err := db.Redis.Get(db.RedisCtx, destCacheKey).Result(); err == nil && destData != "" {
|
||||||
|
var coords struct {
|
||||||
|
Lat float64 `json:"lat"`
|
||||||
|
Lon float64 `json:"lon"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(destData), &coords); err == nil && coords.Lat != 0 && coords.Lon != 0 {
|
||||||
|
destLat, destLon = coords.Lat, coords.Lon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback 2 : géocodage synchrone de l'adresse. Couvre le cas où
|
||||||
|
// le livreur démarre la livraison avant que la goroutine async
|
||||||
|
// d'assignation ait fini de géocoder (race condition).
|
||||||
|
if (destLat == 0 || destLon == 0) && geoService != nil {
|
||||||
|
if adresse, _ := command["adresse"].(string); adresse != "" {
|
||||||
|
if location, err := geoService.GeocodeAddress(adresse); err == nil && location != nil {
|
||||||
|
destLat, destLon = location.Latitude, location.Longitude
|
||||||
|
database.GDB.Exec(
|
||||||
|
"UPDATE commandes SET dest_latitude = ?, dest_longitude = ? WHERE id = ?",
|
||||||
|
destLat, destLon, commandID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if destLat != 0 && destLon != 0 {
|
if destLat != 0 && destLon != 0 {
|
||||||
etaMinutes = database.CalculateETAForDeliveryman(usernameStr, destLat, destLon)
|
etaMinutes = database.CalculateETAForDeliveryman(usernameStr, destLat, destLon)
|
||||||
|
} else {
|
||||||
|
// Fallback 3 : aucune coordonnée exploitable — ETA par
|
||||||
|
// défaut plutôt que pas d'ETA du tout dans le message.
|
||||||
|
etaMinutes = 30
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if etaMinutes > 0 {
|
if etaMinutes > 0 {
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services
|
|||||||
// Approbation livraison
|
// Approbation livraison
|
||||||
cartGroupV1.POST("/commands/:id/approve", handlers.ApproveDelivery)
|
cartGroupV1.POST("/commands/:id/approve", handlers.ApproveDelivery)
|
||||||
cartGroupV1.POST("/commands/:id/address/respond", handlers.RespondToAddressProposal)
|
cartGroupV1.POST("/commands/:id/address/respond", handlers.RespondToAddressProposal)
|
||||||
|
cartGroupV1.PUT("/commands/:id/address", handlers.UpdateOwnCommandAddress)
|
||||||
// ⭐ NOUVEAU - HISTORIQUE DES COMMANDES TERMINÉES
|
// ⭐ NOUVEAU - HISTORIQUE DES COMMANDES TERMINÉES
|
||||||
cartGroupV1.GET("/my-commands/history/detailed", handlers.GetMyCompletedOrdersWithItems)
|
cartGroupV1.GET("/my-commands/history/detailed", handlers.GetMyCompletedOrdersWithItems)
|
||||||
cartGroupV1.GET("/commands/:id/history", handlers.GetOrderHistory)
|
cartGroupV1.GET("/commands/:id/history", handlers.GetOrderHistory)
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ func processAutoAssignmentWithPriority(database *db.Database, geoService *servic
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tenter l'assignation
|
// Tenter l'assignation
|
||||||
success := tryAssignCommandWithPriority(database, geoService, commandID, address, priority, int(waitingTime.Minutes()))
|
username, _ := cmd["username"].(string)
|
||||||
|
success := tryAssignCommandWithPriority(database, geoService, commandID, username, address, priority, int(waitingTime.Minutes()))
|
||||||
if success {
|
if success {
|
||||||
assignedCount++
|
assignedCount++
|
||||||
} else {
|
} else {
|
||||||
@@ -103,6 +104,7 @@ func tryAssignCommandWithPriority(
|
|||||||
database *db.Database,
|
database *db.Database,
|
||||||
geoService *services.GeoService,
|
geoService *services.GeoService,
|
||||||
commandID int,
|
commandID int,
|
||||||
|
username string,
|
||||||
address string,
|
address string,
|
||||||
priority int,
|
priority int,
|
||||||
waitingMinutes int,
|
waitingMinutes int,
|
||||||
@@ -114,6 +116,7 @@ func tryAssignCommandWithPriority(
|
|||||||
location, err := geoService.GeocodeAddress(address)
|
location, err := geoService.GeocodeAddress(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ [CRON] Cmd %d - Géocodage échoué: %v", commandID, err)
|
log.Printf("❌ [CRON] Cmd %d - Géocodage échoué: %v", commandID, err)
|
||||||
|
notifyGeocodeFailure(database, username, commandID, address)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,3 +201,26 @@ func tryAssignCommandWithPriority(
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notifyGeocodeFailure avertit le client que l'adresse de sa commande n'a pas
|
||||||
|
// pu être localisée, pour qu'il puisse la corriger. Le cron retente chaque
|
||||||
|
// minute tant que la commande reste pending : un cooldown Redis d'une heure
|
||||||
|
// évite de spammer le client à chaque cycle avec la même erreur.
|
||||||
|
func notifyGeocodeFailure(database *db.Database, username string, commandID int, address string) {
|
||||||
|
if username == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cooldownKey := fmt.Sprintf("notif:cooldown:geocode_fail:%d", commandID)
|
||||||
|
set, err := db.Redis.SetNX(db.RedisCtx, cooldownKey, "1", time.Hour).Result()
|
||||||
|
if err != nil || !set {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clientOrderID := database.GetClientOrderID(commandID)
|
||||||
|
msg := fmt.Sprintf(
|
||||||
|
"Ta commande #%d ne peut pas être assignée : l'adresse \"%s\" n'a pas été trouvée. Merci de vérifier et corriger l'adresse de livraison.",
|
||||||
|
clientOrderID, address,
|
||||||
|
)
|
||||||
|
if err := database.NotifyClient(username, commandID, "address_error", msg); err != nil {
|
||||||
|
log.Printf("⚠️ [CRON] Cmd %d - Erreur notification échec géocodage: %v", commandID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1440,6 +1440,55 @@ export const cancelCommand = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Le client corrige lui-même l'adresse de sa commande (refusé si déjà en
|
||||||
|
// livraison ou terminée, cf. UpdateOwnCommandAddress côté backend).
|
||||||
|
export const updateOwnCommandAddress = async (
|
||||||
|
commandId: number,
|
||||||
|
deliveryAddress: string,
|
||||||
|
): Promise<{ success: boolean; message: string }> => {
|
||||||
|
const token = sessionStorage.getItem("token");
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return { success: false, message: "Session invalide" };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${API_URL}/commands/${commandId}/address`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ delivery_address: deliveryAddress }),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await safeJson(response);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: data.error || "Erreur lors de la mise à jour",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: data.message || "Adresse mise à jour",
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message:
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Erreur lors de la mise à jour de l'adresse",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ GET MY CANCELLATION HISTORY - Historique des annulations
|
* ✅ GET MY CANCELLATION HISTORY - Historique des annulations
|
||||||
* GET /api/v1/my-cancellation-history
|
* GET /api/v1/my-cancellation-history
|
||||||
|
|||||||
@@ -489,6 +489,40 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
.address-edit-toggle {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--primary);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.address-edit-toggle:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.address-edit {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.address-edit-input {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
background: var(--bg-secondary, #fff);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.address-edit-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
.contact {
|
.contact {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
getOrderETA,
|
getOrderETA,
|
||||||
confirmReception,
|
confirmReception,
|
||||||
cancelCommand,
|
cancelCommand,
|
||||||
|
updateOwnCommandAddress,
|
||||||
isUserAuthenticated,
|
isUserAuthenticated,
|
||||||
getPublicSettings,
|
getPublicSettings,
|
||||||
} from "../../api/api";
|
} from "../../api/api";
|
||||||
@@ -51,6 +52,7 @@ import {
|
|||||||
faWind,
|
faWind,
|
||||||
faChevronUp,
|
faChevronUp,
|
||||||
faChevronDown,
|
faChevronDown,
|
||||||
|
faPen,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
interface OrderWithTracking extends OrderDetail {
|
interface OrderWithTracking extends OrderDetail {
|
||||||
@@ -282,6 +284,11 @@ function SuiviLivraison() {
|
|||||||
const [showCancelDialog, setShowCancelDialog] = useState(false);
|
const [showCancelDialog, setShowCancelDialog] = useState(false);
|
||||||
const [orderToCancel, setOrderToCancel] = useState<number | null>(null);
|
const [orderToCancel, setOrderToCancel] = useState<number | null>(null);
|
||||||
const [cancelReason, setCancelReason] = useState("");
|
const [cancelReason, setCancelReason] = useState("");
|
||||||
|
const [editingAddressOrder, setEditingAddressOrder] = useState<
|
||||||
|
number | null
|
||||||
|
>(null);
|
||||||
|
const [newAddressValue, setNewAddressValue] = useState("");
|
||||||
|
const [savingAddress, setSavingAddress] = useState(false);
|
||||||
const [showPenaltyWarning, setShowPenaltyWarning] = useState(false);
|
const [showPenaltyWarning, setShowPenaltyWarning] = useState(false);
|
||||||
const [penaltyWarningData, setPenaltyWarningData] =
|
const [penaltyWarningData, setPenaltyWarningData] =
|
||||||
useState<CancelCommandResponse | null>(null);
|
useState<CancelCommandResponse | null>(null);
|
||||||
@@ -383,6 +390,28 @@ function SuiviLivraison() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUpdateAddress = async (orderId: number) => {
|
||||||
|
setSavingAddress(true);
|
||||||
|
try {
|
||||||
|
const res = await updateOwnCommandAddress(
|
||||||
|
orderId,
|
||||||
|
newAddressValue,
|
||||||
|
);
|
||||||
|
if (res.success) {
|
||||||
|
showToast(res.message || "Adresse mise à jour", "success");
|
||||||
|
setEditingAddressOrder(null);
|
||||||
|
setNewAddressValue("");
|
||||||
|
loadOrders();
|
||||||
|
} else {
|
||||||
|
showToast(res.message || "Erreur", "error");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
showToast("Erreur mise à jour adresse", "error");
|
||||||
|
} finally {
|
||||||
|
setSavingAddress(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const showToast = (
|
const showToast = (
|
||||||
message: string,
|
message: string,
|
||||||
type: "success" | "error" | "warning" | "info",
|
type: "success" | "error" | "warning" | "info",
|
||||||
@@ -889,6 +918,88 @@ function SuiviLivraison() {
|
|||||||
order,
|
order,
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
{(statusLow ===
|
||||||
|
"pending" ||
|
||||||
|
statusLow ===
|
||||||
|
"assigned") &&
|
||||||
|
(editingAddressOrder ===
|
||||||
|
order.id ? (
|
||||||
|
<div className="address-edit">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="address-edit-input"
|
||||||
|
value={
|
||||||
|
newAddressValue
|
||||||
|
}
|
||||||
|
onChange={(
|
||||||
|
e,
|
||||||
|
) =>
|
||||||
|
setNewAddressValue(
|
||||||
|
e
|
||||||
|
.target
|
||||||
|
.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder="Ex: 24 Rue Docteur Brindeau, 44000 Nantes"
|
||||||
|
/>
|
||||||
|
<div className="address-edit-actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setEditingAddressOrder(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
setNewAddressValue(
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-primary"
|
||||||
|
disabled={
|
||||||
|
savingAddress ||
|
||||||
|
!newAddressValue.trim()
|
||||||
|
}
|
||||||
|
onClick={() =>
|
||||||
|
handleUpdateAddress(
|
||||||
|
order.id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{savingAddress
|
||||||
|
? "Enregistrement..."
|
||||||
|
: "Enregistrer"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="address-edit-toggle"
|
||||||
|
onClick={() => {
|
||||||
|
setNewAddressValue(
|
||||||
|
getDeliveryAddress(
|
||||||
|
order,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
setEditingAddressOrder(
|
||||||
|
order.id,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={
|
||||||
|
faPen
|
||||||
|
}
|
||||||
|
/>{" "}
|
||||||
|
Modifier
|
||||||
|
l'adresse
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
{(() => {
|
{(() => {
|
||||||
const info =
|
const info =
|
||||||
getClientInfo(
|
getClientInfo(
|
||||||
|
|||||||
Reference in New Issue
Block a user