Lint: fix errors and remove unused variables

**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
This commit is contained in:
ncantu
2026-03-04 15:32:27 +01:00
parent d8a55daf3f
commit c7d389ecbb
57 changed files with 4664 additions and 3049 deletions

View File

@@ -0,0 +1,162 @@
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);
}
}
}