Skip to content

Commit 7862348

Browse files
committed
Gestions des utilisateurs dans le formulaire du groupe backend et frontend, ajout d'un état disabled
1 parent 79e7fa6 commit 7862348

File tree

19 files changed

+608
-217
lines changed

19 files changed

+608
-217
lines changed

backend/src/entities/GroupMember.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,7 @@ export class GroupMember extends BaseEntity {
3939
@Field()
4040
isGroupAdmin: boolean;
4141

42-
@Column({ nullable: true })
43-
@Field({ nullable: true })
44-
firstName?: string;
45-
46-
@Column({ nullable: true })
47-
@Field({ nullable: true })
48-
lastName?: string;
49-
50-
@Column({ nullable: true })
51-
@Field({ nullable: true })
52-
email?: string;
53-
42+
@Field(() => User)
5443
@ManyToOne(
5544
() => User,
5645
(user) => user.groupMember, {

backend/src/resolvers/GroupResolver.ts

Lines changed: 77 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ class UpdateGroupInput {
6565

6666
}
6767

68+
@InputType()
69+
class RemoveMembersInput {
70+
@Field(() => [Number], { nullable: true })
71+
userIds?: number[];
72+
73+
@Field(() => [String], { nullable: true })
74+
userEmails?: string[];
75+
}
76+
6877
@ObjectType()
6978
export class MyGroupsResponse {
7079
@Field(() => [Group])
@@ -88,7 +97,9 @@ export default class GroupResolver {
8897
user: { id: ctx.user?.id },
8998
},
9099
},
91-
relations: { groupMember: true, user_admin: true, user_beneficiary: true },
100+
relations: { groupMember: {
101+
user: true, // 👈 THIS is the key
102+
}, user_admin: true, user_beneficiary: true },
92103
order: { id: "DESC" },
93104
});
94105

@@ -103,7 +114,13 @@ export default class GroupResolver {
103114
async getGroupById(@Arg("id") id: number) {
104115
const group = await Group.findOne({
105116
where: { id: id },
106-
relations: { user_admin: true, user_beneficiary: true, groupMember: true },
117+
relations: {
118+
user_admin: true,
119+
user_beneficiary: true,
120+
groupMember: {
121+
user: true,
122+
},
123+
},
107124
});
108125
if (!group) throw new Error("Groupe non trouvé");
109126
return group;
@@ -113,6 +130,7 @@ export default class GroupResolver {
113130
async groupMember(@Root() group: Group) {
114131
const groupMembers = await GroupMember.find({
115132
where: { groupId: group.id },
133+
relations: { user: true },
116134
});
117135

118136
return groupMembers || []; // >>> not null
@@ -252,12 +270,20 @@ export default class GroupResolver {
252270

253271
@UseMiddleware(RoleMiddleware())
254272
@Mutation(() => String)
255-
async removeMembersFromGroup(@Arg("groupId", () => Number) groupId: number,
256-
@Arg("userIds", () => [Number]) userIds: number[], @Ctx() ctx: ContextType ): Promise<string> {
273+
async removeMembersFromGroup(
274+
@Arg("groupId", () => Number) groupId: number,
275+
@Arg("data") data: RemoveMembersInput,
276+
@Ctx() ctx: ContextType
277+
): Promise<string> {
257278
if (!ctx.user) {
258279
throw new Error("Utilisateur non connecté");
259280
}
260281

282+
// Ensure at least one input is provided
283+
if ((!data.userIds || data.userIds.length === 0) && (!data.userEmails || data.userEmails.length === 0)) {
284+
throw new Error("Vous devez fournir au moins un identifiant utilisateur ou un email");
285+
}
286+
261287
const group = await Group.findOne({
262288
where: {
263289
id: groupId
@@ -268,55 +294,69 @@ export default class GroupResolver {
268294
if (!group) {
269295
throw new Error("Groupe introuvable ou accès refusé");
270296
}
271-
const currentUserId = ctx.user.id
272-
const isAdmin = ctx.user.id === group.user_admin.id
297+
298+
const currentUserId = ctx.user.id;
299+
const isAdmin = ctx.user.id === group.user_admin.id;
300+
301+
// Convert emails to user IDs if provided
302+
let userIdsToRemove: number[] = [];
273303

274-
275-
//Admin cannot remove itself from a group
276-
if (isAdmin && userIds.length > 0 && userIds.includes(group?.user_admin.id)) {
277-
throw new Error ("L'administrateur du groupe ne peut pas être supprimer. Supprimer plutôt le groupe")
304+
if (data.userEmails && data.userEmails.length > 0) {
305+
await Promise.all(
306+
data.userEmails.map(async (email) => {
307+
const user = await User.findOne({ where: { email } });
308+
if (user) {
309+
userIdsToRemove.push(user.id);
310+
}
311+
})
312+
);
313+
}
314+
315+
// Add direct user IDs if provided
316+
if (data.userIds && data.userIds.length > 0) {
317+
userIdsToRemove.push(...data.userIds);
318+
}
319+
320+
// Remove duplicates
321+
userIdsToRemove = [...new Set(userIdsToRemove)];
322+
323+
if (userIdsToRemove.length === 0) {
324+
throw new Error("Aucun utilisateur valide trouvé");
325+
}
326+
327+
// Admin cannot remove itself from a group
328+
if (isAdmin && userIdsToRemove.includes(group.user_admin.id)) {
329+
throw new Error("L'administrateur du groupe ne peut pas être supprimé. Supprimez plutôt le groupe");
278330
}
279331

280-
//Remove myself from a group
281-
if(!isAdmin && userIds.length > 0 && userIds.every((id) => (id === currentUserId))) {
332+
// Remove myself from a group
333+
if (!isAdmin && userIdsToRemove.length === 1 && userIdsToRemove[0] === currentUserId) {
282334
try {
283-
removeMembersFromGroup({
335+
await removeMembersFromGroup({
284336
userIds: [currentUserId],
285337
groupId,
286-
})
287-
288-
return "Succès! Vous ne faites plus partie du groupe!"
289-
338+
});
339+
return "Succès! Vous ne faites plus partie du groupe!";
290340
} catch (err) {
291-
console.error("deleteGroup error:", err);
341+
console.error("removeMembersFromGroup error:", err);
292342
throw new Error("Une erreur est survenue, nous n'avons pas pu vous supprimer du groupe");
293343
}
294-
295344
}
296-
297-
//Admin removes users from a group
298-
if (isAdmin && userIds.length > 0 && !userIds.includes(group?.user_admin.id) ) {
345+
346+
// Admin removes users from a group
347+
if (isAdmin && userIdsToRemove.length > 0 && !userIdsToRemove.includes(group.user_admin.id)) {
299348
try {
300-
removeMembersFromGroup({
301-
userIds,
349+
await removeMembersFromGroup({
350+
userIds: userIdsToRemove,
302351
groupId,
303-
})
304-
305-
return "Succès! Les utilisateurs ont été supprimé du groupe!"
306-
352+
});
353+
return "Succès! Les utilisateurs ont été supprimés du groupe!";
307354
} catch (err) {
308-
console.error("deleteGroup error:", err);
355+
console.error("removeMembersFromGroup error:", err);
309356
throw new Error("Une erreur est survenue, nous n'avons pas pu supprimer les utilisateurs du groupe");
310357
}
311-
312358
}
313359

314-
return "Un problème est survenu"
315-
316-
317-
318-
360+
throw new Error("Vous n'avez pas les permissions nécessaires pour effectuer cette action");
319361
}
320-
321-
322362
}

backend/src/resolvers/MessageResolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export default class MessageResolver {
9797

9898
if (!userId) throw new Error("Utilisateur non authentifié");
9999

100-
const group = await GroupMember.findOne({ where: { user: { id: userId }, group: { id: groupId } } });
100+
const group = await GroupMember.findOne({ where: { userId: userId, groupId: groupId } });
101101
// verifier que l'utilisateur fait bien partie du groupe
102102
if (!group) throw new Error("Groupe non trouvé");
103103

backend/src/services/groupMemberService.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,26 +47,23 @@ export async function addMembersToGroup({ userEmails, groupId }: AddMembersInput
4747
}
4848

4949
export async function removeMembersFromGroup({ userIds, groupId }: RemoveMembersInput) {
50-
if (!userIds.length) return;
50+
if (!userIds || userIds.length === 0) return;
5151

52+
// Remove all group members
5253
await Promise.all(
5354
userIds.map(async (userId) => {
54-
const groupMember = await GroupMember.findOne({ where: { id: userId , groupId: groupId}})
55-
56-
if (!groupMember) return
57-
58-
try {
59-
await GroupMember.remove(groupMember);
60-
return "L'utilisateur a été supprimé du groupe";
61-
} catch (err) {
62-
console.error("removeMembersFromGroup error:", err);
63-
throw new Error("Une erreur est survenue lors de la suppression de l'utilisateur du groupe");
64-
}
65-
66-
}))
67-
68-
69-
55+
const groupMember = await GroupMember.findOne({
56+
where: { userId: userId, groupId: groupId }
57+
});
7058

59+
if (!groupMember) return;
7160

61+
try {
62+
await GroupMember.remove(groupMember);
63+
} catch (err) {
64+
console.error("removeMembersFromGroup error:", err);
65+
throw new Error("Une erreur est survenue lors de la suppression de l'utilisateur du groupe");
66+
}
67+
})
68+
);
7269
}

frontend/src/components/forms/groups/GroupForm.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ type GroupFormProps = {
1515
isEdit: boolean;
1616
handleChange: any;
1717
checked: boolean;
18-
setChecked: any
18+
setChecked: any;
19+
isAdmin: boolean;
1920
};
2021

2122

22-
export default function GroupForm({ formData, errors, checked, setChecked, isEdit, handleChange }: GroupFormProps) {
23+
export default function GroupForm({ formData, errors, checked, setChecked, isEdit, handleChange, isAdmin }: GroupFormProps) {
24+
const disabled = isEdit && !isAdmin;
2325

2426
return (
2527
<>
@@ -31,6 +33,7 @@ export default function GroupForm({ formData, errors, checked, setChecked, isEdi
3133
</div>
3234
<div className="flex flex-col gap-4 px-20">
3335
<Input
36+
disabled={disabled}
3437
name="name"
3538
type="text"
3639
value={formData.name}
@@ -41,6 +44,7 @@ export default function GroupForm({ formData, errors, checked, setChecked, isEdi
4144
/>
4245

4346
<SearchSelectInput
47+
disabled={disabled}
4448
name="event_type"
4549
value={formData.event_type}
4650
onChange={(val) =>
@@ -56,6 +60,7 @@ export default function GroupForm({ formData, errors, checked, setChecked, isEdi
5660
/>
5761

5862
<Input
63+
disabled={disabled}
5964
name="piggy_bank"
6065
type="number"
6166
value={String(formData.piggy_bank)}
@@ -66,6 +71,7 @@ export default function GroupForm({ formData, errors, checked, setChecked, isEdi
6671
/>
6772

6873
<InputWithToggle
74+
disabled={disabled}
6975
checked={checked}
7076
onCheckedChange={() => {
7177
setChecked(!checked);
@@ -79,6 +85,7 @@ export default function GroupForm({ formData, errors, checked, setChecked, isEdi
7985
/>
8086

8187
<Input
88+
disabled={disabled}
8289
name="deadline"
8390
type="date"
8491
value={formData.deadline}

frontend/src/components/forms/groups/GroupFormTemplate.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ type GroupFormTemplateProps = {
77
right?: React.ReactNode;
88
isEdit: boolean;
99
submitError:any;
10-
errors: any
10+
errors: any;
11+
onSuccess: () => void;
12+
isAdmin: boolean;
1113
};
1214

13-
export default function GroupFormTemplate({ onSubmit, left, right, isEdit, submitError, errors }: GroupFormTemplateProps) {
15+
export default function GroupFormTemplate({ onSubmit, onSuccess, left, right, isEdit, submitError, errors, isAdmin }: GroupFormTemplateProps) {
1416
return (
15-
<form className="relative flex w-full h-full rounded-2xl" onSubmit={onSubmit} autoComplete="off">
17+
<form className="relative flex w-full h-full rounded-2xl" onSubmit={(e)=> {onSubmit(e); onSuccess()}} autoComplete="off">
1618
<div className="bg-green w-1/2 h-full flex flex-col justify-center pt-10 pb-5 rounded-tl-2xl rounded-bl-2xl">
1719
{left}
1820
</div>
@@ -21,7 +23,16 @@ export default function GroupFormTemplate({ onSubmit, left, right, isEdit, submi
2123
{right}
2224
</div>
2325
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2">
24-
<Button colour="dark" rounded type="submit">{isEdit ? "Mettre à jour" : "Créer le groupe"}</Button>
26+
{isEdit && isAdmin && (
27+
<Button colour="dark" rounded type="submit">Mettre à jour</Button>
28+
)}
29+
30+
{!isEdit && (
31+
<Button colour="dark" rounded type="submit">Créer le groupe</Button>
32+
33+
) }
34+
35+
2536
{submitError && (
2637
<p className="text-orange font-inter text-sm pt-1 text-center">
2738
{submitError}

0 commit comments

Comments
 (0)