Skip to content

Commit 4e18b98

Browse files
committed
rebased version + linter
2 parents 9a0c4e3 + 51ff3fd commit 4e18b98

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2573
-441
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: check-biome
2+
3+
on: push
4+
5+
jobs:
6+
check-biome:
7+
name: check-biome
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- name: Récupère le code du dépôt
12+
uses: actions/checkout@v4
13+
14+
- name: Setup Node
15+
uses: actions/setup-node@v4
16+
with:
17+
node-version: 22.20.0
18+
cache: "npm"
19+
20+
- name: Installe les dépendances
21+
run: npm install
22+
23+
- name: Exécute biome
24+
run: npm run husky-lint
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Test le build des services
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
branches-ignore:
7+
- main
8+
9+
jobs:
10+
build-and-push-projects:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
# pouvoir voir tout les builds qui échouent pas juste le premier
14+
fail-fast: false
15+
matrix:
16+
include:
17+
- nom_service: frontend
18+
- nom_service: backend
19+
- nom_service: gateway
20+
- nom_service: message-service
21+
- nom_service: picture-service
22+
steps:
23+
- name: Récupérer le code source
24+
uses: actions/checkout@v4
25+
26+
- name: setup Docker Buildx (pour du cache)
27+
uses: docker/setup-buildx-action@v3
28+
29+
- name: Build l'image Docker
30+
uses: docker/build-push-action@v4
31+
with:
32+
context: ./${{ matrix.nom_service }}
33+
file: ./${{ matrix.nom_service }}/Dockerfile.staging
34+
push: false
35+
load: false
36+
cache-from: type=gha
37+
cache-to: type=gha,mode=max
38+

.husky/pre-commit

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
npm run husky-lint
2-
3-
# si le paramètre bypass-bin est pas actif
4-
if [ "$bypass_bin" != "1" ]; then
5-
# Bloquer toute modification du dossier bin/
6-
if git diff --cached --name-only | grep -q "^bin/"; then
7-
echo ""
8-
echo "🚫 Vous n'avez pas le droit de modifier le contenu du dossier \"bin/\""
9-
echo "Ce fichier est sensible donc vous devez étre sûr de ce que vous faites."
10-
echo ""
11-
exit 1
12-
fi
1+
npm run husky-lint
2+
3+
# si le paramètre bypass-bin est pas actif
4+
# bypass_bin=1 git commit
5+
if [ "$bypass_bin" != "1" ]; then
6+
# Bloquer toute modification du dossier bin/
7+
if git diff --cached --name-only | grep -q "^bin/"; then
8+
echo ""
9+
echo "🚫 Vous n'avez pas le droit de modifier le contenu du dossier \"bin/\""
10+
echo "Ce fichier est sensible donc vous devez étre sûr de ce que vous faites."
11+
echo ""
12+
exit 1
13+
fi
1314
fi

backend/src/entities/Group.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class Group extends BaseEntity {
4545
@Field()
4646
event_type: string;
4747

48-
@Column()
48+
@Column({ default: 0 })
4949
@Field()
5050
piggy_bank: number;
5151

backend/src/entities/GroupMember.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ export class GroupMember extends BaseEntity {
3939
@Field()
4040
isGroupAdmin: boolean;
4141

42+
@Column({ type: "timestamptz", default: () => "CURRENT_TIMESTAMP" })
43+
@Field()
44+
lastTempstampVu: Date;
45+
4246
@Field(() => User)
4347
@ManyToOne(
4448
() => User,

backend/src/resolvers/GroupResolver.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ class CreateGroupInput {
3030
@Field()
3131
event_type!: string;
3232

33-
@Field()
34-
piggy_bank!: number;
33+
@Field({ nullable: true, defaultValue: 0 })
34+
piggy_bank?: number;
3535

3636
@Field()
3737
deadline!: Date;
@@ -42,6 +42,15 @@ class CreateGroupInput {
4242
user_beneficiary?: string;
4343
}
4444

45+
@InputType()
46+
class AddFundsInput {
47+
@Field()
48+
groupId!: number;
49+
50+
@Field()
51+
amount!: number;
52+
}
53+
4554
@InputType()
4655
class UpdateGroupInput {
4756
@Field()
@@ -176,7 +185,7 @@ export default class GroupResolver {
176185
user_admin: userAdmin,
177186
name: data.name,
178187
event_type: data.event_type,
179-
piggy_bank: data.piggy_bank,
188+
piggy_bank: data.piggy_bank ?? 0,
180189
deadline: data.deadline,
181190
user_beneficiary: beneficiaryUser ?? undefined,
182191
});
@@ -212,6 +221,36 @@ export default class GroupResolver {
212221
return group;
213222
}
214223

224+
@UseMiddleware(RoleMiddleware())
225+
@Mutation(() => Group)
226+
async addFundsToGroup(@Arg("data") data: AddFundsInput, @Ctx() ctx: ContextType) {
227+
if (!ctx.user) throw new Error("Utilisateur non connecté");
228+
229+
// Vérifier que le groupe existe
230+
const group = await Group.findOne({
231+
where: { id: data.groupId },
232+
relations: { groupMember: true },
233+
});
234+
235+
if (!group) throw new Error("Groupe introuvable");
236+
237+
// Vérifier que l'utilisateur est membre du groupe
238+
const isMember = await GroupMember.findOne({
239+
where: { groupId: data.groupId, userId: ctx.user.id },
240+
});
241+
242+
if (!isMember) throw new Error("Vous n'êtes pas membre de ce groupe");
243+
244+
// Vérifier que le montant est positif
245+
if (data.amount <= 0) throw new Error("Le montant doit être positif");
246+
247+
// Ajouter les fonds à la cagnotte
248+
group.piggy_bank += data.amount;
249+
await group.save();
250+
251+
return group;
252+
}
253+
215254
@UseMiddleware(RoleMiddleware())
216255
@Mutation(() => Group)
217256
async updateGroup(@Arg("id") id: number, @Arg("data") data: UpdateGroupInput) {

backend/src/resolvers/MessageResolver.ts

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
Resolver,
1111
UseMiddleware,
1212
} from "type-graphql";
13+
import { LessThan } from "typeorm";
1314
import Group from "../entities/Group";
1415
import { GroupMember } from "../entities/GroupMember";
1516
import { Message } from "../entities/Message";
@@ -39,6 +40,39 @@ class GroupMessagesOutput {
3940

4041
@Field(() => [Message])
4142
messages: Message[];
43+
44+
@Field()
45+
lastTempstampVu: Date;
46+
}
47+
48+
@InputType()
49+
class GetLazyMessagesInput {
50+
@Field()
51+
groupId: number;
52+
53+
@Field()
54+
oldTimestamp: string;
55+
}
56+
57+
@ObjectType()
58+
class GetLazyMessagesOutput {
59+
@Field()
60+
isMaximumMessages: boolean;
61+
62+
@Field(() => [Message])
63+
messages: Message[];
64+
}
65+
66+
@InputType()
67+
class SetLastMessageVuInput {
68+
@Field()
69+
groupId: number;
70+
}
71+
72+
@ObjectType()
73+
class SetLastMessageVuOutput {
74+
@Field()
75+
sucess: boolean;
4276
}
4377

4478
@Resolver(Group)
@@ -60,17 +94,17 @@ export default class MessageResolver {
6094
order: { id: "DESC" },
6195
});
6296

63-
const reponse: { groupId: number; messages: Message[] }[] = [];
97+
const reponse: { groupId: number; messages: Message[]; lastTempstampVu: Date }[] = [];
6498

65-
// charge les 10 derniers messages de chaque groupe
99+
// charge les 40 derniers messages de chaque groupe
66100
for (const group of groups) {
67101
const messages = await Message.find({
68102
where: { group: { id: group.id } },
69103
relations: { user: true },
70104
order: { createdAt: "DESC" },
71-
take: 20,
105+
take: 40,
72106
});
73-
reponse.push({ groupId: group.id, messages });
107+
reponse.push({ groupId: group.id, messages, lastTempstampVu: group.groupMember[0].lastTempstampVu });
74108
}
75109

76110
return reponse;
@@ -112,4 +146,67 @@ export default class MessageResolver {
112146
relations: ["user", "group"],
113147
});
114148
}
149+
150+
@UseMiddleware(RoleMiddleware())
151+
@Query(() => GetLazyMessagesOutput)
152+
async getLazyMessages(@Arg("data") data: GetLazyMessagesInput) {
153+
const { groupId, oldTimestamp } = data;
154+
155+
let oldDate: Date;
156+
try {
157+
oldDate = new Date(oldTimestamp);
158+
if (Number.isNaN(oldDate.getTime())) {
159+
throw new Error("Invalid date");
160+
}
161+
} catch {
162+
throw new Error("Invalid date format");
163+
}
164+
165+
// récupère les 40 premiers messages plus anciens que oldTimestamp
166+
const messages = await Message.find({
167+
where: { group: { id: groupId }, createdAt: LessThan(oldDate) },
168+
relations: { user: true },
169+
order: { createdAt: "DESC" },
170+
take: 40,
171+
});
172+
173+
const oldestMessage = await Message.findOne({
174+
where: { group: { id: groupId } },
175+
order: { createdAt: "ASC" },
176+
select: { id: true, createdAt: true },
177+
});
178+
179+
const isMaximumMessages = oldestMessage ? messages.some((msg) => msg.id === oldestMessage.id) : true;
180+
181+
// renvoie les messages et si c'est tout les messages
182+
return { isMaximumMessages, messages: messages };
183+
}
184+
185+
// met a jour le vu du dernier message pour un groupe donné
186+
@UseMiddleware(RoleMiddleware())
187+
@Mutation(() => SetLastMessageVuOutput)
188+
async setLastMessageVu(@Arg("data") data: SetLastMessageVuInput, @Ctx() ctx: ContextType) {
189+
const { groupId } = data;
190+
191+
const userId = ctx.user?.id;
192+
if (!userId) throw new Error("Utilisateur non authentifié");
193+
194+
// récupère le dernier message du groupe
195+
const lastMessage = await Message.findOne({
196+
where: { group: { id: groupId } },
197+
order: { createdAt: "DESC" },
198+
});
199+
200+
if (lastMessage) {
201+
// met a jour le vu du dernier message pour l'utilisateur
202+
await GroupMember.update(
203+
{ user: { id: userId }, group: { id: groupId } },
204+
{ lastTempstampVu: lastMessage.createdAt },
205+
);
206+
207+
return { sucess: true };
208+
}
209+
210+
return { sucess: false };
211+
}
115212
}

backend/src/resolvers/UserResolver.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class UpdateMyProfileInput {
4747
@Field()
4848
date_of_birth!: string;
4949
@Field()
50-
phone_number!: string;
50+
phone_number?: string;
5151
@Field(() => String, { nullable: true })
5252
pictureBase64?: string;
5353
}
@@ -261,13 +261,14 @@ export default class UserResolver {
261261
password: undefined,
262262
image_url: urlImage ? urlImage.data.url : undefined,
263263
pictureBase64: undefined,
264+
phone_number: data.phone_number ?? undefined,
264265
};
265266

266267
// modifie l'utilisateur connecté
267268
await User.update({ id: ctx.user.id }, newData);
268269

269270
//récupère le profil de l'utilisateur connecté
270-
const user = await User.findOne({ where: { id: ctx.user.id } });
271+
const user = await User.findOne({ where: { id: ctx.user.id }, relations: ["lists"] });
271272

272273
return user as User;
273274
}

frontend/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!doctype html>
2-
<html lang="en">
2+
<html lang="fr">
33

44
<head>
55
<meta charset="UTF-8" />

0 commit comments

Comments
 (0)