@communecter/cocolight-api-client 1.0.126 → 1.0.128
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/Api.ts +6 -4
- package/src/ApiClient.ts +1 -1
- package/src/api/Action.ts +76 -0
- package/src/api/BaseEntity.ts +43 -22
- package/src/api/EndpointApi.types.ts +81 -9
- package/src/api/EntityRegistry.ts +16 -8
- package/src/api/Project.ts +17 -1
- package/src/api/User.ts +1 -0
- package/src/api/UserApi.ts +3 -2
- package/src/api/serverDataType/Action.ts +177 -0
- package/src/endpoints.module.ts +118 -114
- package/src/index.ts +3 -0
- package/src/types/entities.ts +3 -2
- package/types/api/Action.d.ts +27 -0
- package/types/api/BaseEntity.d.ts +21 -4
- package/types/api/EndpointApi.types.d.ts +64 -8
- package/types/api/EntityRegistry.d.ts +1 -1
- package/types/api/Project.d.ts +7 -0
- package/types/api/User.d.ts +1 -0
- package/types/api/serverDataType/Action.d.ts +150 -0
- package/types/endpoints.module.d.ts +738 -6
- package/types/index.d.ts +3 -0
- package/types/types/entities.d.ts +3 -2
package/package.json
CHANGED
package/src/Api.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Api.ts
|
|
2
|
+
import { Action } from "./api/Action.js";
|
|
2
3
|
import { Answer } from "./api/Answer.js";
|
|
3
4
|
import { Badge } from "./api/Badge.js";
|
|
4
5
|
import { Classified } from "./api/Classified.js";
|
|
@@ -33,6 +34,7 @@ registerEntity("Comment", Comment);
|
|
|
33
34
|
registerEntity("Answer", Answer);
|
|
34
35
|
registerEntity("Form", Form);
|
|
35
36
|
registerEntity("Classified", Classified);
|
|
37
|
+
registerEntity("Action", Action);
|
|
36
38
|
|
|
37
39
|
|
|
38
40
|
|
|
@@ -130,9 +132,9 @@ export default class Api {
|
|
|
130
132
|
async user(userData: EntityData): Promise<User> {
|
|
131
133
|
try {
|
|
132
134
|
if (!userData.id && !userData.slug) {
|
|
133
|
-
return new User(this._client, userData, { EndpointApi, Organization, Project, Event, Poi, Badge, News, Comment, Answer, Classified });
|
|
135
|
+
return new User(this._client, userData, { EndpointApi, Organization, Project, Event, Poi, Badge, News, Comment, Answer, Classified, Action });
|
|
134
136
|
} else {
|
|
135
|
-
const user = new User(this._client, userData, { EndpointApi, Organization, Project, Event, Poi, Badge, News, Comment, Answer, Classified });
|
|
137
|
+
const user = new User(this._client, userData, { EndpointApi, Organization, Project, Event, Poi, Badge, News, Comment, Answer, Classified, Action });
|
|
136
138
|
await user.get();
|
|
137
139
|
return user;
|
|
138
140
|
}
|
|
@@ -148,7 +150,7 @@ export default class Api {
|
|
|
148
150
|
*/
|
|
149
151
|
async organization(organizationData: EntityData): Promise<Organization> {
|
|
150
152
|
try {
|
|
151
|
-
const organization = new Organization(this._client, organizationData, { EndpointApi, User, Project, Event, Poi, Badge, News, Comment, Answer, Classified });
|
|
153
|
+
const organization = new Organization(this._client, organizationData, { EndpointApi, User, Project, Event, Poi, Badge, News, Comment, Answer, Classified, Action });
|
|
152
154
|
if (!organizationData.id && !organizationData.slug) {
|
|
153
155
|
throw new Error("Vous devez fournir un id ou un slug pour créer une instance Organization.");
|
|
154
156
|
}
|
|
@@ -165,7 +167,7 @@ export default class Api {
|
|
|
165
167
|
*/
|
|
166
168
|
async project(projectData: EntityData): Promise<Project> {
|
|
167
169
|
try {
|
|
168
|
-
const project = new Project(this._client, projectData, { EndpointApi, User, Organization, Event, Poi, Badge, News, Comment, Classified });
|
|
170
|
+
const project = new Project(this._client, projectData, { EndpointApi, User, Organization, Event, Poi, Badge, News, Comment, Classified, Action });
|
|
169
171
|
if (!projectData.id && !projectData.slug) {
|
|
170
172
|
throw new Error("Vous devez fournir un id ou un slug pour créer une instance Project.");
|
|
171
173
|
}
|
package/src/ApiClient.ts
CHANGED
|
@@ -1659,7 +1659,7 @@ export default class ApiClient extends EventEmitter {
|
|
|
1659
1659
|
* via x-www-form-urlencoded ; sans cette coercion, JSON.stringify diff
|
|
1660
1660
|
* produit des faux positifs dans `hasChanges()`).
|
|
1661
1661
|
*/
|
|
1662
|
-
private _numberFields = ["buildingSurfaceArea", "siteSurfaceArea"];
|
|
1662
|
+
private _numberFields = ["buildingSurfaceArea", "siteSurfaceArea", "credits", "min", "max", "timeSpent"];
|
|
1663
1663
|
|
|
1664
1664
|
/**
|
|
1665
1665
|
* Normalise récursivement un objet, un tableau ou une valeur simple.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ApiError } from "../error.js";
|
|
2
|
+
import { BaseEntity } from "./BaseEntity.js";
|
|
3
|
+
|
|
4
|
+
import type { CostumProjectActionRequestNewData } from "./EndpointApi.types.js";
|
|
5
|
+
import type { ActionItemNormalized } from "./serverDataType/Action.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Entité Action — tâche/jalon attaché à un Project.
|
|
9
|
+
*
|
|
10
|
+
* Toujours liée à un Project parent (parentType="projects" est const côté backend).
|
|
11
|
+
* S'instancie via `project.action(...)` plutôt que directement.
|
|
12
|
+
*
|
|
13
|
+
* Endpoints utilisés :
|
|
14
|
+
* - `get()` : hérité (GET_ELEMENTS_ABOUT via `/co2/element/about/type/actions/id/{id}`)
|
|
15
|
+
* - `_add()` : COSTUM_PROJECT_ACTION_REQUEST_NEW (parentId/parentType injectés depuis le parent)
|
|
16
|
+
*/
|
|
17
|
+
export class Action extends BaseEntity<ActionItemNormalized> {
|
|
18
|
+
static override entityType = "actions";
|
|
19
|
+
|
|
20
|
+
static override entityTag = "Action";
|
|
21
|
+
|
|
22
|
+
static override SCHEMA_CONSTANTS: string[] = [
|
|
23
|
+
"COSTUM_PROJECT_ACTION_REQUEST_NEW"
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
static ADD_BLOCKS = new Map([
|
|
27
|
+
["COSTUM_PROJECT_ACTION_REQUEST_NEW", "addAction"]
|
|
28
|
+
] as const);
|
|
29
|
+
|
|
30
|
+
override defaultFields: Record<string, any> = {
|
|
31
|
+
parentType: "projects"
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
override removeFields: string[] = [];
|
|
35
|
+
|
|
36
|
+
override _add = async (payload: Record<string, any>): Promise<void> => {
|
|
37
|
+
if (!this._calledFromSave) {
|
|
38
|
+
throw new ApiError("utilisation invalide de _add, utilisez save", 400);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!this.parent?.id) {
|
|
42
|
+
throw new ApiError("Le parent (Project) doit être défini pour créer une action.", 400);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.parent.getEntityType() !== "projects") {
|
|
46
|
+
throw new ApiError(`Une Action ne peut être créée que depuis un Project (parent reçu: ${this.parent.getEntityType()}).`, 400);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
payload.parentId = this.parent.id;
|
|
50
|
+
|
|
51
|
+
for (const [constant, methodName] of Array.from(Action.ADD_BLOCKS)) {
|
|
52
|
+
const blockData = this._extractChangedFieldsFromSchema(
|
|
53
|
+
this.apiClient,
|
|
54
|
+
constant,
|
|
55
|
+
{ ...payload, ...this.defaultFields },
|
|
56
|
+
() => {}
|
|
57
|
+
);
|
|
58
|
+
if (blockData && Object.keys(blockData).length > 0) {
|
|
59
|
+
const data = await this._invokeBlockMethod(Action.ADD_BLOCKS, methodName, blockData);
|
|
60
|
+
const content = data?.content;
|
|
61
|
+
if (!this.id && content?.id) {
|
|
62
|
+
this._draftData.id = content.id;
|
|
63
|
+
this._setData(content as ActionItemNormalized, { forceInitialDraftReset: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Crée une nouvelle action via /costum/project/action/request/new.
|
|
71
|
+
* Méthode de bas niveau — utiliser `save()` après modification du draft à la place.
|
|
72
|
+
*/
|
|
73
|
+
async addAction(data: Partial<CostumProjectActionRequestNewData> = {}): Promise<unknown> {
|
|
74
|
+
return this.callIsConnected(() => this.endpointApi.costumProjectActionRequestNew(data as CostumProjectActionRequestNewData));
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/api/BaseEntity.ts
CHANGED
|
@@ -77,6 +77,7 @@ type EventInput = { id: string } | { slug: string } | Record<string, any>;
|
|
|
77
77
|
type ClassifiedInput = { id: string } | Record<string, any>;
|
|
78
78
|
type BadgeInput = { id: string } | Record<string, any>;
|
|
79
79
|
type NewsInput = { id: string } | Record<string, any>;
|
|
80
|
+
type ActionInput = { id: string } | Record<string, any>;
|
|
80
81
|
|
|
81
82
|
/**
|
|
82
83
|
* On force le type de l'import comme une fabrique qui renvoie un objet
|
|
@@ -99,9 +100,10 @@ type Comment = import("./Comment.js").Comment;
|
|
|
99
100
|
type Answer = import("./Answer.js").Answer;
|
|
100
101
|
type Form = import("./Form.js").Form;
|
|
101
102
|
type Classified = import("./Classified.js").Classified;
|
|
103
|
+
type Action = import("./Action.js").Action;
|
|
102
104
|
|
|
103
105
|
// Types d'union
|
|
104
|
-
type AnyEntity = User | Organization | Project | Poi | EventEntity | Badge | News | Comment | Answer | Form | Classified;
|
|
106
|
+
type AnyEntity = User | Organization | Project | Poi | EventEntity | Badge | News | Comment | Answer | Form | Classified | Action;
|
|
105
107
|
type ParentLike = BaseEntity<any> & { apiClient: ApiClient, userContext?: User | null };
|
|
106
108
|
|
|
107
109
|
// Types pour les dépendances
|
|
@@ -120,7 +122,8 @@ interface Deps {
|
|
|
120
122
|
Comment?: any;
|
|
121
123
|
Answer?: any;
|
|
122
124
|
Form?: any;
|
|
123
|
-
Classified?: any
|
|
125
|
+
Classified?: any;
|
|
126
|
+
Action?: any;
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
interface BaseEntityConfig {
|
|
@@ -140,6 +143,7 @@ interface EntityTypeMap {
|
|
|
140
143
|
answers: Answer;
|
|
141
144
|
forms: Form;
|
|
142
145
|
classifieds: Classified;
|
|
146
|
+
actions: Action;
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
// Types pour les streams et uploads
|
|
@@ -250,7 +254,7 @@ interface LinkMeta {
|
|
|
250
254
|
// Type du constructeur de BaseEntity avec ses propriétés statiques
|
|
251
255
|
type BaseEntityCtor = typeof BaseEntity & {
|
|
252
256
|
entityTag: string;
|
|
253
|
-
entityType: "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds";
|
|
257
|
+
entityType: "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds" | "actions";
|
|
254
258
|
SCHEMA_CONSTANTS: string | string[];
|
|
255
259
|
};
|
|
256
260
|
|
|
@@ -344,7 +348,7 @@ interface FinalizerResult<TOut> {
|
|
|
344
348
|
}
|
|
345
349
|
|
|
346
350
|
// Types pour les entités
|
|
347
|
-
export type EntityType = "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds";
|
|
351
|
+
export type EntityType = "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds" | "actions";
|
|
348
352
|
|
|
349
353
|
// ============================================================================
|
|
350
354
|
|
|
@@ -525,7 +529,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
525
529
|
}
|
|
526
530
|
|
|
527
531
|
/** @returns Type de l'entité (ex: 'citoyens') */
|
|
528
|
-
getEntityType(): "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds" {
|
|
532
|
+
getEntityType(): "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds" | "actions" {
|
|
529
533
|
return this._getCtor().entityType;
|
|
530
534
|
}
|
|
531
535
|
|
|
@@ -1947,6 +1951,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1947
1951
|
Answer: selfTag === "Answer" ? selfClass : this.deps.Answer,
|
|
1948
1952
|
Form: selfTag === "Form" ? selfClass : this.deps.Form,
|
|
1949
1953
|
Classified: selfTag === "Classified" ? selfClass : this.deps.Classified,
|
|
1954
|
+
Action: selfTag === "Action" ? selfClass : this.deps.Action,
|
|
1950
1955
|
};
|
|
1951
1956
|
|
|
1952
1957
|
const map = {
|
|
@@ -1968,6 +1973,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1968
1973
|
comments: { entityClass: commonDeps.Comment, deps: { ...commonDeps } },
|
|
1969
1974
|
answers: { entityClass: commonDeps.Answer, deps: { ...commonDeps } },
|
|
1970
1975
|
forms: { entityClass: commonDeps.Form, deps: { ...commonDeps } },
|
|
1976
|
+
actions: { entityClass: commonDeps.Action, deps: { ...commonDeps } },
|
|
1971
1977
|
};
|
|
1972
1978
|
|
|
1973
1979
|
return (map as Record<string, EntityMeta | undefined>)[entityType] || null;
|
|
@@ -2200,6 +2206,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2200
2206
|
comments: [], // Pas de get() pour les commentaires
|
|
2201
2207
|
answers: ["id"],
|
|
2202
2208
|
classifieds: ["id"],
|
|
2209
|
+
actions: ["id"],
|
|
2203
2210
|
};
|
|
2204
2211
|
|
|
2205
2212
|
const fetchKeys = (fetchKeysByEntity as Record<string, string[] | undefined>)[entityType];
|
|
@@ -2622,7 +2629,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2622
2629
|
if (!userLink) {
|
|
2623
2630
|
|
|
2624
2631
|
// 1) Garde runtime pour exclure les types non pris en charge par ConnectData
|
|
2625
|
-
this._assertNotEntityType("connect", ["poi", "badges", "news"]);
|
|
2632
|
+
this._assertNotEntityType("connect", ["poi", "badges", "news", "actions"]);
|
|
2626
2633
|
|
|
2627
2634
|
// 2) Narrow de type pour TypeScript
|
|
2628
2635
|
const parentType = this.getEntityType() as ConnectData["parentType"];
|
|
@@ -2678,7 +2685,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2678
2685
|
if (!userLink) {
|
|
2679
2686
|
|
|
2680
2687
|
// 1) Garde runtime pour exclure les types non pris en charge par ConnectData
|
|
2681
|
-
this._assertNotEntityType("connect", ["poi", "badges", "news"]);
|
|
2688
|
+
this._assertNotEntityType("connect", ["poi", "badges", "news", "actions"]);
|
|
2682
2689
|
|
|
2683
2690
|
// 2) Narrow de type pour TypeScript
|
|
2684
2691
|
const parentType = this.getEntityType() as ConnectData["parentType"];
|
|
@@ -2737,7 +2744,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2737
2744
|
|
|
2738
2745
|
if (userLink && (userLink.isInviting || userLink.isAdminInviting)) {
|
|
2739
2746
|
// 1) Garde runtime pour exclure les types non pris en charge par LinkValidateData
|
|
2740
|
-
this._assertNotEntityType("linkValidate", ["poi", "badges", "news"]);
|
|
2747
|
+
this._assertNotEntityType("linkValidate", ["poi", "badges", "news", "actions"]);
|
|
2741
2748
|
|
|
2742
2749
|
// 2) Narrow de type pour TypeScript
|
|
2743
2750
|
const parentType = this.getEntityType() as LinkValidateData["parentType"];
|
|
@@ -2784,7 +2791,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2784
2791
|
}
|
|
2785
2792
|
|
|
2786
2793
|
// 1) Garde runtime pour exclure les types non pris en charge par DisconnectData
|
|
2787
|
-
this._assertNotEntityType("disconnect", ["poi", "badges", "news"]);
|
|
2794
|
+
this._assertNotEntityType("disconnect", ["poi", "badges", "news", "actions"]);
|
|
2788
2795
|
|
|
2789
2796
|
// 2) Narrow de type pour TypeScript
|
|
2790
2797
|
const parentType = this.getEntityType() as DisconnectData["parentType"];
|
|
@@ -2839,7 +2846,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2839
2846
|
}
|
|
2840
2847
|
|
|
2841
2848
|
// Réduit le type de l'entité aux seuls autorisés par UpdateSettingsData
|
|
2842
|
-
this._assertNotEntityType("updateSettings", ["poi", "badges", "news"]);
|
|
2849
|
+
this._assertNotEntityType("updateSettings", ["poi", "badges", "news", "actions"]);
|
|
2843
2850
|
const typeEntity = this.getEntityType() as UpdateSettingsData["typeEntity"];
|
|
2844
2851
|
|
|
2845
2852
|
// Garde runtime pour s'assurer que 'type' et 'value' sont bien fournis
|
|
@@ -2874,7 +2881,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2874
2881
|
}
|
|
2875
2882
|
|
|
2876
2883
|
// Réduit le type de l'entité aux seuls autorisés par UpdateBlockDescriptionData
|
|
2877
|
-
this._assertNotEntityType("updateDescription", ["badges", "news"]);
|
|
2884
|
+
this._assertNotEntityType("updateDescription", ["badges", "news", "actions"]);
|
|
2878
2885
|
const typeElement = this.getEntityType() as UpdateBlockDescriptionData["typeElement"];
|
|
2879
2886
|
|
|
2880
2887
|
const payload: UpdateBlockDescriptionData = {
|
|
@@ -2903,7 +2910,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2903
2910
|
}
|
|
2904
2911
|
|
|
2905
2912
|
// Réduit le type de l'entité aux seuls autorisés par UpdateBlockInfoData
|
|
2906
|
-
this._assertNotEntityType("updateInfo", ["badges", "news"]);
|
|
2913
|
+
this._assertNotEntityType("updateInfo", ["badges", "news", "actions"]);
|
|
2907
2914
|
const typeElement = this.getEntityType() as UpdateBlockInfoData["typeElement"];
|
|
2908
2915
|
|
|
2909
2916
|
const payload: UpdateBlockInfoData = {
|
|
@@ -2930,7 +2937,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2930
2937
|
}
|
|
2931
2938
|
|
|
2932
2939
|
// Réduit le type de l'entité aux seuls autorisés par UpdateBlockSocialData
|
|
2933
|
-
this._assertNotEntityType("updateSocial", ["badges", "news", "poi", "events"]);
|
|
2940
|
+
this._assertNotEntityType("updateSocial", ["badges", "news", "poi", "events", "actions"]);
|
|
2934
2941
|
const typeElement = this.getEntityType() as UpdateBlockSocialData["typeElement"];
|
|
2935
2942
|
|
|
2936
2943
|
const payload: UpdateBlockSocialData = {
|
|
@@ -2996,7 +3003,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2996
3003
|
}
|
|
2997
3004
|
|
|
2998
3005
|
// Réduit le type de l'entité aux seuls autorisés par UpdateBlockLocalityData
|
|
2999
|
-
this._assertNotEntityType("updateLocality", ["badges", "news", "comments", "answers"]);
|
|
3006
|
+
this._assertNotEntityType("updateLocality", ["badges", "news", "comments", "answers", "actions"]);
|
|
3000
3007
|
|
|
3001
3008
|
// Garde runtime pour s'assurer que 'address' est bien fourni
|
|
3002
3009
|
if (!("address" in data)) {
|
|
@@ -3040,7 +3047,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3040
3047
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3041
3048
|
}
|
|
3042
3049
|
|
|
3043
|
-
this._assertNotEntityType("updateSlug", ["badges", "news", "poi", "comments"]);
|
|
3050
|
+
this._assertNotEntityType("updateSlug", ["badges", "news", "poi", "comments", "actions"]);
|
|
3044
3051
|
|
|
3045
3052
|
const type = this.getEntityType() as CheckData["type"];
|
|
3046
3053
|
|
|
@@ -3071,7 +3078,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3071
3078
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3072
3079
|
}
|
|
3073
3080
|
|
|
3074
|
-
this._assertNotEntityType("updateImageProfil", ["badges", "news", "comments", "answers"]);
|
|
3081
|
+
this._assertNotEntityType("updateImageProfil", ["badges", "news", "comments", "answers", "actions"]);
|
|
3075
3082
|
|
|
3076
3083
|
const folder = this.getEntityType() as NonNullable<ProfilImageData["pathParams"]>["folder"];
|
|
3077
3084
|
|
|
@@ -3098,7 +3105,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3098
3105
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3099
3106
|
}
|
|
3100
3107
|
|
|
3101
|
-
this._assertNotEntityType("updateImageBanner", ["badges", "news", "comments", "answers"]);
|
|
3108
|
+
this._assertNotEntityType("updateImageBanner", ["badges", "news", "comments", "answers", "actions"]);
|
|
3102
3109
|
|
|
3103
3110
|
const folder = this.getEntityType() as NonNullable<ProfilBannerData["pathParams"]>["folder"];
|
|
3104
3111
|
|
|
@@ -3235,6 +3242,20 @@ export class BaseEntity<TServerData = any> {
|
|
|
3235
3242
|
return entity as News;
|
|
3236
3243
|
}
|
|
3237
3244
|
|
|
3245
|
+
/**
|
|
3246
|
+
* Crée une instance d'Action et la récupère si nécessaire.
|
|
3247
|
+
*
|
|
3248
|
+
* Par défaut une action ne peut être créée que depuis un Project — les autres
|
|
3249
|
+
* entités lèvent une erreur. Override sur Project pour activer le comportement.
|
|
3250
|
+
*
|
|
3251
|
+
* @param actionData - Les données pour initialiser l'action.
|
|
3252
|
+
* @returns Une promesse qui résout l'objet Action créé.
|
|
3253
|
+
* @throws {ApiError} Sur les entités non supportées.
|
|
3254
|
+
*/
|
|
3255
|
+
async action(_actionData: ActionInput = {}): Promise<Action> {
|
|
3256
|
+
throw new ApiError(`action n'existe pas dans ${this.constructor.name}`, 501);
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3238
3259
|
/**
|
|
3239
3260
|
* Récupérer les organisations d'une entitée : la liste des organisations dont l'entité est membre ou admin valide.
|
|
3240
3261
|
* Constant : GET_ORGANIZATIONS_ADMIN | GET_ORGANIZATIONS_NO_ADMIN
|
|
@@ -3481,7 +3502,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3481
3502
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3482
3503
|
}
|
|
3483
3504
|
|
|
3484
|
-
this._assertNotEntityType("getNews", ["badges", "news", "poi", "events", "comments", "answers"]);
|
|
3505
|
+
this._assertNotEntityType("getNews", ["badges", "news", "poi", "events", "comments", "answers", "actions"]);
|
|
3485
3506
|
|
|
3486
3507
|
const type = this.getEntityType() as NonNullable<GetNewsData["pathParams"]>["type"];
|
|
3487
3508
|
|
|
@@ -3520,7 +3541,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3520
3541
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3521
3542
|
}
|
|
3522
3543
|
|
|
3523
|
-
this._assertNotEntityType("getGallery", ["badges", "news", "poi", "events", "comments", "answers"]);
|
|
3544
|
+
this._assertNotEntityType("getGallery", ["badges", "news", "poi", "events", "comments", "answers", "actions"]);
|
|
3524
3545
|
|
|
3525
3546
|
const type = this.getEntityType() as NonNullable<GetGalleryData["pathParams"]>["type"];
|
|
3526
3547
|
|
|
@@ -3573,7 +3594,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3573
3594
|
return [];
|
|
3574
3595
|
}
|
|
3575
3596
|
|
|
3576
|
-
this._assertNotEntityType("searchMembers", ["badges", "news", "poi", "comments"]);
|
|
3597
|
+
this._assertNotEntityType("searchMembers", ["badges", "news", "poi", "comments", "actions"]);
|
|
3577
3598
|
|
|
3578
3599
|
result.forEach(item => {
|
|
3579
3600
|
for (const key of Object.keys(item)) {
|
|
@@ -3733,7 +3754,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3733
3754
|
throw new ApiError("Utilisateur non connecté.", 401);
|
|
3734
3755
|
}
|
|
3735
3756
|
|
|
3736
|
-
this._assertNotEntityType("follow", ["badges", "news", "poi", "comments"]);
|
|
3757
|
+
this._assertNotEntityType("follow", ["badges", "news", "poi", "comments", "actions"]);
|
|
3737
3758
|
|
|
3738
3759
|
const parentType = this.getEntityType() as FollowData["parentType"];
|
|
3739
3760
|
|
|
@@ -3777,7 +3798,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3777
3798
|
|
|
3778
3799
|
const userLink = this.userContext?.serverData?.links?.["follows"]?.[this.id] || null;
|
|
3779
3800
|
|
|
3780
|
-
this._assertNotEntityType("unfollow", ["badges", "news", "poi"]);
|
|
3801
|
+
this._assertNotEntityType("unfollow", ["badges", "news", "poi", "actions"]);
|
|
3781
3802
|
|
|
3782
3803
|
const parentType = this.getEntityType() as DisconnectData["parentType"];
|
|
3783
3804
|
|
|
@@ -442,7 +442,17 @@ export interface GetElementsAboutData {
|
|
|
442
442
|
/**
|
|
443
443
|
* Type d'entité
|
|
444
444
|
*/
|
|
445
|
-
type:
|
|
445
|
+
type:
|
|
446
|
+
| "citoyens"
|
|
447
|
+
| "projects"
|
|
448
|
+
| "organizations"
|
|
449
|
+
| "events"
|
|
450
|
+
| "poi"
|
|
451
|
+
| "badges"
|
|
452
|
+
| "answers"
|
|
453
|
+
| "classifieds"
|
|
454
|
+
| "forms"
|
|
455
|
+
| "actions";
|
|
446
456
|
/**
|
|
447
457
|
* ID de l'utilisateur ou de l'entité
|
|
448
458
|
*/
|
|
@@ -5269,9 +5279,15 @@ export interface UpdatePathValueData {
|
|
|
5269
5279
|
/**
|
|
5270
5280
|
* Valeur à mettre à jour (peut être un objet, un tableau, une chaîne, etc.)
|
|
5271
5281
|
*/
|
|
5272
|
-
value:
|
|
5273
|
-
|
|
5274
|
-
|
|
5282
|
+
value:
|
|
5283
|
+
| string
|
|
5284
|
+
| number
|
|
5285
|
+
| boolean
|
|
5286
|
+
| {
|
|
5287
|
+
[k: string]: unknown;
|
|
5288
|
+
}
|
|
5289
|
+
| unknown[]
|
|
5290
|
+
| null;
|
|
5275
5291
|
[k: string]: unknown;
|
|
5276
5292
|
}
|
|
5277
5293
|
|
|
@@ -5500,9 +5516,9 @@ export interface CostumProjectActionRequestNewData {
|
|
|
5500
5516
|
*/
|
|
5501
5517
|
name: string;
|
|
5502
5518
|
/**
|
|
5503
|
-
* Statut de l'action
|
|
5519
|
+
* Statut de l'action. Sert aussi de trigger côté backend : 'done' ajoute startDate/endDate auto, 'tracking' active le mode tracking, 'discuter'/'next'/'totest' ajoutent un tag.
|
|
5504
5520
|
*/
|
|
5505
|
-
status:
|
|
5521
|
+
status: "todo" | "done" | "tracking" | "discuter" | "next" | "totest" | "disabled" | "closed";
|
|
5506
5522
|
/**
|
|
5507
5523
|
* ID du projet parent
|
|
5508
5524
|
*/
|
|
@@ -5512,18 +5528,74 @@ export interface CostumProjectActionRequestNewData {
|
|
|
5512
5528
|
*/
|
|
5513
5529
|
parentType: "projects";
|
|
5514
5530
|
/**
|
|
5515
|
-
* Montant financé
|
|
5531
|
+
* Montant financé (default: 1)
|
|
5516
5532
|
*/
|
|
5517
|
-
credits
|
|
5533
|
+
credits?: number;
|
|
5518
5534
|
/**
|
|
5519
5535
|
* Jalon associé à l'action
|
|
5520
5536
|
*/
|
|
5521
|
-
milestone
|
|
5537
|
+
milestone?: {
|
|
5522
5538
|
/**
|
|
5523
5539
|
* ID du jalon associé
|
|
5524
5540
|
*/
|
|
5525
5541
|
milestoneId: string;
|
|
5542
|
+
/**
|
|
5543
|
+
* Date de début du jalon (ISO 8601, convertie en MongoDate UTC côté backend)
|
|
5544
|
+
*/
|
|
5545
|
+
startDate?: string;
|
|
5546
|
+
/**
|
|
5547
|
+
* Date de fin du jalon (ISO 8601, convertie en MongoDate UTC côté backend)
|
|
5548
|
+
*/
|
|
5549
|
+
endDate?: string;
|
|
5526
5550
|
};
|
|
5551
|
+
/**
|
|
5552
|
+
* Date et heure de début (ISO 8601 avec offset)
|
|
5553
|
+
*/
|
|
5554
|
+
startDate?: string;
|
|
5555
|
+
/**
|
|
5556
|
+
* Date et heure de fin (ISO 8601 avec offset)
|
|
5557
|
+
*/
|
|
5558
|
+
endDate?: string;
|
|
5559
|
+
/**
|
|
5560
|
+
* Temps passé en minutes (utilisé pour calculer endDate quand status=done)
|
|
5561
|
+
*/
|
|
5562
|
+
timeSpent?: number;
|
|
5563
|
+
/**
|
|
5564
|
+
* Nombre minimum de contributeurs
|
|
5565
|
+
*/
|
|
5566
|
+
min?: number;
|
|
5567
|
+
/**
|
|
5568
|
+
* Nombre maximum de contributeurs (doit être >= min côté backend)
|
|
5569
|
+
*/
|
|
5570
|
+
max?: number;
|
|
5571
|
+
/**
|
|
5572
|
+
* Niveau d'importance de l'action
|
|
5573
|
+
*/
|
|
5574
|
+
importance?: string;
|
|
5575
|
+
/**
|
|
5576
|
+
* ID de la room parente. Si absent, auto-créée par Room::getOrCreateActionRoom côté backend
|
|
5577
|
+
*/
|
|
5578
|
+
idParentRoom?: string;
|
|
5579
|
+
/**
|
|
5580
|
+
* Si true, ajoute l'auteur courant comme contributor admin. ATTENTION : écrasé si 'mentions' est aussi fourni.
|
|
5581
|
+
*/
|
|
5582
|
+
is_contributor?: boolean;
|
|
5583
|
+
/**
|
|
5584
|
+
* userId à assigner comme contributor admin. ATTENTION : écrasé si 'mentions' est aussi fourni.
|
|
5585
|
+
*/
|
|
5586
|
+
assign?: string;
|
|
5587
|
+
/**
|
|
5588
|
+
* URLs externes associées à l'action (tableau de strings, ou "" pour vider). Passthrough côté backend.
|
|
5589
|
+
*/
|
|
5590
|
+
urls?: string[] | "";
|
|
5591
|
+
/**
|
|
5592
|
+
* Liste des contributeurs assignés à l'action par username. Résolu côté backend en links.contributors.{userId}. ATTENTION : ÉCRASE 'assign' et 'is_contributor' si fournis ensemble.
|
|
5593
|
+
*/
|
|
5594
|
+
mentions?: string[];
|
|
5595
|
+
/**
|
|
5596
|
+
* Liste des tags associés à l'action. BUG CONNU BACKEND : les tags sont dupliqués dans la réponse (array_merge avec soi-même côté PHP).
|
|
5597
|
+
*/
|
|
5598
|
+
tags?: string[];
|
|
5527
5599
|
[k: string]: unknown;
|
|
5528
5600
|
}
|
|
5529
5601
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// TypeScript native types
|
|
2
2
|
import type { CollectionType } from "../types/entities.js";
|
|
3
3
|
|
|
4
|
-
export type EntityTag = "User" | "Organization" | "Project" | "Event" | "Poi" | "Badge" | "News" | "Comment" | "Answer" | "Form" | "Classified";
|
|
4
|
+
export type EntityTag = "User" | "Organization" | "Project" | "Event" | "Poi" | "Badge" | "News" | "Comment" | "Answer" | "Form" | "Classified" | "Action";
|
|
5
5
|
export type CollectionKey = CollectionType; // Alias pour compatibilité
|
|
6
6
|
type BaseEntity = import("./BaseEntity.js").BaseEntity<any>;
|
|
7
7
|
type ApiClient = import("../ApiClient.js").default;
|
|
@@ -113,7 +113,8 @@ function _getCollectionFromTag(tag: EntityTag): CollectionKey | null {
|
|
|
113
113
|
"Comment": "comments",
|
|
114
114
|
"Answer": "answers",
|
|
115
115
|
"Form": "forms",
|
|
116
|
-
"Classified": "classifieds"
|
|
116
|
+
"Classified": "classifieds",
|
|
117
|
+
"Action": "actions"
|
|
117
118
|
};
|
|
118
119
|
return map[tag] || null;
|
|
119
120
|
}
|
|
@@ -138,6 +139,7 @@ function _getEntityMeta(entityType: CollectionKey, __entityTag: EntityTag): Enti
|
|
|
138
139
|
Answer: selfTag === "Answer" ? selfClass : EntityRegistry.get("Answer"),
|
|
139
140
|
Form: selfTag === "Form" ? selfClass : EntityRegistry.get("Form"),
|
|
140
141
|
Classified: selfTag === "Classified" ? selfClass : EntityRegistry.get("Classified"),
|
|
142
|
+
Action: selfTag === "Action" ? selfClass : EntityRegistry.get("Action"),
|
|
141
143
|
};
|
|
142
144
|
|
|
143
145
|
const map: Record<CollectionKey, EntityMeta> = {
|
|
@@ -147,16 +149,17 @@ function _getEntityMeta(entityType: CollectionKey, __entityTag: EntityTag): Enti
|
|
|
147
149
|
events: { entityClass: commonDeps.Event as EntityClass, deps: { ...commonDeps, Badge: undefined } },
|
|
148
150
|
poi: { entityClass: commonDeps.Poi as EntityClass, deps: { ...commonDeps, Badge: undefined, News: undefined, Comment: undefined } },
|
|
149
151
|
news: { entityClass: commonDeps.News as EntityClass, deps: { ...commonDeps } },
|
|
150
|
-
badges: { entityClass: commonDeps.Badge as EntityClass, deps: {
|
|
151
|
-
EndpointApi: commonDeps.EndpointApi,
|
|
152
|
-
User: commonDeps.User,
|
|
153
|
-
Organization: commonDeps.Organization,
|
|
154
|
-
Project: commonDeps.Project
|
|
152
|
+
badges: { entityClass: commonDeps.Badge as EntityClass, deps: {
|
|
153
|
+
EndpointApi: commonDeps.EndpointApi,
|
|
154
|
+
User: commonDeps.User,
|
|
155
|
+
Organization: commonDeps.Organization,
|
|
156
|
+
Project: commonDeps.Project
|
|
155
157
|
} },
|
|
156
158
|
comments: { entityClass: commonDeps.Comment as EntityClass, deps: { ...commonDeps } },
|
|
157
159
|
answers: { entityClass: commonDeps.Answer as EntityClass, deps: { ...commonDeps } },
|
|
158
160
|
forms: { entityClass: commonDeps.Form as EntityClass, deps: { ...commonDeps } },
|
|
159
161
|
classifieds: { entityClass: commonDeps.Classified as EntityClass, deps: { ...commonDeps, Badge: undefined, News: undefined } },
|
|
162
|
+
actions: { entityClass: commonDeps.Action as EntityClass, deps: { ...commonDeps } },
|
|
160
163
|
};
|
|
161
164
|
|
|
162
165
|
return map[entityType] || null;
|
|
@@ -215,6 +218,10 @@ export function createFromCollection(collection: CollectionKey, parent: ApiClien
|
|
|
215
218
|
classifieds: {
|
|
216
219
|
entityTag: "Classified",
|
|
217
220
|
meta: tag => _buildMeta(tag, { remove: ["Badge","News"] })
|
|
221
|
+
},
|
|
222
|
+
actions: {
|
|
223
|
+
entityTag: "Action",
|
|
224
|
+
meta: tag => _buildMeta(tag, { remove: [] })
|
|
218
225
|
}
|
|
219
226
|
};
|
|
220
227
|
const entry = _collectionMap[collection];
|
|
@@ -248,7 +255,8 @@ function _buildMeta(tag: EntityTag, options: { remove: string[] }): EntityMeta {
|
|
|
248
255
|
Comment: EntityRegistry.get("Comment"),
|
|
249
256
|
Answer: EntityRegistry.get("Answer"),
|
|
250
257
|
Form: EntityRegistry.get("Form"),
|
|
251
|
-
Classified: EntityRegistry.get("Classified")
|
|
258
|
+
Classified: EntityRegistry.get("Classified"),
|
|
259
|
+
Action: EntityRegistry.get("Action")
|
|
252
260
|
};
|
|
253
261
|
// inject self
|
|
254
262
|
(allDeps as any)[tag] = EntityClass;
|
package/src/api/Project.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { BaseEntity } from "./BaseEntity.js";
|
|
|
3
3
|
import { createSocialTransform, transformEntityRefs } from "../types/transforms.js";
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
import type { Action } from "./Action.js";
|
|
6
7
|
import type { PaginatorPage, PaginatorState } from "./BaseEntity.js";
|
|
7
8
|
import type { AddProjectData, GetContributorsAdminData, GetContributorsNoAdminData } from "./EndpointApi.types.js";
|
|
8
9
|
import type { Organization } from "./Organization.js";
|
|
@@ -342,10 +343,25 @@ export class Project extends BaseEntity<ProjectItemNormalized> {
|
|
|
342
343
|
* Crée une instance de news et la récupère si nécessaire.
|
|
343
344
|
*/
|
|
344
345
|
override async news(newsData: Parameters<BaseEntity<ProjectItemNormalized>["news"]>[0] = {}) {
|
|
345
|
-
|
|
346
|
+
if(!newsData?.id && !this.isContributor()){
|
|
347
|
+
throw new ApiError("Vous n'avez pas les droits pour créer une news dans ce projet", 403);
|
|
348
|
+
}
|
|
346
349
|
return super.news(newsData);
|
|
347
350
|
}
|
|
348
351
|
|
|
352
|
+
/**
|
|
353
|
+
* {@inheritDoc BaseEntity#action}
|
|
354
|
+
*
|
|
355
|
+
* Crée une instance d'Action liée à ce projet et la récupère si nécessaire.
|
|
356
|
+
*/
|
|
357
|
+
override async action(actionData: Parameters<BaseEntity<ProjectItemNormalized>["action"]>[0] = {}) {
|
|
358
|
+
if(!actionData?.id && !this.isAdmin()){
|
|
359
|
+
throw new ApiError("Vous n'avez pas les droits pour créer une action dans ce projet", 403);
|
|
360
|
+
}
|
|
361
|
+
const entity = await this.entity("actions", actionData);
|
|
362
|
+
return entity as Action;
|
|
363
|
+
}
|
|
364
|
+
|
|
349
365
|
/**
|
|
350
366
|
* ───────────────────────────────
|
|
351
367
|
* Lien utilisateur ↔ projet
|
package/src/api/User.ts
CHANGED
|
@@ -113,6 +113,7 @@ export class User extends BaseEntity<UserItemNormalized> {
|
|
|
113
113
|
Comment: typeof import("./Comment.js").Comment;
|
|
114
114
|
Answer: typeof import("./Answer.js").Answer;
|
|
115
115
|
Classified?: typeof import("./Classified.js").Classified;
|
|
116
|
+
Action?: typeof import("./Action.js").Action;
|
|
116
117
|
}
|
|
117
118
|
) {
|
|
118
119
|
if(!deps.EndpointApi){
|
package/src/api/UserApi.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ApiClient from "../ApiClient.js";
|
|
2
2
|
import { ApiError, ApiResponseError } from "../error.js";
|
|
3
|
+
import { Action } from "./Action.js";
|
|
3
4
|
import { Answer } from "./Answer.js";
|
|
4
5
|
import { Badge } from "./Badge.js";
|
|
5
6
|
import { Classified } from "./Classified.js";
|
|
@@ -68,7 +69,7 @@ export class UserApi {
|
|
|
68
69
|
this.loggedUser = new User(
|
|
69
70
|
this.client,
|
|
70
71
|
response.data.user,
|
|
71
|
-
{ EndpointApi, Organization, Project, Event, Poi, Badge, News, Comment, Answer, Classified }
|
|
72
|
+
{ EndpointApi, Organization, Project, Event, Poi, Badge, News, Comment, Answer, Classified, Action }
|
|
72
73
|
);
|
|
73
74
|
return this.loggedUser;
|
|
74
75
|
});
|
|
@@ -90,7 +91,7 @@ export class UserApi {
|
|
|
90
91
|
this.loggedUser = new User(
|
|
91
92
|
this.client,
|
|
92
93
|
{ id: this.client.userId },
|
|
93
|
-
{ EndpointApi, Organization, Project, Event, Poi, Badge, News, Comment, Answer, Classified }
|
|
94
|
+
{ EndpointApi, Organization, Project, Event, Poi, Badge, News, Comment, Answer, Classified, Action }
|
|
94
95
|
);
|
|
95
96
|
|
|
96
97
|
return this.loggedUser;
|