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:
+120
-149
@@ -79,9 +79,7 @@ export class JSONSchemaGenerator {
|
||||
else if (regexes.length > 1) {
|
||||
result.schema.allOf = [
|
||||
...regexes.map((regex) => ({
|
||||
...(this.target === "draft-7" || this.target === "draft-4" || this.target === "openapi-3.0"
|
||||
? { type: "string" }
|
||||
: {}),
|
||||
...(this.target === "draft-7" ? { type: "string" } : {}),
|
||||
pattern: regex.source,
|
||||
})),
|
||||
];
|
||||
@@ -96,36 +94,22 @@ export class JSONSchemaGenerator {
|
||||
json.type = "integer";
|
||||
else
|
||||
json.type = "number";
|
||||
if (typeof exclusiveMinimum === "number") {
|
||||
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
||||
json.minimum = exclusiveMinimum;
|
||||
json.exclusiveMinimum = true;
|
||||
}
|
||||
else {
|
||||
json.exclusiveMinimum = exclusiveMinimum;
|
||||
}
|
||||
}
|
||||
if (typeof exclusiveMinimum === "number")
|
||||
json.exclusiveMinimum = exclusiveMinimum;
|
||||
if (typeof minimum === "number") {
|
||||
json.minimum = minimum;
|
||||
if (typeof exclusiveMinimum === "number" && this.target !== "draft-4") {
|
||||
if (typeof exclusiveMinimum === "number") {
|
||||
if (exclusiveMinimum >= minimum)
|
||||
delete json.minimum;
|
||||
else
|
||||
delete json.exclusiveMinimum;
|
||||
}
|
||||
}
|
||||
if (typeof exclusiveMaximum === "number") {
|
||||
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
||||
json.maximum = exclusiveMaximum;
|
||||
json.exclusiveMaximum = true;
|
||||
}
|
||||
else {
|
||||
json.exclusiveMaximum = exclusiveMaximum;
|
||||
}
|
||||
}
|
||||
if (typeof exclusiveMaximum === "number")
|
||||
json.exclusiveMaximum = exclusiveMaximum;
|
||||
if (typeof maximum === "number") {
|
||||
json.maximum = maximum;
|
||||
if (typeof exclusiveMaximum === "number" && this.target !== "draft-4") {
|
||||
if (typeof exclusiveMaximum === "number") {
|
||||
if (exclusiveMaximum <= maximum)
|
||||
delete json.maximum;
|
||||
else
|
||||
@@ -154,13 +138,7 @@ export class JSONSchemaGenerator {
|
||||
break;
|
||||
}
|
||||
case "null": {
|
||||
if (this.target === "openapi-3.0") {
|
||||
_json.type = "string";
|
||||
_json.nullable = true;
|
||||
_json.enum = [null];
|
||||
}
|
||||
else
|
||||
_json.type = "null";
|
||||
_json.type = "null";
|
||||
break;
|
||||
}
|
||||
case "any": {
|
||||
@@ -248,19 +226,10 @@ export class JSONSchemaGenerator {
|
||||
}
|
||||
case "union": {
|
||||
const json = _json;
|
||||
// Discriminated unions use oneOf (exactly one match) instead of anyOf (one or more matches)
|
||||
// because the discriminator field ensures mutual exclusivity between options in JSON Schema
|
||||
const isDiscriminated = def.discriminator !== undefined;
|
||||
const options = def.options.map((x, i) => this.process(x, {
|
||||
json.anyOf = def.options.map((x, i) => this.process(x, {
|
||||
...params,
|
||||
path: [...params.path, isDiscriminated ? "oneOf" : "anyOf", i],
|
||||
path: [...params.path, "anyOf", i],
|
||||
}));
|
||||
if (isDiscriminated) {
|
||||
json.oneOf = options;
|
||||
}
|
||||
else {
|
||||
json.anyOf = options;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "intersection": {
|
||||
@@ -284,42 +253,32 @@ export class JSONSchemaGenerator {
|
||||
case "tuple": {
|
||||
const json = _json;
|
||||
json.type = "array";
|
||||
const prefixPath = this.target === "draft-2020-12" ? "prefixItems" : "items";
|
||||
const restPath = this.target === "draft-2020-12" ? "items" : this.target === "openapi-3.0" ? "items" : "additionalItems";
|
||||
const prefixItems = def.items.map((x, i) => this.process(x, {
|
||||
...params,
|
||||
path: [...params.path, prefixPath, i],
|
||||
}));
|
||||
const rest = def.rest
|
||||
? this.process(def.rest, {
|
||||
...params,
|
||||
path: [...params.path, restPath, ...(this.target === "openapi-3.0" ? [def.items.length] : [])],
|
||||
})
|
||||
: null;
|
||||
const prefixItems = def.items.map((x, i) => this.process(x, { ...params, path: [...params.path, "prefixItems", i] }));
|
||||
if (this.target === "draft-2020-12") {
|
||||
json.prefixItems = prefixItems;
|
||||
if (rest) {
|
||||
json.items = rest;
|
||||
}
|
||||
}
|
||||
else if (this.target === "openapi-3.0") {
|
||||
json.items = {
|
||||
anyOf: prefixItems,
|
||||
};
|
||||
if (rest) {
|
||||
json.items.anyOf.push(rest);
|
||||
}
|
||||
json.minItems = prefixItems.length;
|
||||
if (!rest) {
|
||||
json.maxItems = prefixItems.length;
|
||||
}
|
||||
}
|
||||
else {
|
||||
json.items = prefixItems;
|
||||
if (rest) {
|
||||
}
|
||||
if (def.rest) {
|
||||
const rest = this.process(def.rest, {
|
||||
...params,
|
||||
path: [...params.path, "items"],
|
||||
});
|
||||
if (this.target === "draft-2020-12") {
|
||||
json.items = rest;
|
||||
}
|
||||
else {
|
||||
json.additionalItems = rest;
|
||||
}
|
||||
}
|
||||
// additionalItems
|
||||
if (def.rest) {
|
||||
json.items = this.process(def.rest, {
|
||||
...params,
|
||||
path: [...params.path, "items"],
|
||||
});
|
||||
}
|
||||
// length
|
||||
const { minimum, maximum } = schema._zod.bag;
|
||||
if (typeof minimum === "number")
|
||||
@@ -331,12 +290,7 @@ export class JSONSchemaGenerator {
|
||||
case "record": {
|
||||
const json = _json;
|
||||
json.type = "object";
|
||||
if (this.target === "draft-7" || this.target === "draft-2020-12") {
|
||||
json.propertyNames = this.process(def.keyType, {
|
||||
...params,
|
||||
path: [...params.path, "propertyNames"],
|
||||
});
|
||||
}
|
||||
json.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] });
|
||||
json.additionalProperties = this.process(def.valueType, {
|
||||
...params,
|
||||
path: [...params.path, "additionalProperties"],
|
||||
@@ -396,12 +350,7 @@ export class JSONSchemaGenerator {
|
||||
else if (vals.length === 1) {
|
||||
const val = vals[0];
|
||||
json.type = val === null ? "null" : typeof val;
|
||||
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
||||
json.enum = [val];
|
||||
}
|
||||
else {
|
||||
json.const = val;
|
||||
}
|
||||
json.const = val;
|
||||
}
|
||||
else {
|
||||
if (vals.every((v) => typeof v === "number"))
|
||||
@@ -456,13 +405,7 @@ export class JSONSchemaGenerator {
|
||||
}
|
||||
case "nullable": {
|
||||
const inner = this.process(def.innerType, params);
|
||||
if (this.target === "openapi-3.0") {
|
||||
result.ref = def.innerType;
|
||||
_json.nullable = true;
|
||||
}
|
||||
else {
|
||||
_json.anyOf = [inner, { type: "null" }];
|
||||
}
|
||||
_json.anyOf = [inner, { type: "null" }];
|
||||
break;
|
||||
}
|
||||
case "nonoptional": {
|
||||
@@ -552,12 +495,6 @@ export class JSONSchemaGenerator {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "function": {
|
||||
if (this.unrepresentable === "throw") {
|
||||
throw new Error("Function types cannot be represented in JSON Schema");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
def;
|
||||
}
|
||||
@@ -709,8 +646,7 @@ export class JSONSchemaGenerator {
|
||||
flattenRef(ref, params);
|
||||
// merge referenced schema into current
|
||||
const refSchema = this.seen.get(ref).schema;
|
||||
if (refSchema.$ref &&
|
||||
(params.target === "draft-7" || params.target === "draft-4" || params.target === "openapi-3.0")) {
|
||||
if (refSchema.$ref && params.target === "draft-7") {
|
||||
schema.allOf = schema.allOf ?? [];
|
||||
schema.allOf.push(refSchema);
|
||||
}
|
||||
@@ -737,14 +673,7 @@ export class JSONSchemaGenerator {
|
||||
else if (this.target === "draft-7") {
|
||||
result.$schema = "http://json-schema.org/draft-07/schema#";
|
||||
}
|
||||
else if (this.target === "draft-4") {
|
||||
result.$schema = "http://json-schema.org/draft-04/schema#";
|
||||
}
|
||||
else if (this.target === "openapi-3.0") {
|
||||
// OpenAPI 3.0 schema objects should not include a $schema property
|
||||
}
|
||||
else {
|
||||
// @ts-ignore
|
||||
console.warn(`Invalid target: ${this.target}`);
|
||||
}
|
||||
if (params.external?.uri) {
|
||||
@@ -824,55 +753,97 @@ function isTransforming(_schema, _ctx) {
|
||||
if (ctx.seen.has(_schema))
|
||||
return false;
|
||||
ctx.seen.add(_schema);
|
||||
const def = _schema._zod.def;
|
||||
if (def.type === "transform")
|
||||
return true;
|
||||
if (def.type === "array")
|
||||
return isTransforming(def.element, ctx);
|
||||
if (def.type === "set")
|
||||
return isTransforming(def.valueType, ctx);
|
||||
if (def.type === "lazy")
|
||||
return isTransforming(def.getter(), ctx);
|
||||
if (def.type === "promise" ||
|
||||
def.type === "optional" ||
|
||||
def.type === "nonoptional" ||
|
||||
def.type === "nullable" ||
|
||||
def.type === "readonly" ||
|
||||
def.type === "default" ||
|
||||
def.type === "prefault") {
|
||||
return isTransforming(def.innerType, ctx);
|
||||
}
|
||||
if (def.type === "intersection") {
|
||||
return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
|
||||
}
|
||||
if (def.type === "record" || def.type === "map") {
|
||||
return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
|
||||
}
|
||||
if (def.type === "pipe") {
|
||||
return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
|
||||
}
|
||||
if (def.type === "object") {
|
||||
for (const key in def.shape) {
|
||||
if (isTransforming(def.shape[key], ctx))
|
||||
return true;
|
||||
const schema = _schema;
|
||||
const def = schema._zod.def;
|
||||
switch (def.type) {
|
||||
case "string":
|
||||
case "number":
|
||||
case "bigint":
|
||||
case "boolean":
|
||||
case "date":
|
||||
case "symbol":
|
||||
case "undefined":
|
||||
case "null":
|
||||
case "any":
|
||||
case "unknown":
|
||||
case "never":
|
||||
case "void":
|
||||
case "literal":
|
||||
case "enum":
|
||||
case "nan":
|
||||
case "file":
|
||||
case "template_literal":
|
||||
return false;
|
||||
case "array": {
|
||||
return isTransforming(def.element, ctx);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (def.type === "union") {
|
||||
for (const option of def.options) {
|
||||
if (isTransforming(option, ctx))
|
||||
return true;
|
||||
case "object": {
|
||||
for (const key in def.shape) {
|
||||
if (isTransforming(def.shape[key], ctx))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (def.type === "tuple") {
|
||||
for (const item of def.items) {
|
||||
if (isTransforming(item, ctx))
|
||||
return true;
|
||||
case "union": {
|
||||
for (const option of def.options) {
|
||||
if (isTransforming(option, ctx))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (def.rest && isTransforming(def.rest, ctx))
|
||||
case "intersection": {
|
||||
return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
|
||||
}
|
||||
case "tuple": {
|
||||
for (const item of def.items) {
|
||||
if (isTransforming(item, ctx))
|
||||
return true;
|
||||
}
|
||||
if (def.rest && isTransforming(def.rest, ctx))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
case "record": {
|
||||
return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
|
||||
}
|
||||
case "map": {
|
||||
return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
|
||||
}
|
||||
case "set": {
|
||||
return isTransforming(def.valueType, ctx);
|
||||
}
|
||||
// inner types
|
||||
case "promise":
|
||||
case "optional":
|
||||
case "nonoptional":
|
||||
case "nullable":
|
||||
case "readonly":
|
||||
return isTransforming(def.innerType, ctx);
|
||||
case "lazy":
|
||||
return isTransforming(def.getter(), ctx);
|
||||
case "default": {
|
||||
return isTransforming(def.innerType, ctx);
|
||||
}
|
||||
case "prefault": {
|
||||
return isTransforming(def.innerType, ctx);
|
||||
}
|
||||
case "custom": {
|
||||
return false;
|
||||
}
|
||||
case "transform": {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
case "pipe": {
|
||||
return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
|
||||
}
|
||||
case "success": {
|
||||
return false;
|
||||
}
|
||||
case "catch": {
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
def;
|
||||
}
|
||||
return false;
|
||||
throw new Error(`Unknown schema type: ${def.type}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user