@communecter/cocolight-api-client 1.0.51 → 1.0.55

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.
Files changed (179) hide show
  1. package/dist/401.cocolight-api-client.browser.js +1 -0
  2. package/dist/401.cocolight-api-client.cjs +1 -0
  3. package/dist/401.cocolight-api-client.mjs.js +1 -0
  4. package/dist/588.cocolight-api-client.browser.js +1 -0
  5. package/dist/588.cocolight-api-client.cjs +1 -0
  6. package/dist/588.cocolight-api-client.mjs.js +1 -0
  7. package/dist/593.cocolight-api-client.browser.js +1 -0
  8. package/dist/593.cocolight-api-client.cjs +1 -0
  9. package/dist/593.cocolight-api-client.mjs.js +1 -0
  10. package/dist/839.cocolight-api-client.browser.js +1 -0
  11. package/dist/839.cocolight-api-client.cjs +1 -0
  12. package/dist/839.cocolight-api-client.mjs.js +1 -0
  13. package/dist/cocolight-api-client.browser.js +3 -3
  14. package/dist/cocolight-api-client.cjs +1 -1
  15. package/dist/cocolight-api-client.mjs.js +1 -1
  16. package/dist/cocolight-api-client.vite.mjs.js +1 -1
  17. package/dist/cocolight-api-client.vite.mjs.js.map +1 -1
  18. package/package.json +28 -10
  19. package/src/{Api.js → Api.ts} +99 -91
  20. package/src/{ApiClient.js → ApiClient.ts} +444 -240
  21. package/src/EJSONType.ts +103 -0
  22. package/src/api/{Badge.js → Badge.ts} +56 -45
  23. package/src/api/BaseEntity.ts +3897 -0
  24. package/src/api/Comment.ts +200 -0
  25. package/src/api/{EndpointApi.js → EndpointApi.ts} +365 -299
  26. package/src/api/{EndpointApi.types.d.ts → EndpointApi.types.ts} +166 -9
  27. package/src/api/EntityRegistry.ts +208 -0
  28. package/src/api/Event.ts +332 -0
  29. package/src/api/News.ts +331 -0
  30. package/src/api/{Organization.js → Organization.ts} +155 -119
  31. package/src/api/{Poi.js → Poi.ts} +68 -60
  32. package/src/api/{Project.js → Project.ts} +150 -127
  33. package/src/api/{User.js → User.ts} +321 -256
  34. package/src/api/UserApi.ts +148 -0
  35. package/src/api/serverDataType/Comment.ts +88 -0
  36. package/src/api/serverDataType/Event.ts +80 -0
  37. package/src/api/serverDataType/News.ts +138 -0
  38. package/src/api/serverDataType/Organization.ts +80 -0
  39. package/src/api/serverDataType/Project.ts +71 -0
  40. package/src/api/serverDataType/User.ts +103 -0
  41. package/src/api/serverDataType/common.ts +80 -0
  42. package/src/endpoints.module.ts +2621 -0
  43. package/src/error.ts +86 -0
  44. package/src/index.ts +86 -0
  45. package/src/mixin/UserMixin.ts +4 -0
  46. package/src/types/api-responses.ts +217 -0
  47. package/src/types/entities.ts +22 -0
  48. package/src/types/error-guards.ts +230 -0
  49. package/src/types/index.ts +39 -0
  50. package/src/types/payloads.ts +21 -0
  51. package/src/types/transforms.ts +110 -0
  52. package/src/utils/{FileOfflineStorageStrategy.node.js → FileOfflineStorageStrategy.node.ts} +15 -12
  53. package/src/utils/{FileStorageStrategy.node.js → FileStorageStrategy.node.ts} +17 -14
  54. package/src/utils/MultiServerFileStorageStrategy.node.ts +67 -0
  55. package/src/utils/MultiServerTokenStorageStrategy.ts +139 -0
  56. package/src/utils/{OfflineClientManager.js → OfflineClientManager.ts} +82 -86
  57. package/src/utils/OfflineQueueStorageStrategy.ts +47 -0
  58. package/src/utils/TokenStorage.ts +77 -0
  59. package/src/utils/compat.ts +12 -0
  60. package/src/utils/createDefaultMultiServerTokenStorageStrategy.ts +35 -0
  61. package/src/utils/{createDefaultOfflineStrategy.js → createDefaultOfflineStrategy.ts} +8 -3
  62. package/src/utils/createDefaultTokenStorageStrategy.ts +33 -0
  63. package/src/utils/{reactive.js → reactive.ts} +49 -40
  64. package/src/utils/stream-utils.node.ts +12 -0
  65. package/types/Api.d.ts +87 -0
  66. package/types/Api.d.ts.map +1 -0
  67. package/types/ApiClient.d.ts +437 -0
  68. package/types/ApiClient.d.ts.map +1 -0
  69. package/types/EJSONType.d.ts +53 -0
  70. package/types/EJSONType.d.ts.map +1 -0
  71. package/types/api/Badge.d.ts +24 -0
  72. package/types/api/Badge.d.ts.map +1 -0
  73. package/types/api/BaseEntity.d.ts +1322 -0
  74. package/types/api/BaseEntity.d.ts.map +1 -0
  75. package/types/api/Comment.d.ts +36 -0
  76. package/types/api/EndpointApi.d.ts +985 -0
  77. package/types/api/EndpointApi.d.ts.map +1 -0
  78. package/types/api/EndpointApi.types.d.ts +4233 -0
  79. package/types/api/EntityRegistry.d.ts +24 -0
  80. package/types/api/EntityRegistry.d.ts.map +1 -0
  81. package/types/api/Event.d.ts +122 -0
  82. package/types/api/Event.d.ts.map +1 -0
  83. package/types/api/News.d.ts +77 -0
  84. package/types/api/News.d.ts.map +1 -0
  85. package/types/api/Organization.d.ts +203 -0
  86. package/types/api/Organization.d.ts.map +1 -0
  87. package/types/api/Poi.d.ts +54 -0
  88. package/types/api/Poi.d.ts.map +1 -0
  89. package/types/api/Project.d.ts +180 -0
  90. package/types/api/Project.d.ts.map +1 -0
  91. package/types/api/User.d.ts +332 -0
  92. package/types/api/User.d.ts.map +1 -0
  93. package/types/api/UserApi.d.ts +64 -0
  94. package/types/api/UserApi.d.ts.map +1 -0
  95. package/types/api/serverDataType/Comment.d.ts +83 -0
  96. package/types/api/serverDataType/Event.d.ts +67 -0
  97. package/types/api/serverDataType/News.d.ts +130 -0
  98. package/types/api/serverDataType/Organization.d.ts +65 -0
  99. package/types/api/serverDataType/Organization.d.ts.map +1 -0
  100. package/types/api/serverDataType/Project.d.ts +58 -0
  101. package/types/api/serverDataType/Project.d.ts.map +1 -0
  102. package/types/api/serverDataType/User.d.ts +86 -0
  103. package/types/api/serverDataType/User.d.ts.map +1 -0
  104. package/types/api/serverDataType/common.d.ts +71 -0
  105. package/types/api/serverDataType/common.d.ts.map +1 -0
  106. package/types/endpoints.module.d.ts +20559 -0
  107. package/types/endpoints.module.d.ts.map +1 -0
  108. package/types/error.d.ts +54 -0
  109. package/types/error.d.ts.map +1 -0
  110. package/types/index.d.ts +59 -0
  111. package/types/index.d.ts.map +1 -0
  112. package/types/mixin/UserMixin.d.ts +1 -0
  113. package/types/mixin/UserMixin.d.ts.map +1 -0
  114. package/types/types/api-responses.d.ts +190 -0
  115. package/types/types/api-responses.d.ts.map +1 -0
  116. package/types/types/entities.d.ts +17 -0
  117. package/types/types/entities.d.ts.map +1 -0
  118. package/types/types/error-guards.d.ts +99 -0
  119. package/types/types/error-guards.d.ts.map +1 -0
  120. package/types/types/index.d.ts +7 -0
  121. package/types/types/payloads.d.ts +17 -0
  122. package/types/types/payloads.d.ts.map +1 -0
  123. package/types/types/transforms.d.ts +79 -0
  124. package/types/types/transforms.d.ts.map +1 -0
  125. package/types/utils/FileOfflineStorageStrategy.node.d.ts +11 -0
  126. package/types/utils/FileOfflineStorageStrategy.node.d.ts.map +1 -0
  127. package/types/utils/FileStorageStrategy.node.d.ts +14 -0
  128. package/types/utils/FileStorageStrategy.node.d.ts.map +1 -0
  129. package/types/utils/MultiServerFileStorageStrategy.node.d.ts +17 -0
  130. package/types/utils/MultiServerFileStorageStrategy.node.d.ts.map +1 -0
  131. package/types/utils/MultiServerTokenStorageStrategy.d.ts +44 -0
  132. package/types/utils/MultiServerTokenStorageStrategy.d.ts.map +1 -0
  133. package/types/utils/OfflineClientManager.d.ts +58 -0
  134. package/types/utils/OfflineClientManager.d.ts.map +1 -0
  135. package/types/utils/OfflineQueueStorageStrategy.d.ts +16 -0
  136. package/types/utils/OfflineQueueStorageStrategy.d.ts.map +1 -0
  137. package/types/utils/TokenStorage.d.ts +26 -0
  138. package/types/utils/TokenStorage.d.ts.map +1 -0
  139. package/types/utils/compat.d.ts +4 -0
  140. package/types/utils/compat.d.ts.map +1 -0
  141. package/types/utils/createDefaultMultiServerTokenStorageStrategy.d.ts +2 -0
  142. package/types/utils/createDefaultMultiServerTokenStorageStrategy.d.ts.map +1 -0
  143. package/types/utils/createDefaultOfflineStrategy.d.ts +2 -0
  144. package/types/utils/createDefaultOfflineStrategy.d.ts.map +1 -0
  145. package/types/utils/createDefaultTokenStorageStrategy.d.ts +2 -0
  146. package/types/utils/createDefaultTokenStorageStrategy.d.ts.map +1 -0
  147. package/types/utils/reactive.d.ts +54 -0
  148. package/types/utils/reactive.d.ts.map +1 -0
  149. package/types/utils/stream-utils.node.d.ts +3 -0
  150. package/types/utils/stream-utils.node.d.ts.map +1 -0
  151. package/dist/123.cocolight-api-client.browser.js +0 -1
  152. package/dist/123.cocolight-api-client.cjs +0 -1
  153. package/dist/22.cocolight-api-client.mjs.js +0 -1
  154. package/dist/339.cocolight-api-client.mjs.js +0 -1
  155. package/dist/394.cocolight-api-client.browser.js +0 -1
  156. package/dist/394.cocolight-api-client.cjs +0 -1
  157. package/dist/405.cocolight-api-client.browser.js +0 -1
  158. package/dist/405.cocolight-api-client.cjs +0 -1
  159. package/dist/774.cocolight-api-client.mjs.js +0 -1
  160. package/dist/790.cocolight-api-client.mjs.js +0 -1
  161. package/dist/931.cocolight-api-client.browser.js +0 -1
  162. package/dist/931.cocolight-api-client.cjs +0 -1
  163. package/src/EJSONType.js +0 -53
  164. package/src/api/BaseEntity.js +0 -2828
  165. package/src/api/EntityRegistry.js +0 -152
  166. package/src/api/Event.js +0 -226
  167. package/src/api/News.js +0 -244
  168. package/src/api/UserApi.js +0 -81
  169. package/src/endpoints.module.js +0 -5
  170. package/src/error.js +0 -68
  171. package/src/index.js +0 -34
  172. package/src/mixin/UserMixin.js +0 -8
  173. package/src/utils/MultiServerFileStorageStrategy.node.js +0 -65
  174. package/src/utils/MultiServerTokenStorageStrategy.js +0 -131
  175. package/src/utils/OfflineQueueStorageStrategy.js +0 -51
  176. package/src/utils/TokenStorage.js +0 -93
  177. package/src/utils/createDefaultMultiServerTokenStorageStrategy.js +0 -45
  178. package/src/utils/createDefaultTokenStorageStrategy.js +0 -43
  179. package/src/utils/stream-utils.node.js +0 -10
@@ -0,0 +1,332 @@
1
+ import { ApiError } from "../error.js";
2
+ import { BaseEntity, PaginatorPage } from "./BaseEntity.js";
3
+ import { transformEntityRefs } from "../types/transforms.js";
4
+
5
+ import type { AddEventData, GetAttendeesAdminData, GetAttendeesNoAdminData } from "./EndpointApi.types.js";
6
+ import type { User } from "./User.js";
7
+ import type { EventTransforms } from "../types/transforms.js";
8
+
9
+ type EventItemNormalized = import("./serverDataType/Event.js").EventItemNormalized;
10
+
11
+ export class Event extends BaseEntity<EventItemNormalized> {
12
+ static override entityType = "events";
13
+ static override entityTag = "Event";
14
+
15
+ static override SCHEMA_CONSTANTS: string[] = [
16
+ "ADD_EVENT",
17
+ // "UPDATE_BLOCK_DESCRIPTION",
18
+ // "UPDATE_BLOCK_INFO",
19
+ // "UPDATE_BLOCK_SOCIAL",
20
+ // "UPDATE_BLOCK_LOCALITY",
21
+ "UPDATE_BLOCK_SLUG",
22
+ "PROFIL_IMAGE"
23
+ ];
24
+
25
+ static ADD_BLOCKS = new Map([
26
+ ["ADD_EVENT", "addEvent"],
27
+ ["PROFIL_IMAGE", "updateImageProfil"]
28
+ ] as const);
29
+
30
+ static UPDATE_BLOCKS = new Map([
31
+ // ["UPDATE_BLOCK_DESCRIPTION", "updateDescription"],
32
+ // ["UPDATE_BLOCK_SOCIAL", "updateSocial"],
33
+ // ["UPDATE_BLOCK_LOCALITY", "updateLocality"],
34
+ // ["UPDATE_BLOCK_INFO", "updateInfo"],
35
+ ["UPDATE_BLOCK_SLUG", "updateSlug"],
36
+ ["PROFIL_IMAGE", "updateImageProfil"]
37
+ ] as const);
38
+
39
+ override defaultFields: Record<string, any> = {
40
+ typeElement: this.getEntityType()
41
+ };
42
+
43
+ override removeFields: string[] = [
44
+ "typeElement"
45
+ ];
46
+
47
+ override transforms: EventTransforms = {
48
+ parent: transformEntityRefs,
49
+ organizer: transformEntityRefs
50
+ };
51
+
52
+ override _add = async (payload: Record<string, any>): Promise<void> => {
53
+ if (!this._calledFromSave) {
54
+ throw new ApiError("utilisation invalide de _add, utilisez save", 400);
55
+ }
56
+
57
+ payload.id = this._newId?.();
58
+ if (payload.slug) delete payload.slug;
59
+
60
+ for (const [constant, methodName] of Array.from(Event.ADD_BLOCKS)) {
61
+ const blockData = this._extractChangedFieldsFromSchema(
62
+ this.apiClient,
63
+ constant,
64
+ { ...payload, ...this.defaultFields },
65
+ () => {}
66
+ );
67
+ if (blockData && Object.keys(blockData).length > 0) {
68
+ const data = await this._invokeBlockMethod(Event.ADD_BLOCKS, methodName, blockData);
69
+ if (!this.id && data?.map?.id) {
70
+ this._draftData.id = data.map.id;
71
+ this._draftData.slug = data.map.slug;
72
+ }
73
+ }
74
+ }
75
+ };
76
+
77
+ override _update = async (payload: Record<string, any>): Promise<boolean> => {
78
+ if(!this.isAdmin()){
79
+ throw new ApiError("Vous n'avez pas les droits pour modifier cet événement", 403);
80
+ }
81
+
82
+ if (!this._calledFromSave) {
83
+ throw new ApiError("utilisation invalide de _update, utilisez save", 400);
84
+ }
85
+
86
+ let hasChanged = false;
87
+
88
+ const blockData = this._extractAllValidFieldsFromSchema(
89
+ this.apiClient,
90
+ "ADD_EVENT",
91
+ { ...payload, ...this.defaultFields },
92
+ () => this.initialDraftData,
93
+ this.removeFields
94
+ );
95
+ if (blockData && Object.keys(blockData).length > 0) {
96
+ await this["addEvent"](blockData);
97
+ hasChanged = true;
98
+ }
99
+
100
+ if (payload.id) delete payload.id;
101
+
102
+ for (const [constant, methodName] of Array.from(Event.UPDATE_BLOCKS)) {
103
+ const blockData = this._extractChangedFieldsFromSchema(
104
+ this.apiClient,
105
+ constant,
106
+ { ...payload, ...this.defaultFields },
107
+ () => this.initialDraftData,
108
+ this.removeFields
109
+ );
110
+ if (blockData && Object.keys(blockData).length > 0) {
111
+ await this._invokeBlockMethod(Event.UPDATE_BLOCKS, methodName, blockData);
112
+ hasChanged = true;
113
+ }
114
+ }
115
+
116
+ return hasChanged;
117
+ };
118
+
119
+ async addEvent(data: Partial<AddEventData> = {}): Promise<unknown> {
120
+
121
+ if (!this.isMe && !data.organizer && this.parent) {
122
+ (data as any).organizer = {};
123
+ (data as any).organizer[`${this.parent.id}`] = {
124
+ type: this.parent.getEntityType(),
125
+ name: (this.parent as any).name
126
+ };
127
+ }
128
+
129
+ return this.callIsConnected(() => this.endpointApi.addEvent(data as AddEventData));
130
+ }
131
+
132
+
133
+ override async getOrganizations(): Promise<never> {
134
+ throw new ApiError(`getOrganizations n'existe pas dans ${this.constructor.name}`, 501);
135
+ }
136
+
137
+
138
+ override async getProjects(): Promise<never> {
139
+ throw new ApiError(`getProjects n'existe pas dans ${this.constructor.name}`, 501);
140
+ }
141
+
142
+
143
+ async getEvents(): Promise<never> {
144
+ throw new ApiError(`getEvents - les sous-events ne sont pas encore implémentés dans ${this.constructor.name}`, 501);
145
+ }
146
+
147
+
148
+ override async getPois(): Promise<never> {
149
+ throw new ApiError(`getPois n'existe pas dans ${this.constructor.name}`, 501);
150
+ }
151
+
152
+
153
+ override async getBadgesIssuer(): Promise<never> {
154
+ throw new ApiError(`getBadgesIssuer n'existe pas dans ${this.constructor.name}`, 501);
155
+ }
156
+
157
+ /**
158
+ * {@inheritDoc BaseEntity#getNews}
159
+ *
160
+ * Récupère les actualités de l'événement.
161
+ */
162
+ override async getNews(data: Parameters<BaseEntity<EventItemNormalized>["getNews"]>[0] = {}) {
163
+ return super.getNews(data);
164
+ }
165
+
166
+ /**
167
+ * {@inheritDoc BaseEntity#getSubscribers}
168
+ *
169
+ * Récupère les abonnés de l'événement.
170
+ */
171
+ override async getSubscribers(data: Parameters<BaseEntity<EventItemNormalized>["getSubscribers"]>[0] = {}) {
172
+ return super.getSubscribers(data);
173
+ }
174
+
175
+ /**
176
+ * Récupérer les participants d'un événement.
177
+ * Constant : GET_ATTENDEES_NO_ADMIN
178
+ * @param data - Paramètres (partiels) de recherche/pagination.
179
+ * @param options - Options supplémentaires.
180
+ * @param options.toBeValidated - Indique si les participants doivent être validés.
181
+ * @param options.isAdmin - Indique si l'utilisateur est admin.
182
+ * @param options.isInviting - Indique si l'utilisateur est en attente d'invitation.
183
+ * @param options.roles - Liste des rôles à filtrer.
184
+ * @param isNext - Indique si c'est une recherche suivante (pagination).
185
+ * @returns - Un objet contenant le nombre de participants et la liste des participants.
186
+ * @throws {ApiResponseError} - Si une erreur se produit lors de la récupération des participants.
187
+ *
188
+ * @example
189
+ * // Récupérer tous les participants
190
+ * const participants = await project.getAttendees();
191
+ *
192
+ * // Récupérer les participants avec validation en attente
193
+ * const participantsToBeValidated = await project.getAttendees({}, { toBeValidated: true });
194
+ *
195
+ * // Récupérer les participants avec un rôle spécifique
196
+ * const contributorsWithRole = await project.getAttendees({}, { roles: ["admin"] });
197
+ *
198
+ * // Récupérer les participants administrateurs
199
+ * const adminParticipants = await project.getAttendees({}, { isAdmin: true });
200
+ *
201
+ * // Récupérer les participants en attente d'invitation
202
+ * const invitingParticipants = await project.getAttendees({}, { isInviting: true });
203
+ *
204
+ */
205
+ async getAttendees(
206
+ data: Partial<GetAttendeesNoAdminData | GetAttendeesAdminData> = {},
207
+ options: {
208
+ toBeValidated?: boolean;
209
+ isAdmin?: boolean;
210
+ isAdminPending?: boolean;
211
+ isInviting?: boolean;
212
+ roles?: any[];
213
+ } = {}
214
+ ): Promise<PaginatorPage<User>> {
215
+ data.searchType = this._getDefaultFromEndpoint("GET_ATTENDEES_NO_ADMIN", "searchType") as GetAttendeesNoAdminData["searchType"];
216
+ // data.searchBy = "ALL";
217
+
218
+ const paginator = this._createPaginatorEngine({
219
+ initialData: data,
220
+ finalizer: async (finalData) => {
221
+
222
+ const { toBeValidated, isAdmin, isInviting, isAdminPending, roles = [] } = options;
223
+
224
+ if(this.isMe){
225
+ finalData.pathParams = { type: this.getEntityType(), id: this.id };
226
+ // NOTE : dans le schema je crois que si pas de finalData.filters alors le default ce fait avec finalData.pathParams
227
+ // finalData.filters = {
228
+ // [`links.events.${this.id}`]: { "$exists": true },
229
+ // [`links.events.${this.id}.toBeValidated`]: { "$exists": false },
230
+ // [`links.events.${this.id}.isInviting`]: { "$exists": false }
231
+ // };
232
+ finalData.filters = this._buildLinkFilters(this.id, { linkType: "events", toBeValidated, isAdmin, isAdminPending, isInviting, roles });
233
+ } else {
234
+ delete finalData?.pathParams;
235
+ finalData.filters = this._buildLinkFilters(this.id, { linkType: "events", toBeValidated: false, isAdmin, isInviting, roles });
236
+ }
237
+
238
+ const fetchFn = this.isMe
239
+ ? () => this.callIsMe(() => this.endpointApi.getAttendeesAdmin(finalData))
240
+ : () => this.endpointApi.getAttendeesNoAdmin(finalData);
241
+
242
+ return fetchFn();
243
+ }
244
+ });
245
+
246
+ return paginator.next() as Promise<PaginatorPage<User>>;
247
+ }
248
+
249
+ override async project(): Promise<never> {
250
+ throw new ApiError(`project n'existe pas dans ${this.constructor.name}`, 501);
251
+ }
252
+
253
+
254
+ override async poi(): Promise<never> {
255
+ throw new ApiError(`poi n'existe pas dans ${this.constructor.name}`, 501);
256
+ }
257
+
258
+
259
+ override async event(): Promise<never> {
260
+ throw new ApiError(`les sous-events ne sont pas encore implémentés dans ${this.constructor.name}`, 501);
261
+ }
262
+
263
+
264
+ override async badge(): Promise<never> {
265
+ throw new ApiError(`badge n'existe pas dans ${this.constructor.name}`, 501);
266
+ }
267
+
268
+ /**
269
+ * {@inheritDoc BaseEntity#news}
270
+ *
271
+ * Crée une instance de news et la récupère si nécessaire.
272
+ */
273
+ override async news(newsData: Parameters<BaseEntity<EventItemNormalized>["news"]>[0] = {}) {
274
+ return super.news(newsData);
275
+ }
276
+
277
+ /**
278
+ * ───────────────────────────────
279
+ * Lien utilisateur ↔ event
280
+ * (participation, abonnement)
281
+ * ───────────────────────────────
282
+ */
283
+
284
+ /**
285
+ * {@inheritDoc BaseEntity#requestToJoin}
286
+ *
287
+ * Rejoindre l'événement en tant que participant.
288
+ */
289
+ override async requestToJoin() {
290
+ return super.requestToJoin();
291
+ }
292
+
293
+ /**
294
+ * {@inheritDoc BaseEntity#acceptInvitation}
295
+ *
296
+ * Accepte une invitation à rejoindre l'événement.
297
+ * Cette action valide un lien en attente avec l'option `isInviting`.
298
+ */
299
+ override async acceptInvitation() {
300
+ return super.acceptInvitation();
301
+ }
302
+
303
+ /**
304
+ * {@inheritDoc BaseEntity#leave}
305
+ *
306
+ * Quitte l'événement, que ce soit en tant que participant ou administrateur.
307
+ * Cette action supprime le lien entre l'utilisateur et l'événement.
308
+ */
309
+ override async leave() {
310
+ return super.leave();
311
+ }
312
+
313
+ /**
314
+ * {@inheritDoc BaseEntity#follow}
315
+ *
316
+ * Suivre un événement.
317
+ * Cette action permet à l'utilisateur de suivre un événement.
318
+ */
319
+ override async follow() {
320
+ return super.follow();
321
+ }
322
+
323
+ /**
324
+ * {@inheritDoc BaseEntity#unfollow}
325
+ *
326
+ * Se désabonne d'un événement.
327
+ */
328
+ override async unfollow() {
329
+ return super.unfollow();
330
+ }
331
+
332
+ }
@@ -0,0 +1,331 @@
1
+ import { ApiError, ApiResponseError } from "../error.js";
2
+ import { BaseEntity } from "./BaseEntity.js";
3
+
4
+ import type { Comment } from "./Comment.js";
5
+ import type { AddNewsData, UpdateNewsData, DeleteNewsData, AddImageNewsData, AddFileNewsData, GetCommentsData } from "./EndpointApi.types.js";
6
+ import type { NewsItemNormalized } from "./serverDataType/News.js";
7
+
8
+ export class News extends BaseEntity<NewsItemNormalized> {
9
+ static override entityType = "news";
10
+
11
+ static override entityTag = "News";
12
+
13
+ static override SCHEMA_CONSTANTS: string[] = [
14
+ "ADD_NEWS"
15
+ ];
16
+
17
+ static ADD_BLOCKS = new Map([
18
+ ["ADD_NEWS", "addNews"]
19
+ ] as const);
20
+
21
+ static UPDATE_BLOCKS = new Map([
22
+ ["ADD_NEWS", "updateNews"]
23
+ ] as const);
24
+
25
+ override defaultFields: Record<string, any> = {};
26
+
27
+ override removeFields: string[] = ["parentId", "parentType"];
28
+
29
+ override transforms: {
30
+ scope: (val: any) => any;
31
+ mentions: (val: any) => any[];
32
+ mediaImg: (val: any) => {
33
+ countImages: number;
34
+ images: string[];
35
+ };
36
+ mediaFile: (val: any) => {
37
+ countFiles: number;
38
+ files: any[];
39
+ };
40
+ } = {
41
+ scope: val => val?.type,
42
+ mentions: val =>
43
+ Array.isArray(val)
44
+ ? val.map(m => ({ ...m, count: parseInt(m.count) }))
45
+ : [],
46
+ mediaImg: val => {
47
+ const images = val?.images?.map((img: any) => img.id).filter(Boolean) || [];
48
+ return images.length > 0 ? { countImages: images.length, images } : { countImages: 0, images: [] };
49
+ },
50
+ mediaFile: val => {
51
+ const files = val?.files?.map((f: any) => f.id).filter(Boolean) || [];
52
+ return files.length > 0 ? { countFiles: files.length, files } : { countFiles: 0, files: [] };
53
+ }
54
+ };
55
+
56
+ /**
57
+ * Transforme les champs imbriqués (author, target, sharedBy, etc.) en instances d'entités.
58
+ * @param data - Les données brutes du serveur.
59
+ * @returns Les données transformées.
60
+ * @protected
61
+ */
62
+ protected override _transformServerData(data: NewsItemNormalized): NewsItemNormalized {
63
+ // Transformer author en instance User/Organization
64
+ if (data.author) {
65
+ data.author = this._linkNestedEntity(data.author);
66
+ }
67
+
68
+ // Transformer target en instance User/Organization/Project
69
+ if (data.target) {
70
+ data.target = this._linkNestedEntity(data.target);
71
+ }
72
+
73
+ // Transformer lastAuthorShare en instance
74
+ if (data.lastAuthorShare) {
75
+ data.lastAuthorShare = this._linkNestedEntity(data.lastAuthorShare);
76
+ }
77
+
78
+ // Transformer le tableau sharedBy
79
+ if (Array.isArray(data.sharedBy)) {
80
+ data.sharedBy = data.sharedBy.map(item => this._linkNestedEntity(item));
81
+ }
82
+
83
+ return data;
84
+ }
85
+
86
+ /**
87
+ * Récupérer des actualités par IDs : Récupère des actualités à partir d'une liste d'identifiants.
88
+ * Constant : GET_NEWS_BY_ID
89
+ */
90
+ override async get(): Promise<Record<string, any>> {
91
+ if (!this.id) throw new ApiError("Impossible de rafraîchir sans ID.", 400);
92
+ const id = this.id; // Type narrowing
93
+
94
+ const newsArray = await this.callIsConnected(() =>
95
+ this.endpointApi.getNewsById({ ids: [id] })
96
+ );
97
+
98
+ if (newsArray && Array.isArray(newsArray) && newsArray.length === 1) {
99
+ const data = newsArray[0];
100
+ this._setData(data, { forceInitialDraftReset: true });
101
+ return this.serverData;
102
+ }
103
+ throw new ApiError(`Aucune actualité trouvée pour l'ID ${this.id}`, 404);
104
+ }
105
+
106
+
107
+ override _add = async (payload: Record<string, any>): Promise<void> => {
108
+ if (!this._calledFromSave) {
109
+ throw new Error("utilisation invalide de _add, utilisez save");
110
+ }
111
+
112
+ // si le texte est vide, on met un espace pour que le champ soit pris en compte car sur le serveur
113
+ // il y a une vérification de la taille du texte je pense
114
+ if(payload.text === ""){
115
+ payload.text = " ";
116
+ }
117
+ if (this.parent) {
118
+ payload.parentId = this.parent.id;
119
+ payload.parentType = this.parent.getEntityType();
120
+ }
121
+
122
+ // const data = await this.callIsConnected(() => this.endpointApi.addNews(payload));
123
+ // this._draftData.id = data.object.id;
124
+ // // TODO : voir si j'ai ce qui faut dans reponseData de ADD_NEWS pour mettre à jour #serverData
125
+ // // c'est dans reponseData.object
126
+ // // if(data?.object){
127
+ // // this._serverData = { ...data.object };
128
+ // // }
129
+
130
+ for (const [constant, methodName] of Array.from(News.ADD_BLOCKS)) {
131
+ const blockData = this._extractChangedFieldsFromSchema(
132
+ this.apiClient,
133
+ constant,
134
+ { ...payload, ...this.defaultFields },
135
+ () => {},
136
+ []
137
+ );
138
+ if (blockData && Object.keys(blockData).length > 0) {
139
+ const data = await this._invokeBlockMethod(News.ADD_BLOCKS, methodName, blockData);
140
+ if (!this.id && data?.object?.id) {
141
+ this._draftData.id = data.object.id;
142
+ // this._serverData = { ...data.object };
143
+ }
144
+ }
145
+ }
146
+ };
147
+
148
+ override _update = async (payload: Record<string, any>): Promise<boolean> => {
149
+ if (!this._calledFromSave) {
150
+ throw new Error("utilisation invalide de _update, utilisez save");
151
+ }
152
+
153
+ if (payload.id) delete payload.id;
154
+
155
+ // si le texte est vide, on met un espace pour que le champ soit pris en compte car sur le serveur
156
+ // il y a une vérification de la taille du texte je pense
157
+ if(payload?.text === ""){
158
+ payload.text = " ";
159
+ }
160
+
161
+ if (this.parent) {
162
+ payload.parentId = this.parent.id;
163
+ payload.parentType = this.parent.getEntityType();
164
+ }
165
+ payload.idNews = this.id;
166
+
167
+ let hasChanged = false;
168
+ await this.callIsConnected(() => this.endpointApi.updateNews(payload as UpdateNewsData)) as any;
169
+ // TODO : voir si j'ai ce qui faut dans data de UPDATE_NEWS pour mettre à jour #serverData
170
+ // c'est dans data.object
171
+ // if(data?.object){
172
+ // this._serverData = { ...data.object };
173
+ // }
174
+ hasChanged = true;
175
+ return hasChanged;
176
+ };
177
+
178
+ async addNews(data: Partial<AddNewsData> = {}): Promise<unknown> {
179
+ return this.callIsConnected(() => this.endpointApi.addNews(data as AddNewsData));
180
+ }
181
+
182
+ async updateNews(data: Partial<UpdateNewsData> = {}): Promise<unknown> {
183
+ return this.callIsConnected(() => this.endpointApi.updateNews(data as UpdateNewsData));
184
+ }
185
+
186
+ async addMention({ slug, id }: { slug?: string; id?: string; }): Promise<any[]> {
187
+ try {
188
+ if (!slug && !id) {
189
+ throw new ApiError("Vous devez fournir un slug ou un id pour ajouter une mention.", 400);
190
+ }
191
+
192
+ const userInstance = await this.entity("citoyens", { id, slug });
193
+ // const userInstance = new this.deps.User(this.apiClient, { id, slug }, { EndpointApi: this.endpointApi });
194
+ const user = userInstance.serverData;
195
+
196
+ if (!this._draftData.mentions) {
197
+ this._draftData.mentions = [];
198
+ }
199
+ // Vérification si la mention existe déjà
200
+ if (this._draftData.mentions.find((mention: any) => mention.id === user.id)) {
201
+ // Si la mention existe déjà, on incrémente le compteur
202
+ this._draftData.mentions = this._draftData.mentions.map((mention: any) => {
203
+ if (mention.id === user.id) {
204
+ mention.count += 1;
205
+ }
206
+ return mention;
207
+ });
208
+ return this._draftData.mentions;
209
+ }
210
+ const mention = { id: user.id, slug: user.slug, type: userInstance.getEntityType(), name: user.name, value: user.name, count: 1 };
211
+ this._draftData.mentions.push(mention);
212
+ return this._draftData.mentions;
213
+ } catch (error) {
214
+ this.apiClient._logger.error("Erreur lors de l'ajout de la mention :", error);
215
+ throw error;
216
+ }
217
+
218
+ }
219
+
220
+
221
+ /**
222
+ * Ajouter une image à une actualité : Ajoute une images à une actualité.
223
+ * Constant : ADD_IMAGE_NEWS
224
+ */
225
+ async addImage(image: File | Blob | Buffer | import("stream").Readable): Promise<{ id: string; [key: string]: any }> {
226
+ const validatedImage = await this._validateImage(image);
227
+ const data: AddImageNewsData = {
228
+ pathParams: {
229
+ folder: this.parent?.getEntityType() as "citoyens" | "projects" | "organizations",
230
+ ownerId: this.parent?.id as string
231
+ },
232
+ newsImage: validatedImage as any
233
+ };
234
+ const dataImage = await this.callIsConnected(() => this.endpointApi.addImageNews(data)) as { id: string; [key: string]: any };
235
+ if (this._draftData.mediaImg) {
236
+ this._draftData.mediaImg.countImages = this._draftData.mediaImg.countImages + 1;
237
+ this._draftData.mediaImg.images.push(dataImage.id);
238
+ } else {
239
+ this._draftData.mediaImg = { countImages: 1, images: [dataImage.id] };
240
+ }
241
+ return dataImage;
242
+ }
243
+
244
+ /**
245
+ * Ajouter un fichier à une actualité : Ajoute un fichier à une actualité.
246
+ * Constant : ADD_FILE_NEWS
247
+ */
248
+ async addFile(file: File | Blob | Buffer | import("stream").Readable): Promise<unknown> {
249
+ const validatedFile = await this._validateFile(file);
250
+ const data: AddFileNewsData = {
251
+ pathParams: {
252
+ folder: this.parent?.getEntityType() as "citoyens" | "projects" | "organizations",
253
+ ownerId: this.parent?.id as string
254
+ },
255
+ newsFile: validatedFile as any
256
+ };
257
+ const dataFile = await this.callIsConnected(() => this.endpointApi.addFileNews(data));
258
+ if (this._draftData.mediaFile) {
259
+ this._draftData.mediaFile.countFiles = this._draftData.mediaFile.countFiles + 1;
260
+ this._draftData.mediaFile.files.push(dataFile);
261
+ } else {
262
+ this._draftData.mediaFile = { countFiles: 1, files: [dataFile] };
263
+ }
264
+ return dataFile;
265
+ }
266
+
267
+ /**
268
+ * Supprimer une actualité : Supprime une actualité existante.
269
+ * Constant : DELETE_NEWS
270
+ */
271
+ async delete(): Promise<void> {
272
+ if(!this.id) {
273
+ throw new ApiError("Vous devez fournir un id pour supprimer une news.", 400);
274
+ }
275
+ const data = {
276
+ pathParams: { id: this.id },
277
+ isLive: false // Valeur par défaut, peut être surchargée
278
+ };
279
+ await this.callIsConnected(() => this.endpointApi.deleteNews(data as DeleteNewsData));
280
+
281
+ // Vider les objets réactifs sans casser la réactivité
282
+ Object.keys(this._draftData).forEach(key => delete this._draftData[key]);
283
+ Object.keys(this._serverData).forEach(key => delete (this._serverData as any)[key]);
284
+
285
+ // Marquer comme supprimé
286
+ this._isDeleted = true;
287
+ }
288
+
289
+ /**
290
+ * Créer une instance de commentaire pour cette news.
291
+ * @param commentData - Données du commentaire.
292
+ * @returns Instance de Comment.
293
+ */
294
+ async comment(commentData: Record<string, any> = {}): Promise<Comment> {
295
+ if(!this.isConnected){
296
+ throw new ApiError("Vous devez être connecté.", 401);
297
+ }
298
+
299
+ const entity = await this.entity("comments", commentData);
300
+ return entity as Comment;
301
+ }
302
+
303
+ /**
304
+ * Récupérer les commentaires : Récupère la liste de commentaires selon plusieurs critères.
305
+ * Constant : GET_COMMENTS
306
+ * @returns - Les données de réponse.
307
+ * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse.
308
+ * @throws {Error} - En cas d'erreur inattendue.
309
+ */
310
+ async getComments(): Promise<Comment[]> {
311
+
312
+ if (!this.id) {
313
+ throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
314
+ }
315
+
316
+ const payload: GetCommentsData = {
317
+ pathParams: { type: "news", id: this.id },
318
+ };
319
+
320
+ const arrayObjet = await this.endpointApi.getComments(payload);
321
+
322
+ if(!Array.isArray(arrayObjet)){
323
+ throw new ApiResponseError("Erreur lors de la récupération des commentaires.", 500, arrayObjet as object);
324
+ }
325
+
326
+ const rawList = this._linkEntities(arrayObjet);
327
+
328
+ return this._createFilteredProxy(rawList);
329
+ }
330
+
331
+ }