Files
2026-DEV-BUT3/src/context/OrdersContext.jsx
T

114 lines
2.9 KiB
React
Raw Normal View History

import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react'
const STORAGE_KEY = 'librairie-orders'
function loadOrders() {
try {
const raw = localStorage.getItem(STORAGE_KEY)
if (raw === null) return []
const parsed = JSON.parse(raw)
return Array.isArray(parsed) ? parsed : []
} catch {
return []
}
}
function saveOrders(orders) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(orders))
}
function round2(n) {
return Math.round((Number(n) + Number.EPSILON) * 100) / 100
}
function calcSubtotal(items) {
return round2(
items.reduce((sum, it) => sum + Number(it.unitPrice) * Number(it.qty), 0),
)
}
function calcDiscount({ subtotal, promotion }) {
if (!promotion) return 0
if (promotion.type === 'percent') {
const pct = Number(promotion.value)
if (!Number.isFinite(pct) || pct <= 0) return 0
return round2((subtotal * pct) / 100)
}
return 0
}
const OrdersContext = createContext(null)
export function OrdersProvider({ children }) {
const [orders, setOrders] = useState(loadOrders)
useEffect(() => {
saveOrders(orders)
}, [orders])
/**
* Équivalent local dun POST /api/orders.
* orderDraft: { items: [{ bookId, title, unitPrice, qty }], promotion?: { code, type, value } }
*/
const createOrder = useCallback((orderDraft) => {
const items = Array.isArray(orderDraft?.items) ? orderDraft.items : []
const cleaned = items
.map((it) => ({
bookId: String(it.bookId),
title: String(it.title || ''),
unitPrice: Number(it.unitPrice),
qty: Math.max(0, Math.trunc(Number(it.qty) || 0)),
}))
.filter((it) => it.bookId && it.qty > 0 && Number.isFinite(it.unitPrice))
if (cleaned.length === 0) {
throw new Error('Aucun article dans la commande')
}
const subtotal = calcSubtotal(cleaned)
const promotion = orderDraft?.promotion
? {
code: String(orderDraft.promotion.code || ''),
type: String(orderDraft.promotion.type || 'percent'),
value: Number(orderDraft.promotion.value),
}
: null
const discount = calcDiscount({ subtotal, promotion })
const total = round2(Math.max(0, subtotal - discount))
const order = {
id: crypto.randomUUID(),
createdAt: new Date().toISOString(),
items: cleaned,
promotion: promotion && promotion.code ? promotion : null,
subtotal,
discount,
total,
}
setOrders((prev) => [order, ...prev])
return order
}, [])
const value = useMemo(() => ({ orders, createOrder }), [orders, createOrder])
return <OrdersContext.Provider value={value}>{children}</OrdersContext.Provider>
}
// eslint-disable-next-line react-refresh/only-export-components
export function useOrders() {
const ctx = useContext(OrdersContext)
if (!ctx) {
throw new Error('useOrders doit être utilisé dans un OrdersProvider')
}
return ctx
}