feat(planning): grille hebdomadaire complète avec API et filtres

- Connexion API via proxy Angular (résolution CORS, base path /api)
- Import CSS ng-zorro global pour les modales et composants
- Filtres Camion/Show câblés sur l'affichage de la grille
- Camions affichés via TrucksService (linkés au show du même créneau)
- Panneau de détails : spectacles + camions du jour sélectionné
- Modale de création de spectacle stylisée avec fond et centrage
- Positionnement précis des events à la minute dans leur créneau
- Auto-scroll vers l'heure courante au chargement
- Ligne "maintenant" sur la colonne du jour actuel
- Régénération des services OpenAPI (nouveaux noms de types)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 20:36:03 +02:00
parent 150b97cd2e
commit 654b297e2e
3131 changed files with 149304 additions and 104334 deletions
+22 -129
View File
@@ -6,15 +6,6 @@ const isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\d
/** @type {(value: string) => boolean} */
const isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u)
/** @type {(value: string) => boolean} */
const isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu)
/** @type {(value: string) => boolean} */
const isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu)
/** @type {(value: string) => boolean} */
const isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu)
/**
* @param {Array<string>} input
* @returns {string}
@@ -273,126 +264,31 @@ function removeDotSegments (path) {
}
/**
* Re-escape RFC 3986 gen-delims that must not appear literally in the host.
* After the URI regex parses, these characters cannot be literal in the host
* field, so any that appear after decoding came from percent-encoding and
* must be restored to prevent authority structure changes.
*
* @param {string} host
* @param {boolean} isIP - true for IPv4/IPv6 hosts (skip colon re-escaping)
* @returns {string}
* @param {import('../types/index').URIComponent} component
* @param {boolean} esc
* @returns {import('../types/index').URIComponent}
*/
const HOST_DELIMS = { '@': '%40', '/': '%2F', '?': '%3F', '#': '%23', ':': '%3A' }
const HOST_DELIM_RE = /[@/?#:]/g
const HOST_DELIM_NO_COLON_RE = /[@/?#]/g
function reescapeHostDelimiters (host, isIP) {
const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE
re.lastIndex = 0
return host.replace(re, (ch) => HOST_DELIMS[ch])
}
/**
* Normalizes percent escapes and optionally decodes only unreserved ASCII bytes.
* Reserved delimiters such as `%2F` and `%2E` stay escaped.
*
* @param {string} input
* @param {boolean} [decodeUnreserved=false]
* @returns {string}
*/
function normalizePercentEncoding (input, decodeUnreserved = false) {
if (input.indexOf('%') === -1) {
return input
function normalizeComponentEncoding (component, esc) {
const func = esc !== true ? escape : unescape
if (component.scheme !== undefined) {
component.scheme = func(component.scheme)
}
let output = ''
for (let i = 0; i < input.length; i++) {
if (input[i] === '%' && i + 2 < input.length) {
const hex = input.slice(i + 1, i + 3)
if (isHexPair(hex)) {
const normalizedHex = hex.toUpperCase()
const decoded = String.fromCharCode(parseInt(normalizedHex, 16))
if (decodeUnreserved && isUnreserved(decoded)) {
output += decoded
} else {
output += '%' + normalizedHex
}
i += 2
continue
}
}
output += input[i]
if (component.userinfo !== undefined) {
component.userinfo = func(component.userinfo)
}
return output
}
/**
* Normalizes path data without turning reserved escapes into live path syntax.
* Valid escapes are uppercased, raw unsafe characters are escaped, and only
* unreserved bytes that are not `.` are decoded.
*
* @param {string} input
* @returns {string}
*/
function normalizePathEncoding (input) {
let output = ''
for (let i = 0; i < input.length; i++) {
if (input[i] === '%' && i + 2 < input.length) {
const hex = input.slice(i + 1, i + 3)
if (isHexPair(hex)) {
const normalizedHex = hex.toUpperCase()
const decoded = String.fromCharCode(parseInt(normalizedHex, 16))
if (decoded !== '.' && isUnreserved(decoded)) {
output += decoded
} else {
output += '%' + normalizedHex
}
i += 2
continue
}
}
if (isPathCharacter(input[i])) {
output += input[i]
} else {
output += escape(input[i])
}
if (component.host !== undefined) {
component.host = func(component.host)
}
return output
}
/**
* Escapes a component while preserving existing valid percent escapes.
*
* @param {string} input
* @returns {string}
*/
function escapePreservingEscapes (input) {
let output = ''
for (let i = 0; i < input.length; i++) {
if (input[i] === '%' && i + 2 < input.length) {
const hex = input.slice(i + 1, i + 3)
if (isHexPair(hex)) {
output += '%' + hex.toUpperCase()
i += 2
continue
}
}
output += escape(input[i])
if (component.path !== undefined) {
component.path = func(component.path)
}
return output
if (component.query !== undefined) {
component.query = func(component.query)
}
if (component.fragment !== undefined) {
component.fragment = func(component.fragment)
}
return component
}
/**
@@ -414,7 +310,7 @@ function recomposeAuthority (component) {
if (ipV6res.isIPV6 === true) {
host = `[${ipV6res.escapedHost}]`
} else {
host = reescapeHostDelimiters(host, false)
host = component.host
}
}
uriTokens.push(host)
@@ -431,10 +327,7 @@ function recomposeAuthority (component) {
module.exports = {
nonSimpleDomain,
recomposeAuthority,
reescapeHostDelimiters,
normalizePercentEncoding,
normalizePathEncoding,
escapePreservingEscapes,
normalizeComponentEncoding,
removeDotSegments,
isIPv4,
isUUID,