@@ -12,12 +12,14 @@ import {
1212 Root ,
1313 UseMiddleware ,
1414} from "type-graphql" ;
15+
1516import Group from "../entities/Group" ;
1617import { GroupMember } from "../entities/GroupMember" ;
1718import { Message } from "../entities/Message" ;
1819import User from "../entities/User" ;
1920import { getVariableEnv } from "../lib/envManager/envManager" ;
2021import { RoleMiddleware } from "../middleware/RoleMiddleware" ;
22+ import { addMembersToGroup , removeMembersFromGroup } from "../services/groupMemberService" ;
2123import type { ContextType } from "../types/context" ;
2224
2325@InputType ( )
@@ -49,6 +51,36 @@ class AddFundsInput {
4951 amount ! : number ;
5052}
5153
54+ @InputType ( )
55+ class UpdateGroupInput {
56+ @Field ( )
57+ name ! : string ;
58+
59+ @Field ( )
60+ event_type ! : string ;
61+
62+ @Field ( )
63+ piggy_bank ! : number ;
64+
65+ @Field ( )
66+ deadline ! : Date ;
67+
68+ @Field ( ( ) => [ String ] , { nullable : true } )
69+ users ?: string [ ] ;
70+
71+ @Field ( { nullable : true } )
72+ user_beneficiary ?: string ;
73+ }
74+
75+ @InputType ( )
76+ class RemoveMembersInput {
77+ @Field ( ( ) => [ Number ] , { nullable : true } )
78+ userIds ?: number [ ] ;
79+
80+ @Field ( ( ) => [ String ] , { nullable : true } )
81+ userEmails ?: string [ ] ;
82+ }
83+
5284@ObjectType ( )
5385export class MyGroupsResponse {
5486 @Field ( ( ) => [ Group ] )
@@ -72,7 +104,13 @@ export default class GroupResolver {
72104 user : { id : ctx . user ?. id } ,
73105 } ,
74106 } ,
75- relations : { groupMember : true , user_admin : true , user_beneficiary : true } ,
107+ relations : {
108+ groupMember : {
109+ user : true , // 👈 THIS is the key
110+ } ,
111+ user_admin : true ,
112+ user_beneficiary : true ,
113+ } ,
76114 order : { id : "DESC" } ,
77115 } ) ;
78116
@@ -83,10 +121,27 @@ export default class GroupResolver {
83121 return { groups, groupToken } ;
84122 }
85123
124+ @Query ( ( ) => Group )
125+ async getGroupById ( @Arg ( "id" ) id : number ) {
126+ const group = await Group . findOne ( {
127+ where : { id : id } ,
128+ relations : {
129+ user_admin : true ,
130+ user_beneficiary : true ,
131+ groupMember : {
132+ user : true ,
133+ } ,
134+ } ,
135+ } ) ;
136+ if ( ! group ) throw new Error ( "Groupe non trouvé" ) ;
137+ return group ;
138+ }
139+
86140 @FieldResolver ( ( ) => [ GroupMember ] )
87141 async groupMember ( @Root ( ) group : Group ) {
88142 const groupMembers = await GroupMember . find ( {
89143 where : { groupId : group . id } ,
144+ relations : { user : true } ,
90145 } ) ;
91146
92147 return groupMembers || [ ] ; // >>> not null
@@ -195,4 +250,149 @@ export default class GroupResolver {
195250
196251 return group ;
197252 }
253+
254+ @UseMiddleware ( RoleMiddleware ( ) )
255+ @Mutation ( ( ) => Group )
256+ async updateGroup ( @Arg ( "id" ) id : number , @Arg ( "data" ) data : UpdateGroupInput ) {
257+ const group = await Group . findOne ( { where : { id : id } } ) ;
258+ if ( ! group ) throw new Error ( "Groupe non trouvé" ) ;
259+ group . name = data . name ;
260+ group . event_type = data . event_type ;
261+ group . piggy_bank = data . piggy_bank ;
262+ group . deadline = data . deadline ;
263+ await group . save ( ) ;
264+
265+ if ( data . users ?. length ) {
266+ await addMembersToGroup ( {
267+ userEmails : data . users ,
268+ groupId : group . id ,
269+ } ) ;
270+ }
271+
272+ return group ;
273+ }
274+
275+ @UseMiddleware ( RoleMiddleware ( ) )
276+ @Mutation ( ( ) => String )
277+ async deleteGroup ( @Arg ( "id" ) id : number , @Ctx ( ) ctx : ContextType ) : Promise < string > {
278+ if ( ! ctx . user ) {
279+ throw new Error ( "Utilisateur non connecté" ) ;
280+ }
281+
282+ const group = await Group . findOne ( {
283+ where : {
284+ id,
285+ user_admin : { id : ctx . user . id } ,
286+ } ,
287+ relations : { user_admin : true } ,
288+ } ) ;
289+
290+ if ( ! group ) {
291+ throw new Error ( "Groupe introuvable ou accès refusé" ) ;
292+ }
293+
294+ if ( ! group . user_admin || group . user_admin . id !== ctx . user . id ) {
295+ throw new Error ( "Il faut être administrateur du groupe pour pouvoir le supprimer" ) ;
296+ }
297+
298+ try {
299+ await Group . remove ( group ) ;
300+ return "Le groupe a été supprimé" ;
301+ } catch ( err ) {
302+ console . error ( "deleteGroup error:" , err ) ;
303+ throw new Error ( "Une erreur est survenue lors de la suppression du groupe" ) ;
304+ }
305+ }
306+
307+ @UseMiddleware ( RoleMiddleware ( ) )
308+ @Mutation ( ( ) => String )
309+ async removeMembersFromGroup (
310+ @Arg ( "groupId" , ( ) => Number ) groupId : number ,
311+ @Arg ( "data" ) data : RemoveMembersInput ,
312+ @Ctx ( ) ctx : ContextType ,
313+ ) : Promise < string > {
314+ if ( ! ctx . user ) {
315+ throw new Error ( "Utilisateur non connecté" ) ;
316+ }
317+
318+ // Ensure at least one input is provided
319+ if ( ( ! data . userIds || data . userIds . length === 0 ) && ( ! data . userEmails || data . userEmails . length === 0 ) ) {
320+ throw new Error ( "Vous devez fournir au moins un identifiant utilisateur ou un email" ) ;
321+ }
322+
323+ const group = await Group . findOne ( {
324+ where : {
325+ id : groupId ,
326+ } ,
327+ relations : { user_admin : true } ,
328+ } ) ;
329+
330+ if ( ! group ) {
331+ throw new Error ( "Groupe introuvable ou accès refusé" ) ;
332+ }
333+
334+ const currentUserId = ctx . user . id ;
335+ const isAdmin = ctx . user . id === group . user_admin . id ;
336+
337+ // Convert emails to user IDs if provided
338+ let userIdsToRemove : number [ ] = [ ] ;
339+
340+ if ( data . userEmails && data . userEmails . length > 0 ) {
341+ await Promise . all (
342+ data . userEmails . map ( async ( email ) => {
343+ const user = await User . findOne ( { where : { email } } ) ;
344+ if ( user ) {
345+ userIdsToRemove . push ( user . id ) ;
346+ }
347+ } ) ,
348+ ) ;
349+ }
350+
351+ // Add direct user IDs if provided
352+ if ( data . userIds && data . userIds . length > 0 ) {
353+ userIdsToRemove . push ( ...data . userIds ) ;
354+ }
355+
356+ // Remove duplicates
357+ userIdsToRemove = [ ...new Set ( userIdsToRemove ) ] ;
358+
359+ if ( userIdsToRemove . length === 0 ) {
360+ throw new Error ( "Aucun utilisateur valide trouvé" ) ;
361+ }
362+
363+ // Admin cannot remove itself from a group
364+ if ( isAdmin && userIdsToRemove . includes ( group . user_admin . id ) ) {
365+ throw new Error ( "L'administrateur du groupe ne peut pas être supprimé. Supprimez plutôt le groupe" ) ;
366+ }
367+
368+ // Remove myself from a group
369+ if ( ! isAdmin && userIdsToRemove . length === 1 && userIdsToRemove [ 0 ] === currentUserId ) {
370+ try {
371+ await removeMembersFromGroup ( {
372+ userIds : [ currentUserId ] ,
373+ groupId,
374+ } ) ;
375+ return "Succès! Vous ne faites plus partie du groupe!" ;
376+ } catch ( err ) {
377+ console . error ( "removeMembersFromGroup error:" , err ) ;
378+ throw new Error ( "Une erreur est survenue, nous n'avons pas pu vous supprimer du groupe" ) ;
379+ }
380+ }
381+
382+ // Admin removes users from a group
383+ if ( isAdmin && userIdsToRemove . length > 0 && ! userIdsToRemove . includes ( group . user_admin . id ) ) {
384+ try {
385+ await removeMembersFromGroup ( {
386+ userIds : userIdsToRemove ,
387+ groupId,
388+ } ) ;
389+ return "Succès! Les utilisateurs ont été supprimés du groupe!" ;
390+ } catch ( err ) {
391+ console . error ( "removeMembersFromGroup error:" , err ) ;
392+ throw new Error ( "Une erreur est survenue, nous n'avons pas pu supprimer les utilisateurs du groupe" ) ;
393+ }
394+ }
395+
396+ throw new Error ( "Vous n'avez pas les permissions nécessaires pour effectuer cette action" ) ;
397+ }
198398}
0 commit comments