@communecter/cocolight-api-client 1.0.142 → 1.0.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cocolight-api-client.browser.js +1 -1
- package/dist/cocolight-api-client.cjs +1 -1
- package/dist/cocolight-api-client.mjs.js +1 -1
- package/dist/cocolight-api-client.vite.mjs.js +1 -1
- package/dist/cocolight-api-client.vite.mjs.js.map +1 -1
- package/package.json +1 -1
- package/src/ApiClient.ts +2 -2
- package/src/api/BaseEntity.ts +12 -0
- package/src/api/EndpointApi.ts +80 -1
- package/src/api/EndpointApi.types.ts +296 -0
- package/src/api/Form.ts +43 -1
- package/src/api/Organization.ts +77 -8
- package/src/api/Poi.ts +101 -1
- package/src/api/Project.ts +10 -7
- package/src/api/User.ts +54 -0
- package/src/api/serverDataType/Form.ts +21 -0
- package/src/endpoints.module.ts +194 -106
- package/types/api/EndpointApi.d.ts +50 -1
- package/types/api/EndpointApi.types.d.ts +286 -0
- package/types/api/Form.d.ts +28 -1
- package/types/api/Organization.d.ts +38 -0
- package/types/api/Poi.d.ts +36 -0
- package/types/api/User.d.ts +15 -1
- package/types/api/serverDataType/Form.d.ts +20 -0
- package/types/endpoints.module.d.ts +7123 -1105
package/src/api/Poi.ts
CHANGED
|
@@ -3,8 +3,38 @@ import { BaseEntity } from "./BaseEntity.js";
|
|
|
3
3
|
import { PoiItemNormalized } from "./serverDataType/Poi.js";
|
|
4
4
|
import { transformEntityRefs } from "../types/transforms.js";
|
|
5
5
|
|
|
6
|
+
import type { SetTypeValue } from "./BaseEntity.js";
|
|
6
7
|
import type { AddPoiData } from "./EndpointApi.types.js";
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Source unique des champs « équipement sportif » (RES) éditables sur le POI :
|
|
11
|
+
* - `schema` : type JSON → alimente le VIRTUAL_SCHEMA (draft-allow + cohérence des types)
|
|
12
|
+
* - `setType` : coercion backend pour `UPDATE_PATH_VALUE` (absent = string/array)
|
|
13
|
+
* On en dérive `Poi.VIRTUAL_SCHEMAS`, `Poi.CUSTOM_FIELD_HANDLERS` et `Poi.EQUIPMENT_SETTYPE`.
|
|
14
|
+
*/
|
|
15
|
+
const POI_EQUIPMENT_FIELDS: ReadonlyArray<{ name: string; schema: Record<string, unknown>; setType?: SetTypeValue }> = [
|
|
16
|
+
// Nombres (dimensions)
|
|
17
|
+
...["equip_surf", "equip_larg", "equip_long"].map((name) => ({ name, schema: { type: "number" }, setType: "float" as SetTypeValue })),
|
|
18
|
+
// Dates (string côté schéma, normalisées en Date à la lecture ; isoDate à l'écriture)
|
|
19
|
+
...["equip_maj_date", "inst_date_creation", "inst_enqu_date"].map((name) => ({ name, schema: { type: "string" }, setType: "isoDate" as SetTypeValue })),
|
|
20
|
+
// Booléens
|
|
21
|
+
...[
|
|
22
|
+
"inst_acc_handi_bool", "inst_trans_bool", "equip_eclair", "equip_acc_libre", "inst_part_bool",
|
|
23
|
+
"equip_pmr_acc", "equip_pmr_chem", "equip_pmr_douche", "equip_pmr_sanit", "equip_pmr_trib", "equip_pmr_vest",
|
|
24
|
+
"equip_pshs_aire", "equip_pshs_chem", "equip_pshs_sanit", "equip_pshs_trib", "equip_pshs_vest", "equip_pshs_sign",
|
|
25
|
+
"equip_douche"
|
|
26
|
+
].map((name) => ({ name, schema: { type: "boolean" }, setType: "bool" as SetTypeValue })),
|
|
27
|
+
// Strings
|
|
28
|
+
...[
|
|
29
|
+
"categorie", "equip_type_name", "inst_nom", "equip_type_famille", "equip_nature", "equip_sol",
|
|
30
|
+
"inst_acc_handi_type", "inst_trans_type", "equip_prop_nom", "equip_prop_type", "equip_gest_type"
|
|
31
|
+
].map((name) => ({ name, schema: { type: "string" } })),
|
|
32
|
+
// Arrays de strings
|
|
33
|
+
...[
|
|
34
|
+
"aps_name", "inst_part_type", "equip_loc_type", "equip_utilisateur"
|
|
35
|
+
].map((name) => ({ name, schema: { type: "array", items: { type: "string" } } }))
|
|
36
|
+
];
|
|
37
|
+
|
|
8
38
|
export class Poi extends BaseEntity<PoiItemNormalized> {
|
|
9
39
|
static override entityType = "poi";
|
|
10
40
|
|
|
@@ -16,7 +46,8 @@ export class Poi extends BaseEntity<PoiItemNormalized> {
|
|
|
16
46
|
"UPDATE_BLOCK_INFO",
|
|
17
47
|
"UPDATE_BLOCK_LOCALITY",
|
|
18
48
|
"UPDATE_BLOCK_SLUG",
|
|
19
|
-
"PROFIL_IMAGE"
|
|
49
|
+
"PROFIL_IMAGE",
|
|
50
|
+
"VIRTUAL_POI_EQUIPMENT"
|
|
20
51
|
];
|
|
21
52
|
|
|
22
53
|
static ADD_BLOCKS = new Map([
|
|
@@ -32,6 +63,30 @@ export class Poi extends BaseEntity<PoiItemNormalized> {
|
|
|
32
63
|
["PROFIL_IMAGE", "updateImageProfil"]
|
|
33
64
|
] as const);
|
|
34
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Champs « équipement sportif » (RES) éditables hors `UPDATE_BLOCK_*` : déclarés en
|
|
68
|
+
* VIRTUAL_SCHEMA (autorise l'écriture sur le draft + fixe les types) et persistés à
|
|
69
|
+
* l'update via `CUSTOM_FIELD_HANDLERS` → `updateEquipmentField` → `UPDATE_PATH_VALUE`.
|
|
70
|
+
* Même pattern qu'`Organization`/`Action`. (Ils restent aussi dans `ADD_POI` pour la création.)
|
|
71
|
+
*/
|
|
72
|
+
static override VIRTUAL_SCHEMAS = {
|
|
73
|
+
VIRTUAL_POI_EQUIPMENT: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: Object.fromEntries(POI_EQUIPMENT_FIELDS.map((f) => [f.name, f.schema]))
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
static override CUSTOM_FIELD_HANDLERS = new Map(
|
|
80
|
+
POI_EQUIPMENT_FIELDS.map((f) =>
|
|
81
|
+
[f.name, { updateMethod: "updateEquipmentField", schemaConstant: "VIRTUAL_POI_EQUIPMENT" }] as [string, { updateMethod: string; schemaConstant?: string }]
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
/** setType backend par champ équipement (consommé par `updateEquipmentField`). */
|
|
86
|
+
private static readonly EQUIPMENT_SETTYPE = new Map<string, SetTypeValue | undefined>(
|
|
87
|
+
POI_EQUIPMENT_FIELDS.map((f) => [f.name, f.setType])
|
|
88
|
+
);
|
|
89
|
+
|
|
35
90
|
override defaultFields: Record<string, any> = {
|
|
36
91
|
typeElement: this.getEntityType()
|
|
37
92
|
};
|
|
@@ -83,6 +138,23 @@ export class Poi extends BaseEntity<PoiItemNormalized> {
|
|
|
83
138
|
if (payload.id) delete payload.id;
|
|
84
139
|
let hasChanged = false;
|
|
85
140
|
|
|
141
|
+
// 1. Champs équipement (RES) : non couverts par les UPDATE_BLOCK_*. Persistés
|
|
142
|
+
// individuellement via CUSTOM_FIELD_HANDLERS -> updateEquipmentField -> UPDATE_PATH_VALUE
|
|
143
|
+
// (même mécanisme qu'Organization/Action).
|
|
144
|
+
const customHandlers = Poi.CUSTOM_FIELD_HANDLERS;
|
|
145
|
+
if (customHandlers) {
|
|
146
|
+
const processedFields = new Set<string>();
|
|
147
|
+
for (const [fieldName, config] of customHandlers) {
|
|
148
|
+
if (fieldName in payload && this._hasFieldChanged(fieldName)) {
|
|
149
|
+
await this._invokeCustomFieldHandler(config.updateMethod, payload[fieldName], fieldName);
|
|
150
|
+
processedFields.add(fieldName);
|
|
151
|
+
hasChanged = true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
processedFields.forEach((f) => delete payload[f]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 2. Blocs standards (description, info, locality, slug, image).
|
|
86
158
|
for (const [constant, methodName] of Array.from(Poi.UPDATE_BLOCKS)) {
|
|
87
159
|
const blockData = this._extractChangedFieldsFromSchema(
|
|
88
160
|
this.apiClient,
|
|
@@ -100,6 +172,34 @@ export class Poi extends BaseEntity<PoiItemNormalized> {
|
|
|
100
172
|
return hasChanged;
|
|
101
173
|
};
|
|
102
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Handler générique des champs « équipement sportif » (RES), enregistré dans
|
|
177
|
+
* `CUSTOM_FIELD_HANDLERS`. Persiste un champ via `UPDATE_PATH_VALUE` avec le `setType`
|
|
178
|
+
* adéquat (lu dans `EQUIPMENT_SETTYPE`). `fieldName` est fourni par `_invokeCustomFieldHandler`.
|
|
179
|
+
*
|
|
180
|
+
* Effacement façon `Action` : `null`/`""` → on envoie `""` SANS `setType` → le backend fait
|
|
181
|
+
* `!empty($value)` → `$unset` (sinon `floatval("")=0` écraserait au lieu d'effacer).
|
|
182
|
+
*/
|
|
183
|
+
async updateEquipmentField(value: unknown, fieldName: string): Promise<unknown> {
|
|
184
|
+
const setType = Poi.EQUIPMENT_SETTYPE.get(fieldName);
|
|
185
|
+
const isClear = value === null || value === "";
|
|
186
|
+
return this.updateField(fieldName, isClear ? "" : value, isClear || !setType ? {} : { setType });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Override confiné à `Poi` : forwarde `fieldName` au handler (2e argument) pour permettre
|
|
191
|
+
* un handler générique unique (`updateEquipmentField`) couvrant les ~39 champs équipement,
|
|
192
|
+
* sans toucher `BaseEntity._invokeCustomFieldHandler` (utilisé par les autres entités, qui
|
|
193
|
+
* ne passent que `value`). Copie volontaire du corps de base + le forward du `fieldName`.
|
|
194
|
+
*/
|
|
195
|
+
protected override async _invokeCustomFieldHandler(methodName: string, value: any, fieldName: string): Promise<any> {
|
|
196
|
+
const method = (this as any)[methodName];
|
|
197
|
+
if (typeof method !== "function") {
|
|
198
|
+
throw new ApiError(`Custom handler "${methodName}" not found for field "${fieldName}"`, 500);
|
|
199
|
+
}
|
|
200
|
+
return await method.call(this, value, fieldName);
|
|
201
|
+
}
|
|
202
|
+
|
|
103
203
|
async addPoi(data: Partial<AddPoiData> = {}): Promise<unknown> {
|
|
104
204
|
|
|
105
205
|
// Si le parent direct n'est pas l'utilisateur connecté (ex: création via une org),
|
package/src/api/Project.ts
CHANGED
|
@@ -74,13 +74,16 @@ export class Project extends BaseEntity<ProjectItemNormalized> {
|
|
|
74
74
|
if (rawAnswers && typeof rawAnswers === "object") {
|
|
75
75
|
const linkedAnswers: Record<string, unknown[]> = {};
|
|
76
76
|
for (const [formId, docs] of Object.entries(rawAnswers)) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
// Le backend renvoie tantôt un tableau `[doc, ...]`, tantôt une map indexée
|
|
78
|
+
// par id `{ answerId: doc }`. On normalise vers un tableau dans les deux cas.
|
|
79
|
+
const list = Array.isArray(docs)
|
|
80
|
+
? docs
|
|
81
|
+
: (docs && typeof docs === "object" ? Object.values(docs) : []);
|
|
82
|
+
linkedAnswers[formId] = list.map((doc) =>
|
|
83
|
+
doc && typeof doc === "object"
|
|
84
|
+
? this._linkEntity("answers", { ...(doc as Record<string, unknown>), collection: "answers" })
|
|
85
|
+
: doc
|
|
86
|
+
);
|
|
84
87
|
}
|
|
85
88
|
(data as Record<string, unknown>).answers = linkedAnswers;
|
|
86
89
|
}
|
package/src/api/User.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
GetSubscriptionsData,
|
|
18
18
|
GetOrganizationsNoAdminData,
|
|
19
19
|
GetOrganizationsAdminData,
|
|
20
|
+
GetUserEligiblePlacesData,
|
|
20
21
|
GetFriendsAdminData,
|
|
21
22
|
DemoteAdminData,
|
|
22
23
|
GetNotificationsData,
|
|
@@ -410,6 +411,59 @@ export class User extends BaseEntity<UserItemNormalized> {
|
|
|
410
411
|
return paginator.next() as Promise<PaginatorPage<Organization>>;
|
|
411
412
|
}
|
|
412
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Récupère les lieux (organizations) memberOf de l'utilisateur, filtrés
|
|
416
|
+
* côté serveur par des filters arbitraires (tags, source.key, etc.) et
|
|
417
|
+
* un flag `notSourceKey` configurables.
|
|
418
|
+
*
|
|
419
|
+
* Mirroir de `getOrganizations` mais permet de passer dynamiquement les
|
|
420
|
+
* filtres du finder du formulaire (vue collaborative coform/place), pour
|
|
421
|
+
* que la pagination soit correcte (filtrage côté serveur, pas côté client).
|
|
422
|
+
*
|
|
423
|
+
* Constant : GET_USER_ELIGIBLE_PLACES
|
|
424
|
+
*/
|
|
425
|
+
async getEligiblePlaces(
|
|
426
|
+
data: Partial<GetUserEligiblePlacesData> = {},
|
|
427
|
+
options?: { restoredState?: PaginatorState }
|
|
428
|
+
) {
|
|
429
|
+
// Force le default `searchType` ([NGO, Cooperative, ...]) — le PHP ne
|
|
430
|
+
// retourne rien si le tableau est vide / absent. Pattern aligné avec
|
|
431
|
+
// `getOrganizations`.
|
|
432
|
+
data.searchType = this._getDefaultFromEndpoint(
|
|
433
|
+
"GET_USER_ELIGIBLE_PLACES",
|
|
434
|
+
"searchType"
|
|
435
|
+
) as GetUserEligiblePlacesData["searchType"];
|
|
436
|
+
|
|
437
|
+
const paginator = this._createPaginatorEngine({
|
|
438
|
+
initialData: data,
|
|
439
|
+
methodName: "getEligiblePlaces",
|
|
440
|
+
restoredState: options?.restoredState,
|
|
441
|
+
finalizer: async (finalData) => {
|
|
442
|
+
delete finalData?.pathParams;
|
|
443
|
+
|
|
444
|
+
// Auto-merge du filtre memberOf par défaut si l'appelant n'en pousse
|
|
445
|
+
// que des filters "métier" (tags, etc.). On garde la sémantique
|
|
446
|
+
// "uniquement les orgs où l'user est membre validé".
|
|
447
|
+
const userFilters = finalData.filters ?? {};
|
|
448
|
+
const memberOfDefaults: Record<string, unknown> = {
|
|
449
|
+
[`links.members.${this.id}`]: { "$exists": true },
|
|
450
|
+
[`links.members.${this.id}.toBeValidated`]: { "$exists": false },
|
|
451
|
+
[`links.members.${this.id}.isInviting`]: { "$exists": false },
|
|
452
|
+
};
|
|
453
|
+
finalData.filters = { ...memberOfDefaults, ...userFilters };
|
|
454
|
+
|
|
455
|
+
// Cast nécessaire : callIsMe est typé Promise<unknown>, mais l'endpoint
|
|
456
|
+
// retourne bien la shape `{ results, count }` attendue par le finalizer.
|
|
457
|
+
// Pattern aligné avec getOrganizations (cf. la branche ternaire au-dessus).
|
|
458
|
+
return this.callIsMe(() => this.endpointApi.getUserEligiblePlaces(finalData)) as ReturnType<
|
|
459
|
+
typeof this.endpointApi.getUserEligiblePlaces
|
|
460
|
+
>;
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
return paginator.next() as Promise<PaginatorPage<Organization>>;
|
|
465
|
+
}
|
|
466
|
+
|
|
413
467
|
/**
|
|
414
468
|
* {@inheritDoc BaseEntity#getProjects}
|
|
415
469
|
*
|
|
@@ -134,3 +134,24 @@ export interface FormItemNormalized {
|
|
|
134
134
|
useBannerImg?: boolean;
|
|
135
135
|
[key: string]: unknown;
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Une contribution individuelle à une table commune (commonTable) d'un coform.
|
|
140
|
+
* Retournée par `Form.getCommonTableContributors()` (endpoint GET_COFORM_COMMONTABLE_CONTRIBUTORS).
|
|
141
|
+
* Plusieurs entrées possibles pour un même `userId` (plusieurs solutions au même usage).
|
|
142
|
+
*/
|
|
143
|
+
export interface CoformCommonTableContributor {
|
|
144
|
+
userId: string;
|
|
145
|
+
userName?: string;
|
|
146
|
+
userSlug?: string;
|
|
147
|
+
criteriaId: string;
|
|
148
|
+
/** Nom de la solution saisie. */
|
|
149
|
+
criteria: string;
|
|
150
|
+
/** love | happySmile | neutral | sad | cry | "" */
|
|
151
|
+
happiness?: string;
|
|
152
|
+
/** Note de 0 à 5. */
|
|
153
|
+
note?: number;
|
|
154
|
+
comment?: string;
|
|
155
|
+
fromAnswerId?: string;
|
|
156
|
+
[k: string]: unknown;
|
|
157
|
+
}
|