@communecter/cocolight-api-client 1.0.131 → 1.0.133
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 +3 -3
- 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/Api.ts +9 -9
- package/src/ApiClient.ts +3 -1
- package/src/api/Action.ts +4 -0
- package/src/api/Answer.ts +846 -6
- package/src/api/Badge.ts +5 -0
- package/src/api/BaseEntity.ts +190 -11
- package/src/api/Classified.ts +4 -0
- package/src/api/Comment.ts +3 -0
- package/src/api/EndpointApi.ts +16 -1
- package/src/api/EndpointApi.types.ts +375 -44
- package/src/api/Event.ts +5 -0
- package/src/api/Form.ts +102 -2
- package/src/api/News.ts +3 -0
- package/src/api/Organization.ts +44 -2
- package/src/api/Poi.ts +4 -0
- package/src/api/Project.ts +48 -0
- package/src/api/User.ts +10 -0
- package/src/api/serverDataType/Answer.ts +25 -0
- package/src/api/serverDataType/Form.ts +84 -4
- package/src/api/serverDataType/Organization.ts +13 -0
- package/src/api/serverDataType/Project.ts +15 -0
- package/src/endpoints.module.ts +1185 -208
- package/types/api/Action.d.ts +1 -0
- package/types/api/Answer.d.ts +296 -2
- package/types/api/Badge.d.ts +1 -0
- package/types/api/BaseEntity.d.ts +101 -3
- package/types/api/Classified.d.ts +1 -0
- package/types/api/Comment.d.ts +1 -0
- package/types/api/EndpointApi.d.ts +10 -1
- package/types/api/EndpointApi.types.d.ts +334 -41
- package/types/api/Event.d.ts +1 -0
- package/types/api/Form.d.ts +58 -0
- package/types/api/News.d.ts +1 -0
- package/types/api/Organization.d.ts +20 -1
- package/types/api/Poi.d.ts +1 -0
- package/types/api/Project.d.ts +21 -0
- package/types/api/User.d.ts +7 -0
- package/types/api/serverDataType/Answer.d.ts +22 -0
- package/types/api/serverDataType/Form.d.ts +89 -4
- package/types/api/serverDataType/Organization.d.ts +13 -0
- package/types/api/serverDataType/Project.d.ts +15 -0
- package/types/endpoints.module.d.ts +1222 -1005
package/src/api/Form.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { BaseEntity } from "./BaseEntity.js";
|
|
2
2
|
import { ApiError } from "../error.js";
|
|
3
3
|
|
|
4
|
+
import type { Answer } from "./Answer.js";
|
|
5
|
+
import type { PaginatorPage, PaginatorState } from "./BaseEntity.js";
|
|
6
|
+
import type { CoformAnswersSearchData } from "./EndpointApi.types.js";
|
|
4
7
|
import type { FormItemNormalized } from "./serverDataType/Form.js";
|
|
5
8
|
|
|
6
9
|
export class Form extends BaseEntity<FormItemNormalized> {
|
|
@@ -42,19 +45,116 @@ export class Form extends BaseEntity<FormItemNormalized> {
|
|
|
42
45
|
});
|
|
43
46
|
|
|
44
47
|
if (form?.data?.id || form?.data?._id) {
|
|
45
|
-
// Normaliser les valeurs nulles en objets/tableaux vides pour cohérence
|
|
48
|
+
// Normaliser les valeurs nulles en objets/tableaux vides pour cohérence.
|
|
49
|
+
// Injection de `collection: "forms"` : le backend (PHDB::find) ne renvoie pas
|
|
50
|
+
// ce champ ; on l'ajoute côté SDK pour rester cohérent avec les autres entités.
|
|
46
51
|
const normalizedData = {
|
|
47
52
|
...form.data,
|
|
53
|
+
collection: "forms" as const,
|
|
48
54
|
inputs: form.data.inputs ?? {},
|
|
49
55
|
params: form.data.params ?? {},
|
|
50
56
|
subForms: form.data.subForms ?? [],
|
|
51
57
|
parent: form.data.parent ?? undefined,
|
|
52
58
|
config: form.data.config ?? undefined,
|
|
53
59
|
} as FormItemNormalized;
|
|
54
|
-
|
|
60
|
+
|
|
55
61
|
this._setData(normalizedData, { forceInitialDraftReset: true });
|
|
56
62
|
return this.serverData;
|
|
57
63
|
}
|
|
58
64
|
throw new ApiError(`Aucun formulaire trouvé pour l'ID ${this.id}`, 404);
|
|
59
65
|
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Recherche paginée des Answers liées à CE Form, dans le costumContext de son parent.
|
|
69
|
+
*
|
|
70
|
+
* **Délégation au parent** : le Form lui-même n'a pas de costumContext (pas de
|
|
71
|
+
* `slug` côté serverData). Cette méthode remonte `this.parent` (qui doit être
|
|
72
|
+
* une `Organization` ou un `Project` ayant un slug) et délègue à
|
|
73
|
+
* `parent.coformAnswersSearch({ filters: { form: this.id, ... } })`.
|
|
74
|
+
*
|
|
75
|
+
* Pattern identique à `Comment._add` qui remonte `this.parent` pour récupérer
|
|
76
|
+
* le contexte d'ancrage.
|
|
77
|
+
*
|
|
78
|
+
* **Important** : pour utiliser cette méthode, le Form doit avoir été créé via
|
|
79
|
+
* `org.form({id})` ou `project.form({id})` — pas via `api.form({id})` direct
|
|
80
|
+
* (qui crée un Form avec `parent = ApiClient`, sans costumContext).
|
|
81
|
+
*
|
|
82
|
+
* @param data - Paramètres de recherche (`filters`, `fields`, `sortBy`, etc.).
|
|
83
|
+
* Le champ `filters.form` est automatiquement injecté avec `this.id`.
|
|
84
|
+
* @param options - Options de pagination (`restoredState` pour reprendre une navigation)
|
|
85
|
+
* @returns Première page paginée avec `results` (Answer[]), `count`, `hasNext`, etc.
|
|
86
|
+
* @throws {ApiError} si le Form n'a pas d'id ou si son parent n'a pas de costumContext.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* const org = await api.organization({ slug: "navigatorDesTierslieux" });
|
|
90
|
+
* const form = await org.form({ id: "6925e2b05dd63b02ca70d6d9" });
|
|
91
|
+
* const page = await form.getAnswers({ sortBy: { created: -1 } });
|
|
92
|
+
* page.results; // Answer[]
|
|
93
|
+
* page.count.total; // nombre total
|
|
94
|
+
*/
|
|
95
|
+
async getAnswers(
|
|
96
|
+
data: Partial<CoformAnswersSearchData> = {},
|
|
97
|
+
options?: { restoredState?: PaginatorState }
|
|
98
|
+
): Promise<PaginatorPage<Answer>> {
|
|
99
|
+
if (!this.id) throw new ApiError("Form sans id, impossible de chercher ses réponses.", 400);
|
|
100
|
+
|
|
101
|
+
const parent = this.parent;
|
|
102
|
+
if (
|
|
103
|
+
!(parent instanceof BaseEntity)
|
|
104
|
+
|| typeof parent.serverData?.slug !== "string"
|
|
105
|
+
|| typeof parent.serverData?.id !== "string"
|
|
106
|
+
) {
|
|
107
|
+
throw new ApiError(
|
|
108
|
+
"form.getAnswers nécessite un Form créé via `org.form({id})` ou `project.form({id})` "
|
|
109
|
+
+ "(parent avec costumContext). Pour un Form isolé, utilisez "
|
|
110
|
+
+ "`organization.coformAnswersSearch({ filters: { form: form.id } })` directement.",
|
|
111
|
+
400
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Injecte filters.form = this.id et délègue au parent costum
|
|
116
|
+
const mergedFilters = { ...(data.filters ?? {}), form: this.id };
|
|
117
|
+
return parent.coformAnswersSearch(
|
|
118
|
+
{ ...data, filters: mergedFilters },
|
|
119
|
+
options
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* {@inheritDoc BaseEntity#answer}
|
|
125
|
+
*
|
|
126
|
+
* Crée une instance d'Answer **rattachée à ce Form**.
|
|
127
|
+
*
|
|
128
|
+
* - Si `answerData.id` est fourni → fetch l'Answer existante (via `COFORM_ANSWERS_BY_ID`).
|
|
129
|
+
* - Sinon → crée un draft d'Answer avec `form: this.id` pré-rempli, prêt à
|
|
130
|
+
* être complété puis sauvegardé.
|
|
131
|
+
*
|
|
132
|
+
* Le parent de l'Answer renvoyée est ce Form — la chaîne `org > form > answer`
|
|
133
|
+
* est donc préservée, ce qui permet (à terme) à l'Answer de remonter le
|
|
134
|
+
* costumContext via `this.parent.parent` si besoin.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* // Fetch d'une Answer existante
|
|
138
|
+
* const form = await org.form({ id: "6925e2b05dd63b02ca70d6d9" });
|
|
139
|
+
* const answer = await form.answer({ id: "6925869ad76aaf6c5a2b2f8a" });
|
|
140
|
+
*
|
|
141
|
+
* // Création d'un draft
|
|
142
|
+
* const draft = await form.answer();
|
|
143
|
+
* draft.data.answers = { "field1": "value1" };
|
|
144
|
+
* await draft.save();
|
|
145
|
+
*/
|
|
146
|
+
override async answer(answerData: Parameters<BaseEntity<FormItemNormalized>["answer"]>[0] = {}): Promise<Answer> {
|
|
147
|
+
if (!this.id) {
|
|
148
|
+
throw new ApiError("Form sans id, impossible de créer une Answer rattachée.", 400);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Pré-remplit `form: this.id` uniquement pour les drafts (pas de fetch par id).
|
|
152
|
+
const hasId = typeof (answerData as { id?: string }).id === "string" && (answerData as { id: string }).id.length > 0;
|
|
153
|
+
const payload: Record<string, any> = hasId
|
|
154
|
+
? answerData
|
|
155
|
+
: { form: this.id, ...answerData };
|
|
156
|
+
|
|
157
|
+
const entity = await this.entity("answers", payload);
|
|
158
|
+
return entity as Answer;
|
|
159
|
+
}
|
|
60
160
|
}
|
package/src/api/News.ts
CHANGED
|
@@ -516,4 +516,7 @@ export class News extends BaseEntity<NewsItemNormalized> {
|
|
|
516
516
|
return Boolean(authorId && this.userId === authorId);
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
+
override async form(): Promise<never> {
|
|
520
|
+
throw new ApiError(`form n'existe pas dans ${this.constructor.name}`, 501);
|
|
521
|
+
}
|
|
519
522
|
}
|
package/src/api/Organization.ts
CHANGED
|
@@ -128,6 +128,34 @@ export class Organization extends BaseEntity<OrganizationItemNormalized> {
|
|
|
128
128
|
signal: createSocialTransform("signal")
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Transforme les Answer docs imbriqués dans `data.answers` (renvoyé par /costum/navigator/gettl)
|
|
133
|
+
* en instances d'entité Answer. Le payload backend fait un PHDB::find(Answer::COLLECTION, ...) brut,
|
|
134
|
+
* donc on injecte `collection: "answers"` avant linkage pour rester cohérent avec les autres entités.
|
|
135
|
+
*
|
|
136
|
+
* Structure : `data.answers = { [formId]: AnswerDoc[] }` → `{ [formId]: Answer[] }`
|
|
137
|
+
* (le formId reste une string ; charger le Form se fait via `api.form({ id: formId })`).
|
|
138
|
+
*
|
|
139
|
+
* @protected
|
|
140
|
+
*/
|
|
141
|
+
protected override _transformServerData(data: OrganizationItemNormalized): OrganizationItemNormalized {
|
|
142
|
+
const rawAnswers = (data as Record<string, unknown>).answers as Record<string, unknown[]> | undefined;
|
|
143
|
+
if (rawAnswers && typeof rawAnswers === "object") {
|
|
144
|
+
const linkedAnswers: Record<string, unknown[]> = {};
|
|
145
|
+
for (const [formId, docs] of Object.entries(rawAnswers)) {
|
|
146
|
+
if (Array.isArray(docs)) {
|
|
147
|
+
linkedAnswers[formId] = docs.map((doc) =>
|
|
148
|
+
doc && typeof doc === "object"
|
|
149
|
+
? this._linkEntity("answers", { ...(doc as Record<string, unknown>), collection: "answers" })
|
|
150
|
+
: doc
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
(data as Record<string, unknown>).answers = linkedAnswers;
|
|
155
|
+
}
|
|
156
|
+
return data;
|
|
157
|
+
}
|
|
158
|
+
|
|
131
159
|
override _add = async (payload: Record<string, any>): Promise<void> => {
|
|
132
160
|
if (!this._calledFromSave) {
|
|
133
161
|
throw new ApiError("utilisation invalide de _add, utilisez save", 400);
|
|
@@ -457,6 +485,17 @@ export class Organization extends BaseEntity<OrganizationItemNormalized> {
|
|
|
457
485
|
return super.news(newsData);
|
|
458
486
|
}
|
|
459
487
|
|
|
488
|
+
/**
|
|
489
|
+
* {@inheritDoc BaseEntity#form}
|
|
490
|
+
*
|
|
491
|
+
* Crée une instance de Form dans le contexte de cette organisation : le Form aura
|
|
492
|
+
* `this.parent = Organization`, ce qui permet à `form.getAnswers()` de déléguer
|
|
493
|
+
* `coformAnswersSearch` avec le bon costumContext.
|
|
494
|
+
*/
|
|
495
|
+
override async form(formData: Parameters<BaseEntity<OrganizationItemNormalized>["form"]>[0] = {}) {
|
|
496
|
+
return super.form(formData);
|
|
497
|
+
}
|
|
498
|
+
|
|
460
499
|
/**
|
|
461
500
|
* Met à jour les horaires d'ouverture de l'organisation.
|
|
462
501
|
* Utilise UPDATE_PATH_VALUE pour modifier le champ openingHours.
|
|
@@ -585,8 +624,11 @@ export class Organization extends BaseEntity<OrganizationItemNormalized> {
|
|
|
585
624
|
*
|
|
586
625
|
* Cette surcharge précise que la recherche est faite dans le contexte de l'organisation.
|
|
587
626
|
*/
|
|
588
|
-
override async searchCostum(
|
|
589
|
-
|
|
627
|
+
override async searchCostum(
|
|
628
|
+
data: Parameters<BaseEntity<OrganizationItemNormalized>["searchCostum"]>[0],
|
|
629
|
+
options?: Parameters<BaseEntity<OrganizationItemNormalized>["searchCostum"]>[1]
|
|
630
|
+
) {
|
|
631
|
+
return super.searchCostum(data, options);
|
|
590
632
|
}
|
|
591
633
|
|
|
592
634
|
/**
|
package/src/api/Poi.ts
CHANGED
|
@@ -173,6 +173,10 @@ export class Poi extends BaseEntity<PoiItemNormalized> {
|
|
|
173
173
|
throw new ApiError(`event n'existe pas dans ${this.constructor.name}`, 501);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
override async form(): Promise<never> {
|
|
177
|
+
throw new ApiError(`form n'existe pas dans ${this.constructor.name}`, 501);
|
|
178
|
+
}
|
|
179
|
+
|
|
176
180
|
override async badge(): Promise<never> {
|
|
177
181
|
throw new ApiError(`badge n'existe pas dans ${this.constructor.name}`, 501);
|
|
178
182
|
}
|
package/src/api/Project.ts
CHANGED
|
@@ -63,6 +63,30 @@ export class Project extends BaseEntity<ProjectItemNormalized> {
|
|
|
63
63
|
parent: transformEntityRefs
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Transforme les Answer docs imbriqués dans `data.answers` (renvoyé par /costum/navigator/gettl)
|
|
68
|
+
* en instances d'entité Answer. Voir `Organization._transformServerData` pour le même pattern.
|
|
69
|
+
*
|
|
70
|
+
* @protected
|
|
71
|
+
*/
|
|
72
|
+
protected override _transformServerData(data: ProjectItemNormalized): ProjectItemNormalized {
|
|
73
|
+
const rawAnswers = (data as Record<string, unknown>).answers as Record<string, unknown[]> | undefined;
|
|
74
|
+
if (rawAnswers && typeof rawAnswers === "object") {
|
|
75
|
+
const linkedAnswers: Record<string, unknown[]> = {};
|
|
76
|
+
for (const [formId, docs] of Object.entries(rawAnswers)) {
|
|
77
|
+
if (Array.isArray(docs)) {
|
|
78
|
+
linkedAnswers[formId] = docs.map((doc) =>
|
|
79
|
+
doc && typeof doc === "object"
|
|
80
|
+
? this._linkEntity("answers", { ...(doc as Record<string, unknown>), collection: "answers" })
|
|
81
|
+
: doc
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
(data as Record<string, unknown>).answers = linkedAnswers;
|
|
86
|
+
}
|
|
87
|
+
return data;
|
|
88
|
+
}
|
|
89
|
+
|
|
66
90
|
override _add = async (payload: Record<string, any>): Promise<void> => {
|
|
67
91
|
if (!this._calledFromSave) {
|
|
68
92
|
throw new ApiError("utilisation invalide de _add, utilisez save", 400);
|
|
@@ -302,6 +326,18 @@ export class Project extends BaseEntity<ProjectItemNormalized> {
|
|
|
302
326
|
return super.getGallery(data);
|
|
303
327
|
}
|
|
304
328
|
|
|
329
|
+
/**
|
|
330
|
+
* {@inheritDoc BaseEntity#searchCostum}
|
|
331
|
+
*
|
|
332
|
+
* Cette surcharge précise que la recherche est faite dans le contexte du projet.
|
|
333
|
+
*/
|
|
334
|
+
override async searchCostum(
|
|
335
|
+
data: Parameters<BaseEntity<ProjectItemNormalized>["searchCostum"]>[0],
|
|
336
|
+
options?: Parameters<BaseEntity<ProjectItemNormalized>["searchCostum"]>[1]
|
|
337
|
+
) {
|
|
338
|
+
return super.searchCostum(data, options);
|
|
339
|
+
}
|
|
340
|
+
|
|
305
341
|
/**
|
|
306
342
|
* {@inheritDoc BaseEntity#project}
|
|
307
343
|
*
|
|
@@ -350,6 +386,18 @@ export class Project extends BaseEntity<ProjectItemNormalized> {
|
|
|
350
386
|
return super.badge(badgeData);
|
|
351
387
|
}
|
|
352
388
|
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* {@inheritDoc BaseEntity#form}
|
|
392
|
+
*
|
|
393
|
+
* Crée une instance de Form dans le contexte de ce projet : le Form aura
|
|
394
|
+
* `this.parent = Project`, ce qui permet à `form.getAnswers()` de déléguer
|
|
395
|
+
* `coformAnswersSearch` avec le bon costumContext.
|
|
396
|
+
*/
|
|
397
|
+
override async form(formData: Parameters<BaseEntity<ProjectItemNormalized>["form"]>[0] = {}) {
|
|
398
|
+
return super.form(formData);
|
|
399
|
+
}
|
|
400
|
+
|
|
353
401
|
/**
|
|
354
402
|
* {@inheritDoc BaseEntity#news}
|
|
355
403
|
*
|
package/src/api/User.ts
CHANGED
|
@@ -112,6 +112,7 @@ export class User extends BaseEntity<UserItemNormalized> {
|
|
|
112
112
|
News: typeof import("./News.js").News;
|
|
113
113
|
Comment: typeof import("./Comment.js").Comment;
|
|
114
114
|
Answer: typeof import("./Answer.js").Answer;
|
|
115
|
+
Form?: typeof import("./Form.js").Form;
|
|
115
116
|
Classified?: typeof import("./Classified.js").Classified;
|
|
116
117
|
Action?: typeof import("./Action.js").Action;
|
|
117
118
|
}
|
|
@@ -602,6 +603,15 @@ export class User extends BaseEntity<UserItemNormalized> {
|
|
|
602
603
|
return super.project(projectData);
|
|
603
604
|
}
|
|
604
605
|
|
|
606
|
+
/**
|
|
607
|
+
* Bloqué sur User : un Form nécessite un costumContext (slug d'Organization/Project)
|
|
608
|
+
* que User n'a pas. Utilisez `user.organization({slug}).then(o => o.form({id}))`
|
|
609
|
+
* ou `user.project({slug}).then(p => p.form({id}))` pour la chaîne propagée.
|
|
610
|
+
*/
|
|
611
|
+
override async form(): Promise<never> {
|
|
612
|
+
throw new ApiError(`form n'existe pas dans ${this.constructor.name}`, 501);
|
|
613
|
+
}
|
|
614
|
+
|
|
605
615
|
/**
|
|
606
616
|
* {@inheritDoc BaseEntity#news}
|
|
607
617
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DateValue, IdObject, ParentsMap } from "./common.js";
|
|
2
2
|
|
|
3
3
|
import type EJSONType from "../../EJSONType.js";
|
|
4
|
+
import type { Answer } from "../Answer.js";
|
|
4
5
|
import type { Organization } from "../Organization.js";
|
|
5
6
|
import type { Project } from "../Project.js";
|
|
6
7
|
import type { User } from "../User.js";
|
|
@@ -8,6 +9,30 @@ import type { User } from "../User.js";
|
|
|
8
9
|
type ObjectIDCtor = typeof EJSONType["ObjectID"];
|
|
9
10
|
type ObjectID = InstanceType<ObjectIDCtor>;
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Identifiant d'un Form (string 24 hex). Alias purement sémantique de `string` :
|
|
14
|
+
* sert à lever l'ambiguïté dans `Record<FormId, ...>` — sans ce nom, on pourrait croire
|
|
15
|
+
* qu'il s'agit d'un answerId ou autre.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ Ces formIds proviennent de la config costum (ex: `navigatorDesTierslieux`)
|
|
18
|
+
* et ne correspondent **pas toujours** à des docs queriables via `api.form({id}).get()` —
|
|
19
|
+
* il s'agit parfois de références internes au costum (templates, sous-forms, etc.).
|
|
20
|
+
* À traiter comme un opaque token côté caller.
|
|
21
|
+
*/
|
|
22
|
+
export type FormId = string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Forme JSON brute : Answer docs (MongoDB) groupés par formId.
|
|
26
|
+
* Apparaît dans `result.answers` du payload `/costum/navigator/gettl` avant linkage.
|
|
27
|
+
*/
|
|
28
|
+
export type AnswerDocsByFormId = Record<FormId, AnswerItemJson[]>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Forme normalisée : instances `Answer` groupées par formId.
|
|
32
|
+
* Construite par `Organization/Project._transformServerData` à partir de `AnswerDocsByFormId`.
|
|
33
|
+
*/
|
|
34
|
+
export type AnswersByFormId = Record<FormId, Answer[]>;
|
|
35
|
+
|
|
11
36
|
export interface AnswerLinksBlock {
|
|
12
37
|
answered: string[];
|
|
13
38
|
}
|
|
@@ -10,9 +10,16 @@ export interface FormSubFormInputField {
|
|
|
10
10
|
placeholder?: string;
|
|
11
11
|
info?: string;
|
|
12
12
|
type?: string;
|
|
13
|
+
position?: string;
|
|
13
14
|
positions?: Record<string, string>;
|
|
15
|
+
width?: string;
|
|
14
16
|
markdown?: boolean;
|
|
15
17
|
uploader?: Record<string, any>;
|
|
18
|
+
rules?: {
|
|
19
|
+
width?: { min: number; max: number };
|
|
20
|
+
syntaxe?: { regexp: string; errormsg: string };
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
};
|
|
16
23
|
[key: string]: unknown;
|
|
17
24
|
}
|
|
18
25
|
|
|
@@ -26,31 +33,104 @@ export interface FormSubFormInputs {
|
|
|
26
33
|
[key: string]: unknown;
|
|
27
34
|
}
|
|
28
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Bloc d'accès au Form — décrit qui peut répondre, quand, comment.
|
|
38
|
+
* Renvoyé par `GET_COFORM_BY_ID` (cf. /survey/coform/getformbyid).
|
|
39
|
+
*/
|
|
40
|
+
export interface FormAccess {
|
|
41
|
+
/** Si l'utilisateur courant peut répondre maintenant. */
|
|
42
|
+
canAnswer: boolean;
|
|
43
|
+
/** Raison si `canAnswer` est false (ex: "not_logged_in", "form_closed"). */
|
|
44
|
+
reason?: string;
|
|
45
|
+
/** État global du form (ex: "open", "closed", "pending"). */
|
|
46
|
+
formStatus?: string;
|
|
47
|
+
/** Id de la réponse existante de l'user courant (s'il en a une). */
|
|
48
|
+
existingAnswerId?: string | null;
|
|
49
|
+
/** Doc de la réponse existante (peut être enrichi). */
|
|
50
|
+
existingAnswer?: Record<string, unknown> | null;
|
|
51
|
+
/** Si le form requiert d'être connecté. */
|
|
52
|
+
requiresLogin?: boolean;
|
|
53
|
+
/** Réponse temporaire (brouillon) autorisée. */
|
|
54
|
+
allowTemporary?: boolean;
|
|
55
|
+
/** Réponse nécessite confirmation. */
|
|
56
|
+
withConfirmation?: boolean;
|
|
57
|
+
/** Réservé aux membres uniquement. */
|
|
58
|
+
isOnlyMember?: boolean;
|
|
59
|
+
/** Une seule réponse par personne. */
|
|
60
|
+
isOneAnswerPerPers?: boolean;
|
|
61
|
+
/** Form actif (vs désactivé). */
|
|
62
|
+
isActive?: boolean;
|
|
63
|
+
/** Fenêtres de dates (ouverture/fermeture, avec/sans confirmation). */
|
|
64
|
+
dates?: {
|
|
65
|
+
start?: string | null;
|
|
66
|
+
end?: string | null;
|
|
67
|
+
startNoConfirmation?: string | null;
|
|
68
|
+
endNoConfirmation?: string | null;
|
|
69
|
+
};
|
|
70
|
+
/** Liste des champs restreints (non visibles selon le rôle). */
|
|
71
|
+
restrictedFields?: string[];
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Forme JSON brute du Form telle que renvoyée par `GET_COFORM_BY_ID`.
|
|
77
|
+
*
|
|
78
|
+
* ⚠️ Le champ `collection` n'est PAS présent dans le payload backend natif
|
|
79
|
+
* (PHDB::find renvoie le doc brut sans nom de table). Il doit être injecté
|
|
80
|
+
* côté SDK par `Form.get()` pour cohérence avec les autres entités.
|
|
81
|
+
*/
|
|
29
82
|
export interface FormItemJson {
|
|
30
83
|
_id: IdObject;
|
|
31
|
-
|
|
84
|
+
id?: string;
|
|
85
|
+
/** Injecté côté SDK (le backend ne le renvoie pas). */
|
|
86
|
+
collection?: "forms";
|
|
87
|
+
/** Nom du Form (ex: "Coworking", "Hébergement"). */
|
|
88
|
+
name?: string;
|
|
89
|
+
/** Type du Form (ex: "coformgeneric", "template"). */
|
|
90
|
+
type: string;
|
|
91
|
+
config?: Record<string, unknown> | null;
|
|
32
92
|
parent?: ParentsMap | null;
|
|
33
93
|
subForms?: string[] | null;
|
|
34
|
-
type: string;
|
|
35
94
|
created?: DateValue;
|
|
36
95
|
creator?: string;
|
|
37
96
|
params?: Record<string, unknown> | null;
|
|
38
97
|
updated?: DateValue;
|
|
39
98
|
inputs?: Record<string, FormSubFormInputs> | null;
|
|
99
|
+
/** Logique d'accès calculée côté backend pour l'user courant. */
|
|
100
|
+
access?: FormAccess;
|
|
101
|
+
/** Champs réservés aux admins du lieu/projet. */
|
|
102
|
+
placeAdminOnlyFields?: string[];
|
|
103
|
+
/** Champs réservés aux membres du lieu/projet. */
|
|
104
|
+
placeMemberOnlyFields?: string[];
|
|
105
|
+
/** Si le grand public peut éditer une réponse partagée. */
|
|
106
|
+
publicCanEditSharedAnswer?: boolean;
|
|
107
|
+
/** Path vers une question partagée (legacy / advanced usage). */
|
|
108
|
+
sharedQuestionPath?: Record<string, unknown> | null;
|
|
109
|
+
/** Utilise une image de bannière (présent surtout sur `type=template`). */
|
|
110
|
+
useBannerImg?: boolean;
|
|
40
111
|
[key: string]: unknown;
|
|
41
112
|
}
|
|
42
113
|
|
|
43
114
|
export interface FormItemNormalized {
|
|
44
115
|
id: string;
|
|
45
116
|
_id: ObjectID;
|
|
46
|
-
|
|
117
|
+
/** Injecté côté SDK (le backend ne le renvoie pas). */
|
|
118
|
+
collection?: "forms";
|
|
119
|
+
name?: string;
|
|
120
|
+
type: string;
|
|
121
|
+
config?: Record<string, unknown> | null;
|
|
47
122
|
parent?: ParentsMap | null;
|
|
48
123
|
subForms?: string[] | null;
|
|
49
|
-
type: string;
|
|
50
124
|
created?: Date;
|
|
51
125
|
creator?: string;
|
|
52
126
|
params?: Record<string, unknown> | null;
|
|
53
127
|
updated?: Date;
|
|
54
128
|
inputs?: Record<string, FormSubFormInputs> | null;
|
|
129
|
+
access?: FormAccess;
|
|
130
|
+
placeAdminOnlyFields?: string[];
|
|
131
|
+
placeMemberOnlyFields?: string[];
|
|
132
|
+
publicCanEditSharedAnswer?: boolean;
|
|
133
|
+
sharedQuestionPath?: Record<string, unknown> | null;
|
|
134
|
+
useBannerImg?: boolean;
|
|
55
135
|
[key: string]: unknown;
|
|
56
136
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AnswerDocsByFormId, AnswersByFormId } from "./Answer.js";
|
|
1
2
|
import type {
|
|
2
3
|
GeoCoordinates,
|
|
3
4
|
GeoPosition,
|
|
@@ -64,6 +65,12 @@ export interface OrganizationItemJson {
|
|
|
64
65
|
email?: string;
|
|
65
66
|
url?: string;
|
|
66
67
|
socialNetwork?: SocialNetwork;
|
|
68
|
+
/**
|
|
69
|
+
* Enrichissement par les Answers liées au résultat — renvoyé uniquement par
|
|
70
|
+
* `/costum/navigator/gettl` (variant `navigator-tl` de `searchCostum`).
|
|
71
|
+
* Forme JSON brute (avant linkage par `_transformServerData`).
|
|
72
|
+
*/
|
|
73
|
+
answers?: AnswerDocsByFormId;
|
|
67
74
|
[key: string]: unknown;
|
|
68
75
|
}
|
|
69
76
|
|
|
@@ -94,5 +101,11 @@ export interface OrganizationItemNormalized {
|
|
|
94
101
|
email?: string;
|
|
95
102
|
url?: string;
|
|
96
103
|
socialNetwork?: SocialNetwork;
|
|
104
|
+
/**
|
|
105
|
+
* Enrichissement par les Answers liées au résultat — renvoyé uniquement par
|
|
106
|
+
* `/costum/navigator/gettl` (variant `navigator-tl` de `searchCostum`).
|
|
107
|
+
* Forme normalisée (après linkage par `Organization._transformServerData`) : `Answer[]` par formId.
|
|
108
|
+
*/
|
|
109
|
+
answers?: AnswersByFormId;
|
|
97
110
|
[key: string]: unknown;
|
|
98
111
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AnswerDocsByFormId, AnswersByFormId } from "./Answer.js";
|
|
1
2
|
import type {
|
|
2
3
|
GeoCoordinates,
|
|
3
4
|
GeoPosition,
|
|
@@ -66,6 +67,13 @@ export interface ProjectItemJson {
|
|
|
66
67
|
email?: string;
|
|
67
68
|
url?: string;
|
|
68
69
|
answer?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Enrichissement par les Answers liées au résultat — renvoyé uniquement par
|
|
72
|
+
* `/costum/navigator/gettl` (variant `navigator-tl` de `searchCostum`).
|
|
73
|
+
* Forme JSON brute (avant linkage par `_transformServerData`).
|
|
74
|
+
* Distinct du champ `answer` (singulier) qui est l'id d'une Answer principale.
|
|
75
|
+
*/
|
|
76
|
+
answers?: AnswerDocsByFormId;
|
|
69
77
|
[key: string]: unknown;
|
|
70
78
|
}
|
|
71
79
|
|
|
@@ -96,5 +104,12 @@ export interface ProjectItemNormalized {
|
|
|
96
104
|
email?: string;
|
|
97
105
|
url?: string;
|
|
98
106
|
answer?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Enrichissement par les Answers liées au résultat — renvoyé uniquement par
|
|
109
|
+
* `/costum/navigator/gettl` (variant `navigator-tl` de `searchCostum`).
|
|
110
|
+
* Forme normalisée (après linkage par `Project._transformServerData`) : `Answer[]` par formId.
|
|
111
|
+
* Distinct du champ `answer` (singulier) qui est l'id d'une Answer principale.
|
|
112
|
+
*/
|
|
113
|
+
answers?: AnswersByFormId;
|
|
99
114
|
[key: string]: unknown;
|
|
100
115
|
}
|