@communecter/cocolight-api-client 1.0.78 → 1.0.79

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@communecter/cocolight-api-client",
3
- "version": "1.0.78",
3
+ "version": "1.0.79",
4
4
  "description": "Client Axios simplifié pour l'API cocolight",
5
5
  "repository": {
6
6
  "type": "git",
@@ -230,6 +230,8 @@ type LinkFilters = Record<string, FilterValue>;
230
230
  interface MinimalUserLink {
231
231
  toBeValidated?: boolean;
232
232
  isInviting?: boolean;
233
+ isAdminInviting?: boolean;
234
+ isAdminPending?: boolean;
233
235
  }
234
236
 
235
237
  // Types pour linkEntitiesFromServerData
@@ -2406,7 +2408,7 @@ export class BaseEntity<TServerData = any> {
2406
2408
  }
2407
2409
 
2408
2410
  // Invitation reçue → accepte automatiquement
2409
- if (userLink.isInviting) {
2411
+ if (userLink.isInviting || userLink.isAdminInviting) {
2410
2412
  return this._acceptLinkRequest();
2411
2413
  }
2412
2414
 
@@ -2443,7 +2445,7 @@ export class BaseEntity<TServerData = any> {
2443
2445
 
2444
2446
  const userLink = this._getLinkFromConnectedUser();
2445
2447
 
2446
- if (userLink && userLink.isInviting) {
2448
+ if (userLink && (userLink.isInviting || userLink.isAdminInviting)) {
2447
2449
  const t = this.getEntityType();
2448
2450
 
2449
2451
  // 1) Garde runtime pour exclure les types non pris en charge par LinkValidateData
@@ -3382,8 +3384,8 @@ export class BaseEntity<TServerData = any> {
3382
3384
  */
3383
3385
  protected _validateUserLink(userLink: MinimalUserLink | null | undefined): boolean {
3384
3386
  if (!userLink) return false;
3385
- const { toBeValidated, isInviting } = userLink;
3386
- return !toBeValidated && !isInviting;
3387
+ const { toBeValidated, isInviting, isAdminInviting, isAdminPending } = userLink;
3388
+ return !toBeValidated && !isInviting && !isAdminInviting && !isAdminPending;
3387
3389
  }
3388
3390
 
3389
3391
  /**
@@ -2,7 +2,7 @@
2
2
  import { ApiAuthenticationError } from "../error.js";
3
3
 
4
4
  import type ApiClient from "../ApiClient.js";
5
- import type { PersonRegisterData, AuthenticateUrlData, RefreshTokenUrlData, PasswordRecoveryData, ServerExchangeTokenData, ChangePasswordData, DeleteAccountData, UpdateSettingsData, UpdateBlockDescriptionData, UpdateBlockInfoData, UpdateBlockSocialData, UpdateBlockLocalityData, UpdateBlockSlugData, CheckData, ProfilImageData, ProfilBannerData, GetElementsAboutData, MulticonnectData, GetNewsData, GetNewsByIdData, AddNewsData, AddImageNewsData, AddFileNewsData, DeleteNewsData, UpdateNewsData, ShareNewsData, GetCommentsData, AddCommentsData, DeleteCommentsData, UpdateCommentsData, SearchTagsData, ShowVoteData, GlobalAutocompleteData, CityAutocompleteData, CityAutocompleteByCountryData, SuggestionInputData, GetProjectsNoAdminData, GetProjectsAdminData, GetPoisNoAdminData, GetPoisAdminData, GetOrganizationsNoAdminData, GetOrganizationsAdminData, GetMembersNoAdminData, GetMembersAdminData, GetFriendsAdminData, GetSubscriptionsData, GetSubscriptionsAdminData, GetSubscribersData, GetSubscribersAdminData, GetContributorsNoAdminData, GetContributorsAdminData, GetBadgesData, GetBadgesFiltersData, ConnectData, DisconnectData, GetElementsKeyData, GetFavorisData, DeleteFavorisData, AddFavorisData, AddOrganizationData, AddProjectData, AddPoiData, AddEventData, DeletePoiData, DeleteEventData, DeleteElementData, AddImageElementData, LinkValidateData, SearchMemberAutocompleteData, GetNotificationsData, GetNotificationsCountData, NotificationUpdateData, MarkNotificationAsReadData, ActivitypubSearchData, ActivitypubLinkData, ActivitypubGetCommunityData, GetBadgeData, AddBadgesData, AssignBadgesData, GetEventsData, ShareEventsData, InviteEventData, FollowData, GetCostumJsonData, GlobalAutocompleteCostumData, CostumEventRequestActorsData, CostumEventRequestSubeventsData, CostumEventRequestElementEventData, CostumEventRequestCategoriesData, CostumEventRequestDatesData, CostumEventRequestEventData, CostumEventRequestLinkTlToEventData, CostumEventRequestLoadContextTagData, GetGalleryData, GetAttendeesNoAdminData, GetAttendeesAdminData, CoformAnswersSearchData, CoformAnswersByIdData, AddVoteData, AddReportAbuseData, UpdatePathValueData, DeleteDocumentByContextData } from "./EndpointApi.types.js";
5
+ import type { PersonRegisterData, AuthenticateUrlData, RefreshTokenUrlData, PasswordRecoveryData, ServerExchangeTokenData, ChangePasswordData, DeleteAccountData, UpdateSettingsData, UpdateBlockDescriptionData, UpdateBlockInfoData, UpdateBlockSocialData, UpdateBlockLocalityData, UpdateBlockSlugData, CheckData, ProfilImageData, ProfilBannerData, GetElementsAboutData, MulticonnectData, GetNewsData, GetNewsByIdData, AddNewsData, AddImageNewsData, AddFileNewsData, DeleteNewsData, UpdateNewsData, ShareNewsData, GetCommentsData, AddCommentsData, DeleteCommentsData, UpdateCommentsData, SearchTagsData, ShowVoteData, GlobalAutocompleteData, CityAutocompleteData, CityAutocompleteByCountryData, SuggestionInputData, GetProjectsNoAdminData, GetProjectsAdminData, GetPoisNoAdminData, GetPoisAdminData, GetOrganizationsNoAdminData, GetOrganizationsAdminData, GetMembersNoAdminData, GetMembersAdminData, GetFriendsAdminData, GetSubscriptionsData, GetSubscriptionsAdminData, GetSubscribersData, GetSubscribersAdminData, GetContributorsNoAdminData, GetContributorsAdminData, GetBadgesData, GetBadgesFiltersData, ConnectData, DisconnectData, GetElementsKeyData, GetFavorisData, DeleteFavorisData, AddFavorisData, AddOrganizationData, AddProjectData, AddPoiData, AddEventData, DeletePoiData, DeleteEventData, DeleteElementData, AddImageElementData, LinkValidateData, SearchMemberAutocompleteData, GetNotificationsData, GetNotificationsCountData, NotificationUpdateData, MarkNotificationAsReadData, ActivitypubSearchData, ActivitypubLinkData, ActivitypubGetCommunityData, GetBadgeData, AddBadgesData, AssignBadgesData, GetEventsData, ShareEventsData, InviteEventData, FollowData, GetCostumJsonData, GlobalAutocompleteCostumData, CostumEventRequestActorsData, CostumEventRequestSubeventsData, CostumEventRequestElementEventData, CostumEventRequestCategoriesData, CostumEventRequestDatesData, CostumEventRequestEventData, CostumEventRequestLinkTlToEventData, CostumEventRequestLoadContextTagData, GetGalleryData, GetAttendeesNoAdminData, GetAttendeesAdminData, CoformAnswersSearchData, CoformAnswersByIdData, AddVoteData, AddReportAbuseData, UpdatePathValueData, DeleteDocumentByContextData, DemoteAdminData } from "./EndpointApi.types.js";
6
6
 
7
7
  /**
8
8
  * Classe EndpointApi générée automatiquement depuis endpoints-copie.json
@@ -1704,6 +1704,22 @@ export class EndpointApi {
1704
1704
  return this.callIsConnected("DELETE_DOCUMENT_BY_CONTEXT", data);
1705
1705
  }
1706
1706
 
1707
+ /**
1708
+ * Rétirer les droits d’administrateur d’un membre : Rétirer les droits d’administrateur d’un membre d’une organisation ou d’un projet
1709
+ * Constant : DEMOTE_ADMIN
1710
+ * @param data - Données envoyées à l'API
1711
+ * @returns Les données de réponse.
1712
+ * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse.
1713
+ * @throws {ApiAuthenticationError} - En cas d'erreur d'authentification.
1714
+ * @throws {Error} - En cas d'erreur inattendue.
1715
+ */
1716
+ async demoteAdmin(data: DemoteAdminData): Promise<any> {
1717
+ if (!data || typeof data !== "object") {
1718
+ throw new TypeError("Le paramètre data doit être un objet.");
1719
+ }
1720
+ return this.callIsConnected("DEMOTE_ADMIN", data);
1721
+ }
1722
+
1707
1723
  }
1708
1724
 
1709
1725
  export default EndpointApi;
@@ -4899,3 +4899,17 @@ export interface DeleteDocumentByContextData {
4899
4899
  };
4900
4900
  [k: string]: unknown;
4901
4901
  }
4902
+
4903
+
4904
+ export interface DemoteAdminData {
4905
+ parentId: string;
4906
+ /**
4907
+ * Type de contexte de l’entité
4908
+ */
4909
+ parentType: "organizations" | "projects";
4910
+ childId: string;
4911
+ childType: "citoyens";
4912
+ connect: "members" | "contributors";
4913
+ isAdmin: false;
4914
+ [k: string]: unknown;
4915
+ }
package/src/api/User.ts CHANGED
@@ -15,7 +15,8 @@ import type {
15
15
  GetSubscriptionsData,
16
16
  GetOrganizationsNoAdminData,
17
17
  GetOrganizationsAdminData,
18
- GetFriendsAdminData
18
+ GetFriendsAdminData,
19
+ DemoteAdminData
19
20
  } from "./EndpointApi.types.js";
20
21
  import type { Organization } from "./Organization.js";
21
22
  import type { EntityTypes } from "@/types/entities.js";
@@ -922,16 +923,18 @@ export class User extends BaseEntity<UserItemNormalized> {
922
923
  }
923
924
  }
924
925
 
926
+
925
927
  /**
926
- * Récupère le lien utilisateur pour l'entité parente.
928
+ * Récupère le lien parent pour l'utilisateur actuel.
929
+ *
930
+ * @returns Le lien parent de l'utilisateur ou `null` s'il n'existe pas.
927
931
  * @private
928
- * @returns Le lien utilisateur ou null.
929
932
  */
930
- private _getUserLinkForParent(): any {
931
- const { linkType } = this.parent!._getLinkMeta();
932
- const parentId = this.parent!.id;
933
- if (!parentId) return null;
934
- return this?.serverData?.links?.[linkType]?.[parentId] || null;
933
+ private _getParentLinkForUser(): any {
934
+ const { connectTypeDisconnect } = this.parent!._getLinkMeta();
935
+ const userId = this!.id;
936
+ if (!userId) return null;
937
+ return this.parent?.serverData?.links?.[connectTypeDisconnect]?.[userId] || null;
935
938
  }
936
939
 
937
940
  /**
@@ -976,8 +979,8 @@ export class User extends BaseEntity<UserItemNormalized> {
976
979
  */
977
980
  override isAdmin(): boolean {
978
981
  this._validateRoleCheckPreconditions("isAdmin", ["organizations", "projects"]);
979
- const userLink = this._getUserLinkForParent();
980
- return this._validateUserLink(userLink) && userLink?.isAdmin === true && !userLink?.isAdminPending;
982
+ const parentLink = this._getParentLinkForUser();
983
+ return this._validateUserLink(parentLink) && parentLink?.isAdmin === true;
981
984
  }
982
985
 
983
986
  /**
@@ -1007,8 +1010,8 @@ export class User extends BaseEntity<UserItemNormalized> {
1007
1010
  */
1008
1011
  override isMember(): boolean {
1009
1012
  this._validateRoleCheckPreconditions("isMember", ["organizations"]);
1010
- const userLink = this._getUserLinkForParent();
1011
- return this._validateUserLink(userLink);
1013
+ const parentLink = this._getParentLinkForUser();
1014
+ return this._validateUserLink(parentLink);
1012
1015
  }
1013
1016
 
1014
1017
  /**
@@ -1038,8 +1041,8 @@ export class User extends BaseEntity<UserItemNormalized> {
1038
1041
  */
1039
1042
  override isContributor(): boolean {
1040
1043
  this._validateRoleCheckPreconditions("isContributor", ["projects"]);
1041
- const userLink = this._getUserLinkForParent();
1042
- return this._validateUserLink(userLink);
1044
+ const parentLink = this._getParentLinkForUser();
1045
+ return this._validateUserLink(parentLink);
1043
1046
  }
1044
1047
 
1045
1048
  /**
@@ -1069,8 +1072,483 @@ export class User extends BaseEntity<UserItemNormalized> {
1069
1072
  */
1070
1073
  override isAttendee(): boolean {
1071
1074
  this._validateRoleCheckPreconditions("isAttendee", ["events"]);
1072
- const userLink = this._getUserLinkForParent();
1073
- return this._validateUserLink(userLink);
1075
+ const parentLink = this._getParentLinkForUser();
1076
+ return this._validateUserLink(parentLink);
1077
+ }
1078
+
1079
+ /**
1080
+ * Vérifie si l'utilisateur a une invitation en attente d'acceptation pour l'entité parente.
1081
+ *
1082
+ * Cette méthode vérifie si l'utilisateur a été invité à rejoindre l'organisation, le projet
1083
+ * ou l'événement parent, mais n'a pas encore accepté l'invitation (`isInviting: true`).
1084
+ *
1085
+ * @returns {boolean} `true` si l'utilisateur a une invitation en attente, `false` sinon
1086
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin du parent, ou si le parent n'est pas défini
1087
+ *
1088
+ * @example
1089
+ * ```typescript
1090
+ * const org = await me.organization({ slug: "myOrg" });
1091
+ * const members = await org.getMembers();
1092
+ * const invitedUser = members.results.find(m => m.isInviting());
1093
+ * console.log("Invitation en attente:", invitedUser?.data.name);
1094
+ * ```
1095
+ */
1096
+ isInviting(): boolean {
1097
+ this._validateRoleCheckPreconditions("isInviting", ["organizations", "projects", "events"]);
1098
+ const parentLink = this._getParentLinkForUser();
1099
+ if (!parentLink) return false;
1100
+ return parentLink?.isInviting === true;
1101
+ }
1102
+
1103
+ /**
1104
+ * Vérifie si l'utilisateur a une invitation en attente avec des droits d'admin.
1105
+ *
1106
+ * Cette méthode vérifie si l'utilisateur a été invité à rejoindre l'organisation ou le projet
1107
+ * parent en tant qu'administrateur, mais n'a pas encore accepté (`isInviting: true` et `isAdmin: true`).
1108
+ *
1109
+ * @returns {boolean} `true` si l'utilisateur a une invitation admin en attente, `false` sinon
1110
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin du parent, ou si le parent n'est pas défini
1111
+ *
1112
+ * @example
1113
+ * ```typescript
1114
+ * const org = await me.organization({ slug: "myOrg" });
1115
+ * const members = await org.getMembers();
1116
+ * const invitedAdmin = members.results.find(m => m.isInvitingAdmin());
1117
+ * console.log("Invitation admin en attente:", invitedAdmin?.data.name);
1118
+ * ```
1119
+ */
1120
+ isInvitingAdmin(): boolean {
1121
+ this._validateRoleCheckPreconditions("isInvitingAdmin", ["organizations", "projects"]);
1122
+ const parentLink = this._getParentLinkForUser();
1123
+ if (!parentLink) return false;
1124
+ return parentLink?.isAdminInviting === true && parentLink?.isAdmin === true;
1125
+ }
1126
+
1127
+ /**
1128
+ * Vérifie si l'utilisateur a une demande de promotion admin en attente de validation.
1129
+ *
1130
+ * Cette méthode vérifie si l'utilisateur a demandé ou a été proposé pour devenir administrateur
1131
+ * de l'organisation ou du projet parent, mais la demande n'a pas encore été validée (`isAdminPending: true`).
1132
+ *
1133
+ * @returns {boolean} `true` si l'utilisateur a une demande admin en attente, `false` sinon
1134
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin du parent, ou si le parent n'est pas défini
1135
+ *
1136
+ * @example
1137
+ * ```typescript
1138
+ * const org = await me.organization({ slug: "myOrg" });
1139
+ * const members = await org.getMembers();
1140
+ * const pendingAdmin = members.results.find(m => m.isAdminPending());
1141
+ * if (pendingAdmin) {
1142
+ * await pendingAdmin.validateAdminRequest();
1143
+ * }
1144
+ * ```
1145
+ */
1146
+ isAdminPending(): boolean {
1147
+ this._validateRoleCheckPreconditions("isAdminPending", ["organizations", "projects"]);
1148
+ const parentLink = this._getParentLinkForUser();
1149
+ return parentLink?.isAdmin === true && parentLink?.isAdminPending === true;
1150
+ }
1151
+
1152
+ /**
1153
+ * Vérifie si l'utilisateur a une demande d'adhésion en attente de validation.
1154
+ *
1155
+ * Cette méthode vérifie si l'utilisateur a demandé à rejoindre l'organisation, le projet
1156
+ * ou l'événement parent, mais la demande n'a pas encore été validée par un administrateur (`toBeValidated: true`).
1157
+ *
1158
+ * @returns {boolean} `true` si l'utilisateur a une demande en attente, `false` sinon
1159
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin du parent, ou si le parent n'est pas défini
1160
+ *
1161
+ * @example
1162
+ * ```typescript
1163
+ * const org = await me.organization({ slug: "myOrg" });
1164
+ * const members = await org.getMembers();
1165
+ * const pendingUser = members.results.find(m => m.isToBeValidated());
1166
+ * if (pendingUser) {
1167
+ * await pendingUser.validateMemberRequest();
1168
+ * }
1169
+ * ```
1170
+ */
1171
+ isToBeValidated(): boolean {
1172
+ this._validateRoleCheckPreconditions("isToBeValidated", ["organizations", "projects", "events"]);
1173
+ const parentLink = this._getParentLinkForUser();
1174
+ return parentLink?.toBeValidated === true;
1175
+ }
1176
+
1177
+
1178
+ // ────────────────────────────────
1179
+ // Actions d'admin sur l'utilisateur par rapport à l'entité parente
1180
+ // ────────────────────────────────
1181
+
1182
+ /**
1183
+ * Envoie une demande pour rejoindre l'entité parente.
1184
+ *
1185
+ * Cette méthode permet à un admin de créer une demande de connexion pour un utilisateur
1186
+ * vers l'organisation ou le projet parent. Si l'utilisateur n'a pas encore de lien,
1187
+ * une demande de connexion est créée.
1188
+ *
1189
+ * @returns {Promise<unknown>} La réponse de l'API après création de la demande
1190
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
1191
+ * @throws {ApiError} Si l'utilisateur est déjà en attente de validation
1192
+ * @throws {ApiError} Si l'utilisateur est déjà connecté à l'entité
1193
+ *
1194
+ * @example
1195
+ * ```typescript
1196
+ * // Un admin récupère les membres et envoie une invitation
1197
+ * const org = await me.organization({ slug: "myOrg" });
1198
+ * const users = await org.getMembers();
1199
+ * const user = users.results[0];
1200
+ * await user.sendRequestToJoinParent();
1201
+ * ```
1202
+ */
1203
+ async sendRequestToJoinParent(): Promise<unknown> {
1204
+ this._validateRoleCheckPreconditions("sendRequestToJoinParent", ["organizations", "projects"]);
1205
+
1206
+ const { connectTypeConnect } = this.parent!._getLinkMeta();
1207
+
1208
+ const parentLink = this._getParentLinkForUser();
1209
+
1210
+ // Cas : aucun lien → on demande à se connecter
1211
+ if (!parentLink) {
1212
+
1213
+ const t = this.parent!.getEntityType();
1214
+
1215
+ // 2) Narrow de type pour TypeScript
1216
+ const parentType = t as ConnectData["parentType"];
1217
+
1218
+ const data: ConnectData = {
1219
+ childId: this.id!,
1220
+ childType: "citoyens",
1221
+ parentType,
1222
+ parentId: this.parent!.id!,
1223
+ connectType: connectTypeConnect
1224
+ };
1225
+
1226
+ const retour = await this.callIsConnected(() => this.endpointApi.connect(data));
1227
+ // Refresh l'utilisateur membre et l'entité parente
1228
+ await this.refresh();
1229
+ if (this.parent) {
1230
+ await this.parent.refresh();
1231
+ }
1232
+ return retour;
1233
+ }
1234
+
1235
+ // Cas : déjà en attente
1236
+ if (parentLink.toBeValidated) {
1237
+ throw new ApiError("Vous êtes déjà en attente de validation.", 400);
1238
+ }
1239
+
1240
+ // Cas par défaut : rien à faire
1241
+ throw new ApiError("Vous êtes déjà connecté à cette entité.", 400);
1242
+ }
1243
+
1244
+ /**
1245
+ * Valide une demande de membre en attente de validation.
1246
+ *
1247
+ * Cette méthode permet à un admin de valider une demande d'adhésion d'un utilisateur
1248
+ * qui est en attente (`toBeValidated: true`). Après validation, l'utilisateur devient
1249
+ * membre actif de l'entité parente.
1250
+ *
1251
+ * @returns {Promise<unknown>} La réponse de l'API après validation
1252
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
1253
+ * @throws {ApiError} Si l'utilisateur n'a pas de demande en attente
1254
+ *
1255
+ * @example
1256
+ * ```typescript
1257
+ * // Un admin valide une demande en attente
1258
+ * const org = await me.organization({ slug: "myOrg" });
1259
+ * const members = await org.getMembers();
1260
+ * const pendingUser = members.results.find(u => u.serverData.links?.memberOf?.[org.id]?.toBeValidated);
1261
+ * await pendingUser.validateMemberRequest();
1262
+ * ```
1263
+ */
1264
+ async validateMemberRequest(): Promise<unknown> {
1265
+ this._validateRoleCheckPreconditions("validateMemberRequest", ["organizations", "projects"]);
1266
+
1267
+ const parentLink = this._getParentLinkForUser();
1268
+
1269
+ if (!parentLink?.toBeValidated) {
1270
+ throw new ApiError("Cet utilisateur n'a pas de demande en attente de validation.", 400);
1271
+ }
1272
+
1273
+ const t = this.parent!.getEntityType();
1274
+
1275
+ const parentType = t as LinkValidateData["parentType"];
1276
+
1277
+ const data: LinkValidateData = {
1278
+ childId: this.id!,
1279
+ childType: "citoyens",
1280
+ parentType,
1281
+ parentId: this.parent!.id!,
1282
+ linkOption: "toBeValidated"
1283
+ };
1284
+
1285
+ const retour = await this.callIsConnected(() => this.endpointApi.linkValidate(data));
1286
+ // Refresh l'utilisateur membre et l'entité parente
1287
+ await this.refresh();
1288
+ if (this.parent) {
1289
+ await this.parent.refresh();
1290
+ }
1291
+ return retour;
1292
+ }
1293
+
1294
+ /**
1295
+ * Valide une invitation en attente d'acceptation.
1296
+ *
1297
+ * Cette méthode permet à un admin de valider une invitation envoyée à un utilisateur
1298
+ * qui est en attente (`isInviting: true`). Après validation, l'utilisateur devient
1299
+ * membre actif de l'entité parente.
1300
+ *
1301
+ * @returns {Promise<unknown>} La réponse de l'API après validation
1302
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
1303
+ * @throws {ApiError} Si l'utilisateur n'a pas d'invitation en attente
1304
+ *
1305
+ * @example
1306
+ * ```typescript
1307
+ * // Un admin valide une invitation en attente
1308
+ * const org = await me.organization({ slug: "myOrg" });
1309
+ * const members = await org.getMembers();
1310
+ * const invitedUser = members.results.find(u => u.serverData.links?.memberOf?.[org.id]?.isInviting);
1311
+ * await invitedUser.validateInvitation();
1312
+ * ```
1313
+ */
1314
+ // async validateInvitation(): Promise<unknown> {
1315
+ // this._validateRoleCheckPreconditions("validateInvitation", ["organizations", "projects"]);
1316
+
1317
+ // const parentLink = this._getParentLinkForUser();
1318
+
1319
+ // if (!parentLink?.isInviting) {
1320
+ // throw new ApiError("Cet utilisateur n'a pas d'invitation en attente.", 400);
1321
+ // }
1322
+
1323
+ // const t = this.parent!.getEntityType();
1324
+
1325
+ // const parentType = t as LinkValidateData["parentType"];
1326
+
1327
+ // const data: LinkValidateData = {
1328
+ // childId: this.id!,
1329
+ // childType: "citoyens",
1330
+ // parentType,
1331
+ // parentId: this.parent!.id!,
1332
+ // linkOption: "isInviting"
1333
+ // };
1334
+
1335
+ // const retour = await this.callIsConnected(() => this.endpointApi.linkValidate(data));
1336
+ // // Refresh l'utilisateur membre et l'entité parente
1337
+ // await this.refresh();
1338
+ // if (this.parent) {
1339
+ // await this.parent.refresh();
1340
+ // }
1341
+ // return retour;
1342
+ // }
1343
+
1344
+ /**
1345
+ * Valide une demande d'admin en attente.
1346
+ *
1347
+ * Cette méthode permet à un admin de valider une demande de promotion au statut d'admin
1348
+ * pour un utilisateur qui est en attente (`isAdminPending: true`). Après validation,
1349
+ * l'utilisateur devient admin de l'entité parente.
1350
+ *
1351
+ * @returns {Promise<unknown>} La réponse de l'API après validation
1352
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
1353
+ * @throws {ApiError} Si l'utilisateur n'a pas de demande d'admin en attente
1354
+ *
1355
+ * @example
1356
+ * ```typescript
1357
+ * // Un admin valide une demande de promotion à admin
1358
+ * const org = await me.organization({ slug: "myOrg" });
1359
+ * const members = await org.getMembers();
1360
+ * const pendingAdmin = members.results.find(u => u.serverData.links?.memberOf?.[org.id]?.isAdminPending);
1361
+ * await pendingAdmin.validateAdminRequest();
1362
+ * ```
1363
+ */
1364
+ async validateAdminRequest(): Promise<unknown> {
1365
+ this._validateRoleCheckPreconditions("validateAdminRequest", ["organizations", "projects"]);
1366
+
1367
+ const parentLink = this._getParentLinkForUser();
1368
+
1369
+ if (!parentLink?.isAdminPending) {
1370
+ throw new ApiError("Cet utilisateur n'a pas de demande d'admin en attente.", 400);
1371
+ }
1372
+
1373
+ const t = this.parent!.getEntityType();
1374
+
1375
+ const parentType = t as LinkValidateData["parentType"];
1376
+
1377
+ const data: LinkValidateData = {
1378
+ childId: this.id!,
1379
+ childType: "citoyens",
1380
+ parentType,
1381
+ parentId: this.parent!.id!,
1382
+ linkOption: "isAdminPending"
1383
+ };
1384
+
1385
+ const retour = await this.callIsConnected(() => this.endpointApi.linkValidate(data));
1386
+ // Refresh l'utilisateur membre et l'entité parente
1387
+ await this.refresh();
1388
+ if (this.parent) {
1389
+ await this.parent.refresh();
1390
+ }
1391
+ return retour;
1392
+ }
1393
+
1394
+ /**
1395
+ * Retire un utilisateur de l'entité parente.
1396
+ *
1397
+ * Cette méthode permet à un admin de déconnecter un membre de l'organisation ou du projet parent.
1398
+ * L'utilisateur perd tous ses liens avec l'entité (membre, admin, etc.).
1399
+ *
1400
+ * @returns {Promise<unknown>} La réponse de l'API après déconnexion
1401
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
1402
+ * @throws {ApiError} Si l'utilisateur n'est pas membre de l'entité
1403
+ *
1404
+ * @example
1405
+ * ```typescript
1406
+ * // Un admin retire un membre
1407
+ * const org = await me.organization({ slug: "myOrg" });
1408
+ * const members = await org.getMembers();
1409
+ * const userToRemove = members.results[0];
1410
+ * await userToRemove.removeFromParent();
1411
+ * ```
1412
+ */
1413
+ async removeFromParent(): Promise<unknown> {
1414
+ this._validateRoleCheckPreconditions("removeFromParent", ["organizations", "projects"]);
1415
+
1416
+ const parentLink = this._getParentLinkForUser();
1417
+
1418
+ if (!parentLink) {
1419
+ throw new ApiError("Cet utilisateur n'est pas connecté à cette entité.", 400);
1420
+ }
1421
+
1422
+ const t = this.parent!.getEntityType();
1423
+
1424
+ const parentType = t as DisconnectData["parentType"];
1425
+
1426
+ const { connectTypeDisconnect } = this.parent!._getLinkMeta();
1427
+
1428
+ const data: DisconnectData = {
1429
+ childId: this.id!,
1430
+ childType: "citoyens",
1431
+ parentType,
1432
+ parentId: this.parent!.id!,
1433
+ connectType: connectTypeDisconnect
1434
+ };
1435
+
1436
+ const retour = await this.callIsConnected(() => this.endpointApi.disconnect(data));
1437
+ // Refresh l'utilisateur membre et l'entité parente
1438
+ await this.refresh();
1439
+ if (this.parent) {
1440
+ await this.parent.refresh();
1441
+ }
1442
+ return retour;
1443
+ }
1444
+
1445
+ /**
1446
+ * Promeut un utilisateur au statut d'admin de l'entité parente.
1447
+ *
1448
+ * Cette méthode permet à un admin de promouvoir un membre ordinaire au statut d'admin
1449
+ * de l'organisation ou du projet parent.
1450
+ *
1451
+ * @returns {Promise<unknown>} La réponse de l'API après promotion
1452
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
1453
+ * @throws {ApiError} Si l'utilisateur n'est pas membre de l'entité
1454
+ * @throws {ApiError} Si l'utilisateur est déjà admin
1455
+ *
1456
+ * @example
1457
+ * ```typescript
1458
+ * // Un admin promeut un membre au statut d'admin
1459
+ * const org = await me.organization({ slug: "myOrg" });
1460
+ * const members = await org.getMembers();
1461
+ * const userToPromote = members.results.find(u => !u.isAdmin());
1462
+ * await userToPromote.promoteToAdmin();
1463
+ * ```
1464
+ */
1465
+ async promoteToAdmin(): Promise<unknown> {
1466
+ this._validateRoleCheckPreconditions("promoteToAdmin", ["organizations", "projects"]);
1467
+
1468
+ const parentLink = this._getParentLinkForUser();
1469
+
1470
+ if (!parentLink) {
1471
+ throw new ApiError("Cet utilisateur n'est pas membre de cette entité.", 400);
1472
+ }
1473
+
1474
+ if (parentLink.isAdmin && !parentLink.isAdminPending) {
1475
+ throw new ApiError("Cet utilisateur est déjà admin.", 400);
1476
+ }
1477
+
1478
+ const t = this.parent!.getEntityType();
1479
+
1480
+ const parentType = t as ConnectData["parentType"];
1481
+
1482
+ const data: ConnectData = {
1483
+ childId: this.id!,
1484
+ childType: "citoyens",
1485
+ parentType,
1486
+ parentId: this.parent!.id!,
1487
+ connectType: "admin"
1488
+ };
1489
+
1490
+ const retour = await this.callIsConnected(() => this.endpointApi.connect(data));
1491
+ // Refresh l'utilisateur membre et l'entité parente
1492
+ await this.refresh();
1493
+ if (this.parent) {
1494
+ await this.parent.refresh();
1495
+ }
1496
+ return retour;
1497
+ }
1498
+
1499
+ /**
1500
+ * Rétrograde un admin au statut de membre ordinaire.
1501
+ *
1502
+ * Cette méthode permet à un admin de rétrograder un autre admin au statut de membre
1503
+ * ordinaire de l'organisation ou du projet parent. L'utilisateur perd ses privilèges d'admin
1504
+ * mais reste membre de l'entité.
1505
+ *
1506
+ * @returns {Promise<unknown>} La réponse de l'API après rétrogradation
1507
+ * @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
1508
+ * @throws {ApiError} Si l'utilisateur n'est pas admin de l'entité
1509
+ *
1510
+ * @example
1511
+ * ```typescript
1512
+ * // Un admin rétrograde un autre admin
1513
+ * const org = await me.organization({ slug: "myOrg" });
1514
+ * const admins = await org.getMembers({}, { isAdmin: true });
1515
+ * const adminToDemote = admins.results.find(u => u.isAdmin());
1516
+ * await adminToDemote.demoteFromAdmin();
1517
+ * ```
1518
+ */
1519
+ async demoteFromAdmin(): Promise<unknown> {
1520
+ this._validateRoleCheckPreconditions("demoteFromAdmin", ["organizations", "projects"]);
1521
+
1522
+ const parentLink = this._getParentLinkForUser();
1523
+
1524
+ if (!parentLink?.isAdmin || parentLink.isAdminPending) {
1525
+ throw new ApiError("Cet utilisateur n'est pas admin de cette entité.", 400);
1526
+ }
1527
+
1528
+ const t = this.parent!.getEntityType();
1529
+
1530
+ const { connectTypeDisconnect } = this.parent!._getLinkMeta();
1531
+
1532
+ const parentType = t as DemoteAdminData["parentType"];
1533
+
1534
+ // Pour rétrograder un admin, on déconnecte le lien "admin"
1535
+ // L'utilisateur garde son lien "member"
1536
+ const data: DemoteAdminData = {
1537
+ childId: this.id!,
1538
+ childType: "citoyens",
1539
+ parentType,
1540
+ parentId: this.parent!.id!,
1541
+ connect: connectTypeDisconnect as DemoteAdminData["connect"],
1542
+ isAdmin: false
1543
+ };
1544
+
1545
+ const retour = await this.callIsConnected(() => this.endpointApi.demoteAdmin(data));
1546
+ // Refresh l'utilisateur membre et l'entité parente
1547
+ await this.refresh();
1548
+ if (this.parent) {
1549
+ await this.parent.refresh();
1550
+ }
1551
+ return retour;
1074
1552
  }
1075
1553
 
1076
1554
  }
@@ -18,6 +18,7 @@ export interface LinkRef<TType extends string = string> {
18
18
  isAdmin?: boolean;
19
19
  isAdminPending?: boolean;
20
20
  isInviting?: boolean;
21
+ isAdminInviting?: boolean;
21
22
  invitorId?: string;
22
23
  invitorName?: string;
23
24
  toBeValidated?: boolean;