Skip to content

Commit 42d4a0d

Browse files
authored
feat: add optional photo input for the adhesion form (#110)
1 parent 9939ab6 commit 42d4a0d

8 files changed

Lines changed: 110 additions & 3 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "Adhesion" ADD COLUMN "photosPaths" TEXT[] DEFAULT ARRAY[]::TEXT[];

prisma/seed.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,12 @@ async function main() {
289289
category: "Adhésions",
290290
description: "Télécharger les dossiers d'adhésion"
291291
},
292+
{
293+
name: "download:adhesion-folder",
294+
title: "Télécharger Dossier",
295+
category: "Adhésions",
296+
description: "Télécharger les dossiers d'adhésion"
297+
},
292298

293299
// Bouge Ta Prison
294300
{

schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ model Adhesion {
233233
lettreEngagementPath String?
234234
reglementInterieurPath String?
235235
bilanFinancierPath String?
236+
photosPaths String[]
236237
bureau Json?
237238
linkedAssociation Association?
238239
}

src/actions/adhesion/downloadFolderAction.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ async function downloadFolderActionImpl(
5757
adhesion.value.extraitPVPath,
5858
adhesion.value.lettreEngagementPath,
5959
adhesion.value.reglementInterieurPath,
60-
adhesion.value.bilanFinancierPath
60+
adhesion.value.bilanFinancierPath,
61+
...adhesion.value.photosPaths
6162
].filter((path): path is string => Boolean(path))
6263

6364
const supabase = await createClient()

src/app/(public)/a-propos/adhesion/form-schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export const bureauMemberSchema = type({
1414
adresse: "string >= 1"
1515
})
1616

17+
const photosSchema = type(fileSchema({ mimeType: ["image", "pdf"] }))
18+
.array()
19+
.atMostLength(15)
20+
1721
export const AdhesionFormSchema = type({
1822
// Basic info
1923
sigle: "string >= 2",
@@ -51,6 +55,7 @@ export const AdhesionFormSchema = type({
5155
"lettreEngagement?": fileSchema({ optional: true }),
5256
"reglementInterieur?": fileSchema({ optional: true }),
5357
"bilanFinancier?": fileSchema({ optional: true }),
58+
"photos?": photosSchema,
5459

5560
// Captcha
5661
captchaToken: "string >= 1"

src/app/(public)/a-propos/adhesion/form.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ import { processAdhesion } from "./process-adhesion"
5555
/** Max file size: 2 MB (in bytes) */
5656
const MAX_FILE_SIZE = 2 * 1024 * 1024
5757

58+
/** Max photo size: 5 MB (in bytes) */
59+
const MAX_PHOTO_SIZE = 5 * 1024 * 1024
60+
5861
const emptyBureauMember: BureauMember = {
5962
isAdmin: false,
6063
poste: "",
@@ -91,6 +94,7 @@ const emptyForm = {
9194
lettreEngagement: undefined as unknown as File,
9295
reglementInterieur: undefined as unknown as File,
9396
bilanFinancier: undefined as unknown as File,
97+
photos: [] as File[],
9498
captchaToken: ""
9599
}
96100

@@ -1380,6 +1384,61 @@ export function AdhesionForm(): React.ReactNode {
13801384
)
13811385
}}
13821386
/>
1387+
1388+
<form.Field
1389+
name="photos"
1390+
children={(field) => {
1391+
const isInvalid =
1392+
field.state.meta.isTouched &&
1393+
!field.state.meta.isValid
1394+
return (
1395+
<Field data-invalid={isInvalid}>
1396+
<FieldLabel
1397+
htmlFor={field.name}
1398+
>
1399+
Trombinoscope{" "}
1400+
<span className="text-muted-foreground">
1401+
(optionnel)
1402+
</span>
1403+
</FieldLabel>
1404+
<FieldDescription>
1405+
Ajoutez une photo de votre
1406+
bureau pour qu'on apprenne à
1407+
vous reconnaître !<br />
1408+
Formats acceptés : images
1409+
(PNG, JPG, WebP, SVG) ou
1410+
PDF. Jusqu'à 15 fichiers, 5
1411+
Mo maximum par fichier.
1412+
</FieldDescription>
1413+
<FilePondInput
1414+
allowMultiple
1415+
maxFiles={15}
1416+
maxFileSize={`${MAX_PHOTO_SIZE / (1024 * 1024)}MB`}
1417+
acceptedFileTypes={[
1418+
"image/png",
1419+
"image/jpeg",
1420+
"image/webp",
1421+
"image/svg+xml",
1422+
"application/pdf"
1423+
]}
1424+
onChangeMultiple={(files) =>
1425+
field.handleChange(
1426+
files
1427+
)
1428+
}
1429+
/>
1430+
{isInvalid && (
1431+
<FieldError
1432+
errors={
1433+
field.state.meta
1434+
.errors
1435+
}
1436+
/>
1437+
)}
1438+
</Field>
1439+
)
1440+
}}
1441+
/>
13831442
</FieldGroup>
13841443
</FieldSet>
13851444

src/app/(public)/a-propos/adhesion/process-adhesion.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const logoExtensionByMime: Record<string, string> = {
2323
"image/png": "png",
2424
"image/jpeg": "jpg",
2525
"image/webp": "webp",
26-
"image/svg+xml": "svg"
26+
"application/pdf": "pdf"
2727
}
2828

2929
type Result = { success: true } | { success: false; message: string }
@@ -131,6 +131,37 @@ async function processAdhesionImpl(formData: TAdhesionForm): Promise<Result> {
131131
}
132132
}
133133

134+
const photoUploads = await tryCatch(
135+
Promise.all(
136+
(data.photos ?? []).map((photo, i) => {
137+
const ext = logoExtensionByMime[photo.type]
138+
return ext
139+
? upload(`photo-${i + 1}.${ext}`, photo)
140+
: Promise.resolve(null)
141+
})
142+
)
143+
)
144+
if (!photoUploads.success) {
145+
captureActionError(photoUploads.error)
146+
await cleanup()
147+
return {
148+
success: false,
149+
message:
150+
"Échec de l'envoi des fichiers. Veuillez réessayer plus tard."
151+
}
152+
}
153+
if (photoUploads.value.some((path) => !path)) {
154+
await cleanup()
155+
return {
156+
success: false,
157+
message:
158+
"Échec de l'envoi des fichiers. Veuillez réessayer plus tard."
159+
}
160+
}
161+
const photosPaths = photoUploads.value.filter(
162+
(path): path is string => !!path
163+
)
164+
134165
const created = await tryCatch(
135166
prisma.adhesion.create({
136167
data: {
@@ -158,7 +189,8 @@ async function processAdhesionImpl(formData: TAdhesionForm): Promise<Result> {
158189
extraitPVPath,
159190
lettreEngagementPath,
160191
reglementInterieurPath,
161-
bilanFinancierPath
192+
bilanFinancierPath,
193+
photosPaths
162194
}
163195
})
164196
)

src/test/factories/adhesion.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export function validAdhesionRecord(
7878
lettreEngagementPath: null,
7979
reglementInterieurPath: null,
8080
bilanFinancierPath: null,
81+
photosPaths: [],
8182
bureau: [bureauMember],
8283
...overrides
8384
}

0 commit comments

Comments
 (0)