Badges hang on guests' necks: their content must be exact and stable. The model builds one badge per non-excluded person, from the retained plan or, on demand, from a proposal, which always prints as a draft. Each round says table, seat when seats are assigned, and a title only where a reservation makes the person its holder there; a constant itinerary takes one line. Names sort by code point, never by locale; namesakes carry their id, and a reprint gives the same texts. Without any placement it refuses, pointing at the proposals. Checked: red first; 3343 node tests green from the index alone. --- FR --- [ADD] badges : modèle de planche, du retenu ou d'une proposition choisie Les badges pendent au cou des invités : leur contenu doit être exact et stable. Le modèle fait un badge par personne non exclue, depuis le retenu ou, sur demande, une proposition, toujours imprimée en brouillon. Chaque tour dit la table, le siège sous attribution, et un titre seulement là où une réservation en fait la personne titulaire ; un itinéraire constant tient en une ligne. Les noms se classent par points de code, jamais selon la locale ; les homonymes portent leur identifiant, et une réédition rend les mêmes textes. Sans aucun placement, il refuse et renvoie aux propositions. Vérifié : rouge d'abord ; 3343 node verts depuis l'index seul. Assisted-by: Claude Opus 5.5
149 lines
5.5 KiB
JavaScript
149 lines
5.5 KiB
JavaScript
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
// Les petites charges des épreuves de la planche (§ 11.4 à § 11.8) : une
|
|
// soirée construite par ses personnes, ses tables et un plan écrit tour par
|
|
// tour, sous la forme positionnelle du fichier d'état (§ 8.9). Le plan d'un
|
|
// tour se donne comme le fichier l'écrit — une liste par table, dans l'ordre
|
|
// de charge.tables, et la réserve — ; sans attribution des sièges, chaque
|
|
// liste se trie par identifiant, comme le veut le format. Chaque appel rend
|
|
// une charge neuve, que l'épreuve modifie à son gré. Les noms sont inventés,
|
|
// hors du réservoir des démonstrations ; la version vient du module engendré.
|
|
import { creerCharge } from '../../../src/stockage/document.js';
|
|
import { VERSION } from '../../../src/version.genere.js';
|
|
|
|
/** L'horodatage du retenu des soirées construites, et celui d'une retouche. */
|
|
export const H_RETENU = '2031-05-17T20:15:07-04:00';
|
|
export const H_RETOUCHE = '2031-05-17T21:42:30-04:00';
|
|
/** L'horodatage des propositions construites. */
|
|
export const H_PROPOSITION = '2031-05-17T19:05:00-04:00';
|
|
|
|
/**
|
|
* Une personne de la charge : identifiant, prénom (null admis), nom ;
|
|
* appartenance, courriel, titre pressenti et notes à null, non exclue,
|
|
* sauf ce que champs remplace.
|
|
*
|
|
* @param {number} id
|
|
* @param {string|null} prenom
|
|
* @param {string} nom
|
|
* @param {Object} [champs]
|
|
* @returns {import('../../../src/stockage/types.js').Participant}
|
|
*/
|
|
export function personne(id, prenom, nom, champs = {}) {
|
|
return {
|
|
id,
|
|
nom,
|
|
prenom,
|
|
appartenance: null,
|
|
courriel: null,
|
|
titrePressenti: null,
|
|
notes: null,
|
|
exclu: false,
|
|
...champs,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Une table : son identifiant, son numéro affiché, ses sièges (null : le
|
|
* défaut de la soirée), ronde, posée en rangée par identifiant.
|
|
*
|
|
* @param {number} id
|
|
* @param {number} numero
|
|
* @param {number|null} [sieges]
|
|
*/
|
|
export function table(id, numero, sieges = null) {
|
|
return { id, numero, sieges, forme: 'ronde', position: { x: 300 * (id - 1), y: 0 } };
|
|
}
|
|
|
|
// La capacité courante d'une table de la charge.
|
|
const capaciteDe = (charge, { sieges }) => sieges ?? charge.evenement.siegesParDefaut;
|
|
|
|
/**
|
|
* La forme positionnelle d'un plan sur les tables de la charge : tables et
|
|
* capacités de charge.tables, dans leur ordre ; un tour par élément de plan,
|
|
* { sieges: (number|null)[][], reserve?: number[] } ; les participants, ceux
|
|
* que le premier tour écrit, triés. Sans attribution, chaque liste se trie
|
|
* par identifiant ; avec, elle garde l'ordre des sièges et ses null.
|
|
*
|
|
* @param {Object} charge
|
|
* @param {Array<{sieges: Array<Array<number|null>>, reserve?: number[]}>} plan
|
|
* @param {{siegesAttribues?: boolean}} [options]
|
|
*/
|
|
export function placementDe(charge, plan, { siegesAttribues = false } = {}) {
|
|
const placement = plan.map(({ sieges, reserve = [] }) => ({
|
|
sieges: sieges.map((liste) => (siegesAttribues ? [...liste] : [...liste].sort((a, b) => a - b))),
|
|
reserve: [...reserve].sort((a, b) => a - b),
|
|
}));
|
|
const premier = placement[0];
|
|
const participants = [...premier.sieges.flat(), ...premier.reserve].filter((id) => id !== null).sort((a, b) => a - b);
|
|
return {
|
|
siegesAttribues,
|
|
tables: charge.tables.map(({ id }) => id),
|
|
capacites: charge.tables.map((enregistrement) => capaciteDe(charge, enregistrement)),
|
|
tours: plan.length,
|
|
participants,
|
|
placement,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Une proposition de la charge, d'identifiant id, sur ce plan ; datée de
|
|
* horodatage, sans le champ quand il vaut null — un fichier écrit avant le
|
|
* champ.
|
|
*/
|
|
export function proposition(charge, id, plan, { siegesAttribues = false, horodatage = H_PROPOSITION } = {}) {
|
|
const p = {
|
|
id,
|
|
graine: id,
|
|
arret: 40,
|
|
historique: 500,
|
|
produitVersion: VERSION.affichee,
|
|
...placementDe(charge, plan, { siegesAttribues }),
|
|
};
|
|
return horodatage === null ? p : { ...p, horodatage };
|
|
}
|
|
|
|
/**
|
|
* Une soirée construite. personnes et tables sont les listes de la charge
|
|
* (personne, table) ; plan, le plan du retenu, null pour une soirée sans
|
|
* retenu ; propositions, des paires [id, plan] ; le retenu vient de la
|
|
* proposition 1, daté de horodatage (null : sans le champ). Les
|
|
* compteurs passent au-delà de chaque identifiant posé.
|
|
*
|
|
* @returns {import('../../../src/stockage/types.js').Charge}
|
|
*/
|
|
export function soiree({
|
|
personnes,
|
|
tables,
|
|
plan = null,
|
|
siegesParDefaut = 8,
|
|
siegesAttribues = false,
|
|
etat = 'retenu',
|
|
horodatage = H_RETENU,
|
|
nom = 'Soirée des Lucioles',
|
|
date = '2031-05-17',
|
|
reservations = [],
|
|
titres = [],
|
|
propositions = [],
|
|
}) {
|
|
const tours = plan?.length ?? propositions[0]?.[1].length ?? 1;
|
|
const charge = creerCharge({ id: 'evt-lucioles', nom, date, siegesParDefaut, tours });
|
|
charge.evenement.etat = etat;
|
|
charge.reglages.attribuerSieges = siegesAttribues;
|
|
charge.participants = personnes;
|
|
charge.tables = tables;
|
|
charge.reservations = reservations;
|
|
charge.titres = titres;
|
|
charge.propositions = propositions.map(([id, leur]) => proposition(charge, id, leur, { siegesAttribues }));
|
|
const ids = [1, ...propositions.map(([id]) => id)];
|
|
charge.prochainsIds = {
|
|
participant: Math.max(0, ...personnes.map(({ id }) => id)) + 1,
|
|
table: Math.max(0, ...tables.map(({ id }) => id)) + 1,
|
|
proposition: Math.max(...ids) + 1,
|
|
};
|
|
if (plan !== null) {
|
|
const retenu = { proposition: 1, ...placementDe(charge, plan, { siegesAttribues }) };
|
|
charge.retenu = horodatage === null ? retenu : { ...retenu, horodatage };
|
|
}
|
|
return charge;
|
|
}
|