**Motivations:** - Ensure lint config is not degraded and fix all lint errors for pousse workflow. **Root causes:** - Unused variables kept with _ prefix instead of removed (_row, _questReward, _i). - getAnimalBlockOrigin had 5 parameters (max 4). - use of continue statement (no-continue rule). **Correctifs:** - ESLint config verified; no eslint-disable in codebase. - Removed unused variable _row (biome-rules); removed dead function _questReward (quests); removed unused map param _i (state.js). - getAnimalBlockOrigin refactored to 4 params (pos object instead of x, y). - Replaced continue with if (cell) block in normalizeLoadedCells (state.js). - JSDoc param names aligned with _height, _y (biome-rules). **Evolutions:** - (none) **Pages affectées:** - web/js/biome-rules.js - web/js/quests.js - web/js/state.js - web/js/placement.js
163 lines
7.6 KiB
JavaScript
163 lines
7.6 KiB
JavaScript
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<string, string> }} 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 = `<span class="offer-emoji">${emoji}</span><span class="offer-label">${label}</span><span class="offer-price">${s.initial_price} 💰</span>`;
|
|
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<string, string> }} 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 = `<span class="offer-emoji">${emoji}</span><span class="offer-label">${label}</span>`;
|
|
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<string, string> }} 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<string, string> }} 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 = `<span class="offer-emoji">${emoji}</span><span class="offer-label">${label}</span><span class="offer-price">${s.initial_price} 💰</span>`;
|
|
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);
|
|
}
|
|
}
|
|
}
|