@communecter/cocolight-api-client 1.0.9 → 1.0.11

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/src/api/News.js CHANGED
@@ -1,51 +1,70 @@
1
1
  import { ApiError } from "../error.js";
2
- import { NewsMixin } from "../mixin/NewsMixin.js";
2
+ import { DraftStateMixin } from "../mixin/DraftStateMixin.js";
3
3
  import { UtilMixin } from "../mixin/UtilMixin.js";
4
4
 
5
5
  // News.js
6
6
  export class News {
7
- // Champs privés pour protéger l'état
8
7
  #draftData = {};
9
- // Data d'une news venant du serveur
8
+ #initialDraftData = {};
10
9
  #serverData = null;
11
10
 
11
+ static entityType = "news";
12
+
13
+ static SCHEMA_CONSTANTS = [
14
+ "ADD_NEWS"
15
+ ];
16
+
12
17
  /**
13
18
  * Crée une instance de News.
14
19
  * @param {(Organization|Project|User)} parent - L'instance de l'element parent (ex: Organization, Project, User).
15
- * @param {Object} identifier - Objet contenant { id }
20
+ * @param {Object} data - Objet contenant { id }
16
21
  * @param {Object} deps - Dépendances injectées (ex: User).
17
22
  * @param {EndpointApi} deps.EndpointApi - Classe EndpointApi pour éviter les dépendances circulaires.
18
23
  * @param {User} deps.User - Classe User pour éviter les dépendances circulaires.
19
24
  */
20
25
 
21
26
  constructor(parent, data = {}, deps = {}) {
22
- if(!deps.EndpointApi){
23
- throw new ApiError("EndpointApi class must be injected to avoid circular dependency.");
24
- }
25
- if (!deps.User) {
26
- throw new ApiError("User class must be injected to avoid circular dependency.");
27
- }
28
- if (!parent) {
29
- throw new ApiError("Parent is required.");
30
- }
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.");
31
33
  this.deps = deps;
32
- this.UserClass = deps.User;
33
- this.apiClient = parent.apiClient;
34
-
35
- // Gérer les deux cas : fonction constructeur ou instance
36
- if (typeof deps.EndpointApi === "function") {
37
- this.endpointApi = new deps.EndpointApi(this.apiClient);
38
- } else if (typeof deps.EndpointApi === "object") {
39
- this.endpointApi = deps.EndpointApi;
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;
40
39
  } else {
41
- throw new ApiError("deps.EndpointApi doit être une classe ou une instance valide.");
40
+ throw new ApiError("Invalid parent for News.");
42
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;
43
51
 
44
- this.#draftData = { ...data };
45
- this.parent = parent;
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;
46
64
 
47
65
  }
48
66
 
67
+
49
68
  // Getters en lecture seule pour chaque propriété
50
69
  get id() {
51
70
  return this.#draftData.id || null;
@@ -58,6 +77,10 @@ export class News {
58
77
  get draftData() {
59
78
  return this.#draftData;
60
79
  }
80
+
81
+ get initialDraftData() {
82
+ return this.#initialDraftData;
83
+ }
61
84
 
62
85
  get serverData() {
63
86
  return this.#serverData;
@@ -71,18 +94,41 @@ export class News {
71
94
  return this.isConnected && this.userId === this.parent.id;
72
95
  }
73
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
+
74
113
  getEntityType() {
75
- return "news";
114
+ return News.entityType;
76
115
  }
77
116
 
78
117
  static fromServerData(data, parent, deps) {
79
- const instance = new News(parent, { id: data.id }, deps);
80
-
81
- // Injecte les données serveur
118
+ const instance = new News(parent, {}, deps);
82
119
  instance.#serverData = { ...data };
83
120
 
84
- // Construit les données draft à partir du serverData
85
- instance.#draftData = instance._getDraftFromServerData(data);
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;
86
132
 
87
133
  return instance;
88
134
  }
@@ -97,64 +143,23 @@ export class News {
97
143
  * Constant : GET_NEWS_BY_ID
98
144
  */
99
145
  async get() {
100
- if(!this.id) {
101
- throw new ApiError("Vous devez fournir un id pour récupérer une news.");
102
- }
103
- const newsArray = await this.callIsConnected(() => this.endpointApi.getNewsById({ ids: [this.id] }));
104
- if (newsArray && newsArray.length > 0) {
105
- this.#serverData = newsArray[0];
106
- this.#draftData = this._getDraftFromServerData(this.#serverData);
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
+
107
158
  return this.#serverData;
108
159
  }
109
160
  throw new ApiError(`Aucune actualité trouvée pour l'ID ${this.id}`);
110
161
  }
111
162
 
112
- _getDraftFromServerData(data) {
113
- // je veux copier que certaine info si présente dans le draftData
114
- // - newsArray[0].scope.type > draftData.scope
115
- // - newsArray[0].text > draftData.text
116
- // - newsArray[0].tags > draftData.tags
117
- // - newsArray[0].mentions > draftData.mentions > draftData.mentions.count integer
118
- // - newsArray[0].mediaImg > draftData.mediaImg > draftData.mediaImg.images > un array id
119
- // - newsArray[0].mediaFile > draftData.mediaFile > draftData.mediaFile.files > un array id
120
- const transformed = this._pickProps(
121
- data,
122
- ["scope", "text", "tags", "mentions", "mediaImg", "mediaFile"],
123
- {
124
- scope: (val) => val?.type,
125
- mentions: (val) =>
126
- Array.isArray(val)
127
- ? val.map((mention) => ({
128
- ...mention,
129
- count: parseInt(mention.count)
130
- }))
131
- : [],
132
- mediaImg: (val) => {
133
- const images = val?.images?.map((img) => img.id).filter(Boolean) || [];
134
- return images.length > 0
135
- ? {
136
- countImages: parseInt(val.countImages) || images.length,
137
- images
138
- }
139
- : undefined;
140
- },
141
- mediaFile: (val) => {
142
- const files = val?.files?.map((file) => file.id).filter(Boolean) || [];
143
- return files.length > 0
144
- ? {
145
- countFiles: parseInt(val.countFiles) || files.length,
146
- files
147
- }
148
- : undefined;
149
- }
150
- }
151
- );
152
-
153
- return {
154
- id: data.id,
155
- ...Object.fromEntries(Object.entries(transformed).filter(([, v]) => v !== undefined))
156
- };
157
- }
158
163
 
159
164
  /**
160
165
  * Ajouter une actualité : Ajoute une nouvelle actualité.
@@ -162,6 +167,10 @@ export class News {
162
167
  */
163
168
  async save() {
164
169
 
170
+ if(!this.isConnected){
171
+ throw new ApiError("Impossible de sauvegarder sans être connecté.");
172
+ }
173
+
165
174
  // si le texte est vide, on met un espace pour que le champ soit pris en compte car sur le serveur
166
175
  // il y a une vérification de la taille du texte je pense
167
176
  if(this.#draftData?.text === ""){
@@ -203,8 +212,8 @@ export class News {
203
212
  if (!slug && !id) {
204
213
  throw new ApiError("Vous devez fournir un slug ou un id pour ajouter une mention.");
205
214
  }
206
- const userInstance = new this.UserClass(this.apiClient, { id, slug }, null , { EndpointApi: this.endpointApi });
207
- const user = await userInstance.getProfil();
215
+ const userInstance = new this.deps.User(this.apiClient, { id, slug }, { EndpointApi: this.endpointApi });
216
+ const user = userInstance.serverData;
208
217
  if (!this.#draftData.mentions) {
209
218
  this.#draftData.mentions = [];
210
219
  }
@@ -229,6 +238,7 @@ export class News {
229
238
 
230
239
  }
231
240
 
241
+
232
242
  /**
233
243
  * Ajouter une image à une actualité : Ajoute une images à une actualité.
234
244
  * Constant : ADD_IMAGE_NEWS
@@ -278,9 +288,40 @@ export class News {
278
288
  this.#draftData.id = null;
279
289
  this._isDeleted = true;
280
290
  }
281
-
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
+
282
324
  }
283
325
 
284
326
  // Incorporation du mixin dans Organization
285
- Object.assign(News.prototype, UtilMixin, NewsMixin);
286
-
327
+ Object.assign(News.prototype, UtilMixin, DraftStateMixin);