@communecter/cocolight-api-client 1.0.8 → 1.0.10

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.
@@ -0,0 +1,327 @@
1
+ import { ApiError } from "../error.js";
2
+ import { DraftStateMixin } from "../mixin/DraftStateMixin.js";
3
+ import { UtilMixin } from "../mixin/UtilMixin.js";
4
+
5
+ // News.js
6
+ export class News {
7
+ #draftData = {};
8
+ #initialDraftData = {};
9
+ #serverData = null;
10
+
11
+ static entityType = "news";
12
+
13
+ static SCHEMA_CONSTANTS = [
14
+ "ADD_NEWS"
15
+ ];
16
+
17
+ /**
18
+ * Crée une instance de News.
19
+ * @param {(Organization|Project|User)} parent - L'instance de l'element parent (ex: Organization, Project, User).
20
+ * @param {Object} data - Objet contenant { id }
21
+ * @param {Object} deps - Dépendances injectées (ex: User).
22
+ * @param {EndpointApi} deps.EndpointApi - Classe EndpointApi pour éviter les dépendances circulaires.
23
+ * @param {User} deps.User - Classe User pour éviter les dépendances circulaires.
24
+ */
25
+
26
+ constructor(parent, data = {}, deps = {}) {
27
+ this.__entityTag = "News";
28
+
29
+ if (!deps.EndpointApi) throw new ApiError("EndpointApi class must be injected to avoid circular dependency.");
30
+ if (!deps.User) throw new ApiError("User class must be injected.");
31
+
32
+ if (!parent) throw new ApiError("Parent is required.");
33
+ this.deps = deps;
34
+ this.EndpointApiClass = deps.EndpointApi;
35
+
36
+ if (parent?.__entityTag === "User" || parent?.__entityTag === "Organization" || parent?.__entityTag === "Project") {
37
+ this.apiClient = parent.apiClient;
38
+ this.parent = parent;
39
+ } else {
40
+ throw new ApiError("Invalid parent for News.");
41
+ }
42
+
43
+ this.endpointApi = typeof deps.EndpointApi === "function"
44
+ ? new deps.EndpointApi(this.apiClient)
45
+ : (typeof deps.EndpointApi === "object"
46
+ ? deps.EndpointApi
47
+ : (() => { throw new ApiError("deps.EndpointApi must be a class or instance."); })());
48
+
49
+
50
+ this.#serverData = null;
51
+
52
+ const { draft, proxy } = this.buildDraftAndProxy({
53
+ data: { ...data, ...this.defaultFields },
54
+ serverData: this.#serverData,
55
+ constant: News.SCHEMA_CONSTANTS,
56
+ apiClient: this.apiClient,
57
+ transforms: this.transforms,
58
+ removeFields: this.removeFields
59
+ });
60
+
61
+ this.#initialDraftData = JSON.parse(JSON.stringify(draft)); // snapshot propre
62
+ this.#draftData = draft;
63
+ this.data = proxy;
64
+
65
+ }
66
+
67
+
68
+ // Getters en lecture seule pour chaque propriété
69
+ get id() {
70
+ return this.#draftData.id || null;
71
+ }
72
+
73
+ get isConnected() {
74
+ return this.apiClient.isConnected;
75
+ }
76
+
77
+ get draftData() {
78
+ return this.#draftData;
79
+ }
80
+
81
+ get initialDraftData() {
82
+ return this.#initialDraftData;
83
+ }
84
+
85
+ get serverData() {
86
+ return this.#serverData;
87
+ }
88
+
89
+ get userId() {
90
+ return this.apiClient.userId;
91
+ }
92
+
93
+ get isMe() {
94
+ return this.isConnected && this.userId === this.parent.id;
95
+ }
96
+
97
+ _setData(newData) {
98
+ this.#serverData = { ...newData };
99
+
100
+ const { draft, proxy } = this.buildDraftAndProxy({
101
+ data: { ...newData, ...this.defaultFields },
102
+ serverData: this.#serverData,
103
+ constant: News.SCHEMA_CONSTANTS,
104
+ apiClient: this.apiClient,
105
+ transforms: this.transforms,
106
+ removeFields: this.removeFields
107
+ });
108
+ this.#initialDraftData = JSON.parse(JSON.stringify(draft));
109
+ this.#draftData = draft;
110
+ this.data = proxy;
111
+ }
112
+
113
+ getEntityType() {
114
+ return News.entityType;
115
+ }
116
+
117
+ static fromServerData(data, parent, deps) {
118
+ const instance = new News(parent, {}, deps);
119
+ instance.#serverData = { ...data };
120
+
121
+ const { draft, proxy } = instance.buildDraftAndProxy({
122
+ data: { ...data, ...instance.defaultFields },
123
+ serverData: instance.#serverData,
124
+ constant: News.SCHEMA_CONSTANTS,
125
+ apiClient: instance.apiClient,
126
+ transforms: instance.transforms,
127
+ removeFields: instance.removeFields
128
+ });
129
+
130
+ instance.#draftData = draft;
131
+ instance.data = proxy;
132
+
133
+ return instance;
134
+ }
135
+
136
+ async refresh() {
137
+ if (!this.id) throw new ApiError("Impossible de rafraîchir sans ID.");
138
+ return this.get();
139
+ }
140
+
141
+ /**
142
+ * Récupérer des actualités par IDs : Récupère des actualités à partir d’une liste d’identifiants.
143
+ * Constant : GET_NEWS_BY_ID
144
+ */
145
+ async get() {
146
+ if (!this.id) throw new ApiError("Impossible de rafraîchir sans ID.");
147
+
148
+ const newsArray = await this.callIsConnected(() =>
149
+ this.endpointApi.getNewsById({ ids: [this.id] })
150
+ );
151
+
152
+ if (newsArray && Array.isArray(newsArray) && newsArray.length === 1) {
153
+ const data = newsArray[0];
154
+ this.#serverData = { ...data };
155
+
156
+ this._setData(data);
157
+
158
+ return this.#serverData;
159
+ }
160
+ throw new ApiError(`Aucune actualité trouvée pour l'ID ${this.id}`);
161
+ }
162
+
163
+
164
+ /**
165
+ * Ajouter une actualité : Ajoute une nouvelle actualité.
166
+ * Constant : ADD_NEWS | UPDATE_NEWS
167
+ */
168
+ async save() {
169
+
170
+ if(!this.isConnected){
171
+ throw new ApiError("Impossible de sauvegarder sans être connecté.");
172
+ }
173
+
174
+ // si le texte est vide, on met un espace pour que le champ soit pris en compte car sur le serveur
175
+ // il y a une vérification de la taille du texte je pense
176
+ if(this.#draftData?.text === ""){
177
+ this.#draftData.text = " ";
178
+ }
179
+
180
+ const payload = { ...this.#draftData };
181
+ payload.parentId = this.parent.id;
182
+ payload.parentType = this.parent.getEntityType();
183
+
184
+ if (!this.id) {
185
+ const reponseData = await this.callIsConnected(() => this.endpointApi.addNews(payload));
186
+ this.#draftData.id = reponseData.object.id;
187
+ // TODO : voir si j'ai ce qui faut dans reponseData de ADD_NEWS pour mettre à jour #serverData
188
+ // c'est dans reponseData.object
189
+ if(reponseData?.object){
190
+ this.#serverData = { ...reponseData.object };
191
+ }
192
+ // await this.refresh();
193
+ return reponseData;
194
+ } else {
195
+ if(payload.id){
196
+ delete payload.id;
197
+ }
198
+ const reponseData = await this.callIsConnected(() => this.endpointApi.updateNews({ ...payload, idNews: this.id }));
199
+ // TODO : voir si j'ai ce qui faut dans reponseData de UPDATE_NEWS pour mettre à jour #serverData
200
+ // c'est dans reponseData.object
201
+ if(reponseData?.object){
202
+ this.#serverData = { ...reponseData.object };
203
+ }
204
+ // await this.refresh();
205
+ return reponseData;
206
+ }
207
+ }
208
+
209
+
210
+ async addMention({ slug, id }) {
211
+ try {
212
+ if (!slug && !id) {
213
+ throw new ApiError("Vous devez fournir un slug ou un id pour ajouter une mention.");
214
+ }
215
+ const userInstance = new this.deps.User(this.apiClient, { id, slug }, { EndpointApi: this.endpointApi });
216
+ const user = userInstance.serverData;
217
+ if (!this.#draftData.mentions) {
218
+ this.#draftData.mentions = [];
219
+ }
220
+ // Vérification si la mention existe déjà
221
+ if (this.#draftData.mentions.find((mention) => mention.id === user.id)) {
222
+ // Si la mention existe déjà, on incrémente le compteur
223
+ this.#draftData.mentions = this.#draftData.mentions.map((mention) => {
224
+ if (mention.id === user.id) {
225
+ mention.count += 1;
226
+ }
227
+ return mention;
228
+ });
229
+ return this.#draftData.mentions;
230
+ }
231
+ const mention = { id: user.id, slug: user.slug, type: userInstance.getEntityType(), name: user.name, value: user.name, count: 1 };
232
+ this.#draftData.mentions.push(mention);
233
+ return this.#draftData.mentions;
234
+ } catch (error) {
235
+ this.apiClient._logger.error("Erreur lors de l'ajout de la mention :", error);
236
+ throw error;
237
+ }
238
+
239
+ }
240
+
241
+
242
+ /**
243
+ * Ajouter une image à une actualité : Ajoute une images à une actualité.
244
+ * Constant : ADD_IMAGE_NEWS
245
+ */
246
+ async addImage(image) {
247
+ image = await this.validateImage(image);
248
+ const data = { pathParams: { folder: this.parent.getEntityType(), ownerId: this.parent.id }, newsImage: image };
249
+ const dataImage = await this.callIsConnected(() => this.endpointApi.addImageNews(data));
250
+ if (this.#draftData.mediaImg) {
251
+ this.#draftData.mediaImg.countImages = this.#draftData.mediaImg.countImages + 1;
252
+ this.#draftData.mediaImg.images.push(dataImage.id);
253
+ } else {
254
+ this.#draftData.mediaImg = { countImages: 1, images: [dataImage.id] };
255
+ }
256
+ return dataImage;
257
+ }
258
+
259
+ /**
260
+ * Ajouter un fichier à une actualité : Ajoute un fichier à une actualité.
261
+ * Constant : ADD_FILE_NEWS
262
+ */
263
+ async addFile(file) {
264
+ file = await this.validateFile(file);
265
+ const data = { pathParams: { folder: this.parent.getEntityType(), ownerId: this.parent.id }, newsFile: file };
266
+ const dataFile = await this.callIsConnected(() =>this.endpointApi.addFileNews(data));
267
+ if (this.#draftData.mediaFile) {
268
+ this.#draftData.mediaFile.countFiles = this.#draftData.mediaFile.countFiles + 1;
269
+ this.#draftData.mediaFile.files.push(dataFile);
270
+ } else {
271
+ this.#draftData.mediaFile = { countFiles: 1, files: [dataFile] };
272
+ }
273
+ return dataFile;
274
+ }
275
+
276
+ /**
277
+ * Supprimer une actualité : Supprime une actualité existante.
278
+ * Constant : DELETE_NEWS
279
+ */
280
+ async delete() {
281
+ if(!this.id) {
282
+ throw new ApiError("Vous devez fournir un id pour supprimer une news.");
283
+ }
284
+ const data = { pathParams: { id: this.id } };
285
+ await this.callIsConnected(() => this.endpointApi.deleteNews(data));
286
+ this.#draftData = {};
287
+ this.#serverData = null;
288
+ this.#draftData.id = null;
289
+ this._isDeleted = true;
290
+ }
291
+
292
+ _updateInitialDraftSnapshot() {
293
+ this.#initialDraftData = JSON.parse(JSON.stringify(this.#draftData));
294
+ }
295
+
296
+ hasChanges() {
297
+ return JSON.stringify(this.#draftData) !== JSON.stringify(this.#initialDraftData);
298
+ }
299
+
300
+ defaultFields = {
301
+
302
+ };
303
+
304
+ removeFields = [
305
+
306
+ ];
307
+
308
+ transforms = {
309
+ scope: val => val?.type,
310
+ mentions: val =>
311
+ Array.isArray(val)
312
+ ? val.map(m => ({ ...m, count: parseInt(m.count) }))
313
+ : [],
314
+ mediaImg: val => {
315
+ const images = val?.images?.map(img => img.id).filter(Boolean) || [];
316
+ return images.length > 0 ? { countImages: images.length, images } : undefined;
317
+ },
318
+ mediaFile: val => {
319
+ const files = val?.files?.map(f => f.id).filter(Boolean) || [];
320
+ return files.length > 0 ? { countFiles: files.length, files } : undefined;
321
+ }
322
+ };
323
+
324
+ }
325
+
326
+ // Incorporation du mixin dans Organization
327
+ Object.assign(News.prototype, UtilMixin, DraftStateMixin);