import { addPendingBaby, addReceptionAnimal } from "./zoo.js"; import { acceptSale, rejectSale, deliverSale, placeBid } from "./api-client.js"; import { animalLabel, salesPanelMySales, salesPanelToRecover, salesPanelAuctions, salesBtnAccept, salesBtnReject, salesBtnDeliver, salesBtnBid, salesPendingValidation, salesValidationInMinutes, salesBidInputAriaLabel, noFreeNursery, noFreeReception, } from "./texts-fr.js"; /** * @param {HTMLElement} panel * @param {{ asSeller?: Array<{ id: string, animal_id: string, is_baby: boolean, initial_price: number, best_bid_amount?: number | null }> }} api * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, animalEmoji: Record }} ctx */ export function addSalesPanelSellerSection(panel, api, ctx) { if (!api.asSeller || api.asSeller.length === 0) return; const { state, setState, setError, animalEmoji } = ctx; const sellerTitle = document.createElement("div"); sellerTitle.className = "sales-panel-title"; sellerTitle.textContent = salesPanelMySales; panel.appendChild(sellerTitle); for (const s of api.asSeller) { const row = document.createElement("div"); row.className = "sales-panel-row"; const emoji = animalEmoji[s.animal_id] ?? "🐾"; const label = s.is_baby ? `Bébé ${animalLabel[s.animal_id] ?? s.animal_id}` : (animalLabel[s.animal_id] ?? s.animal_id); row.innerHTML = `${emoji}${label}${s.initial_price} 💰`; if (s.best_bid_amount !== null) { const btnWrap = document.createElement("div"); btnWrap.className = "sales-panel-actions"; const acceptBtn = document.createElement("button"); acceptBtn.type = "button"; acceptBtn.textContent = salesBtnAccept; acceptBtn.className = "sales-btn-accept"; acceptBtn.addEventListener("click", () => { acceptSale(s.id).then(() => { state.salesFromApi = undefined; setState(); }).catch((e) => { setError(e.message || "Erreur"); setState(); }); }); const rejectBtn = document.createElement("button"); rejectBtn.type = "button"; rejectBtn.textContent = salesBtnReject; rejectBtn.className = "sales-btn-reject"; rejectBtn.addEventListener("click", () => { rejectSale(s.id).then(() => { state.salesFromApi = undefined; setState(); }).catch((e) => { setError(e.message || "Erreur"); setState(); }); }); btnWrap.appendChild(acceptBtn); btnWrap.appendChild(rejectBtn); row.appendChild(btnWrap); } panel.appendChild(row); } } /** * @param {{ id: string, animal_id: string, is_baby: boolean, status?: string, validated_at?: string }} s * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, animalEmoji: Record }} ctx * @returns {HTMLElement} */ export function createBuyerDeliverRow(s, ctx) { const { state, setState, setError, animalEmoji } = ctx; const row = document.createElement("div"); row.className = "sales-panel-row"; const emoji = animalEmoji[s.animal_id] ?? "🐾"; const label = s.is_baby ? `Bébé ${animalLabel[s.animal_id] ?? s.animal_id}` : (animalLabel[s.animal_id] ?? s.animal_id); row.innerHTML = `${emoji}${label}`; const validatedAtMs = s.validated_at ? new Date(s.validated_at).getTime() : 0; const nowMs = Date.now(); const pendingValidation = s.status === "sold" && validatedAtMs > nowMs; if (pendingValidation) { const remainingMin = Math.ceil((validatedAtMs - nowMs) / 60000); const pendingEl = document.createElement("span"); pendingEl.className = "sales-pending-validation"; pendingEl.setAttribute("aria-label", salesPendingValidation); pendingEl.textContent = `⏳ ${salesValidationInMinutes.replace("%s", String(remainingMin))}`; row.appendChild(pendingEl); } const deliverBtn = document.createElement("button"); deliverBtn.type = "button"; deliverBtn.textContent = salesBtnDeliver; deliverBtn.className = "sales-btn-deliver"; deliverBtn.disabled = pendingValidation; deliverBtn.addEventListener("click", () => { const [ok, keyOrReason] = s.is_baby ? addPendingBaby(state, s.animal_id, true) : addReceptionAnimal(state, s.animal_id); if (!ok) { setError(keyOrReason === "NoFreeNursery" ? noFreeNursery : keyOrReason === "NoFreeReception" ? noFreeReception : String(keyOrReason)); setState(); return; } setState(); deliverSale(s.id).then(() => { state.salesFromApi = undefined; setState(); }).catch((e) => { setError(e.message || "Erreur"); setState(); }); }); row.appendChild(deliverBtn); return row; } /** * @param {HTMLElement} panel * @param {{ asBuyerUndelivered?: Array<{ id: string, animal_id: string, is_baby: boolean, status?: string, validated_at?: string }> }} api * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, animalEmoji: Record }} ctx */ export function addSalesPanelBuyerSection(panel, api, ctx) { if (!api.asBuyerUndelivered || api.asBuyerUndelivered.length === 0) return; const buyerTitle = document.createElement("div"); buyerTitle.className = "sales-panel-title"; buyerTitle.textContent = salesPanelToRecover; panel.appendChild(buyerTitle); for (const s of api.asBuyerUndelivered) { panel.appendChild(createBuyerDeliverRow(s, ctx)); } } /** * @param {HTMLElement} panel * @param {{ active?: Array<{ id: string, seller_zoo_id: string, animal_id: string, is_baby: boolean, initial_price: number, best_bid_amount?: number | null }> }} api * @param {string} playerZooId * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, animalEmoji: Record }} ctx */ export function addSalesPanelActiveSection(panel, api, playerZooId, ctx) { if (!api.active || api.active.length === 0) return; const { state, setState, setError, animalEmoji } = ctx; const activeTitle = document.createElement("div"); activeTitle.className = "sales-panel-title"; activeTitle.textContent = salesPanelAuctions; panel.appendChild(activeTitle); for (const s of api.active) { if (s.seller_zoo_id === playerZooId) { // skip own listings in active list } else { const row = document.createElement("div"); row.className = "sales-panel-row sales-panel-row-bid"; const emoji = animalEmoji[s.animal_id] ?? "🐾"; const label = s.is_baby ? `Bébé ${animalLabel[s.animal_id] ?? s.animal_id}` : (animalLabel[s.animal_id] ?? s.animal_id); const minBid = (s.best_bid_amount ?? s.initial_price) + 1; row.innerHTML = `${emoji}${label}${s.initial_price} 💰`; const input = document.createElement("input"); input.type = "number"; input.min = String(minBid); input.value = String(minBid); input.className = "sales-bid-input"; input.setAttribute("aria-label", salesBidInputAriaLabel); const bidBtn = document.createElement("button"); bidBtn.type = "button"; bidBtn.textContent = salesBtnBid; bidBtn.className = "sales-btn-bid"; bidBtn.addEventListener("click", () => { const amount = Number(input.value) || minBid; placeBid(s.id, amount).then(() => { state.salesFromApi = undefined; setState(); }).catch((e) => { setError(e.message || "Erreur"); setState(); }); }); row.appendChild(input); row.appendChild(bidBtn); panel.appendChild(row); } } }