From 2b9b989d24e718b031a671127f279d46627a29c3 Mon Sep 17 00:00:00 2001
From: Marc-Antoine Manningham <marc-antoine.m@outlook.com>
Date: Tue, 12 Nov 2024 00:18:39 -0500
Subject: [PATCH] feat: add download csv button

---
 .../src/components/forms/ParticipantForm.tsx  | 454 ++++++++++--------
 1 file changed, 246 insertions(+), 208 deletions(-)

diff --git a/client/src/components/forms/ParticipantForm.tsx b/client/src/components/forms/ParticipantForm.tsx
index 0a2a8c1..7e204e4 100644
--- a/client/src/components/forms/ParticipantForm.tsx
+++ b/client/src/components/forms/ParticipantForm.tsx
@@ -17,6 +17,34 @@ interface ParticipantRowProps {
     refetch: () => void
 }
 
+function jsonToCsv(items: ParticipantPreview[]) {
+    if (items.length === 0) {
+        return ""
+    }
+    const headers = Object.keys(items[0] as any)
+    const csvRows = [
+        headers
+            .filter((header) => {
+                return header != "contain_cv" && header != "id"
+            })
+            .join(","),
+    ]
+
+    items.forEach((item: any) => {
+        const values = headers
+            .filter((header) => {
+                return header != "contain_cv" && header != "id"
+            })
+            .map((header) => {
+                const escaped = ("" + item[header]).replace(/"/g, '""')
+                return `"${escaped}"`
+            })
+        csvRows.push(values.join(","))
+    })
+
+    return csvRows.join("\n")
+}
+
 function ParticipantRow(props: ParticipantRowProps) {
     const p = props.participant
     const [isModalOpen, setIsModalOpen] = createSignal(false)
@@ -76,13 +104,6 @@ function ParticipantRow(props: ParticipantRowProps) {
     )
 }
 
-async function fetchWrapper() {
-    console.log("fetching")
-    const participants = await fetchParticipants()
-    console.log(participants)
-    return participants
-}
-
 function getGivableRole() {
     const role = localStorage.getItem("role")
     if (role == "organizer") {
@@ -121,231 +142,162 @@ function getGivableRole() {
 }
 
 export default function ParticipantForm() {
-    const [user, { refetch }] = createResource(fetchWrapper)
+    const [user, { refetch }] = createResource(fetchParticipants)
     const [_form, { Form, Field }] = createForm<MinimalParticipant>()
     const onSubmit = async (data: MinimalParticipant) => {
-        console.log("submitting")
         await submitMinimalParticipant(data)
-        console.log("refetching")
         refetch()
     }
+    const download = () => {
+        const csv = jsonToCsv(user())
+        const blob = new Blob([csv], { type: "text/csv" })
+        const url = window.URL.createObjectURL(blob)
+        const a = document.createElement("a")
+        a.href = url
+        a.download = "participants.csv"
+        a.click()
+        window.URL.revokeObjectURL(url)
+    }
 
     return (
-        <Form onSubmit={onSubmit}>
-            <table class="min-w-full border border-gray-300 bg-white">
-                <thead>
-                    <tr class="bg-gray-100">
-                        <th class="border-b border-gray-300 p-2 text-center">
-                            Prénom
-                        </th>
-                        <th class="border-b border-gray-300 p-2 text-center">
-                            Nom
-                        </th>
-                        <th class="border-b border-gray-300 p-2 text-center">
-                            Courriel
-                        </th>
-                        <th class="border-b border-gray-300 p-2 text-center">
-                            Compétition
-                        </th>
-                        <th class="border-b border-gray-300 p-2 text-center">
-                            Rôle
-                        </th>
-                        {localStorage.getItem("role") === "organizer" && (
+        <div class="flex flex-col gap-4">
+            <button
+                onclick={download}
+                class="w-64 rounded bg-blue-500 p-2 text-white hover:bg-blue-700"
+            >
+                Télécharger CSV
+            </button>
+            <Form onSubmit={onSubmit}>
+                <table class="min-w-full border border-gray-300 bg-white">
+                    <thead>
+                        <tr class="bg-gray-100">
                             <th class="border-b border-gray-300 p-2 text-center">
-                                Université
+                                Prénom
+                            </th>
+                            <th class="border-b border-gray-300 p-2 text-center">
+                                Nom
+                            </th>
+                            <th class="border-b border-gray-300 p-2 text-center">
+                                Courriel
+                            </th>
+                            <th class="border-b border-gray-300 p-2 text-center">
+                                Compétition
+                            </th>
+                            <th class="border-b border-gray-300 p-2 text-center">
+                                Rôle
                             </th>
-                        )}
-                        <th class="border-b border-gray-300 p-2 text-center">
-                            Actions
-                        </th>
-                    </tr>
-                </thead>
-                <tbody>
-                    <For each={user()}>
-                        {(participant: ParticipantPreview) => (
-                            <ParticipantRow
-                                participant={participant}
-                                refetch={refetch}
-                            />
-                        )}
-                    </For>
-                    {localStorage.getItem("role") !== "volunteer" && (
-                        <tr class="border-b border-gray-200">
-                            <td class="p-2">
-                                <Field name="first_name">
-                                    {(field, props) => (
-                                        <TextInput
-                                            {...props}
-                                            value={field.value}
-                                            error={field.error}
-                                            class="w-48"
-                                            type="text"
-                                            placeholder="Prénom"
-                                            required
-                                        />
-                                    )}
-                                </Field>
-                            </td>
-                            <td class="p-2">
-                                <Field name="last_name">
-                                    {(field, props) => (
-                                        <TextInput
-                                            {...props}
-                                            value={field.value}
-                                            error={field.error}
-                                            class="w-48"
-                                            type="text"
-                                            placeholder="Nom"
-                                            required
-                                        />
-                                    )}
-                                </Field>
-                            </td>
-                            <td class="p-2">
-                                <Field name="email">
-                                    {(field, props) => (
-                                        <TextInput
-                                            {...props}
-                                            value={field.value}
-                                            error={field.error}
-                                            type="email"
-                                            placeholder="exemple@courriel.com"
-                                            required
-                                        />
-                                    )}
-                                </Field>
-                            </td>
-                            <td class="p-2">
-                                <Field name="competition">
-                                    {(field, props) => (
-                                        <Select
-                                            {...props}
-                                            value={field.value}
-                                            error={field.error}
-                                            options={[
-                                                {
-                                                    label: "Aucune",
-                                                    value: "none",
-                                                },
-                                                {
-                                                    label: "Conception Senior",
-                                                    value: "conception_senior",
-                                                },
-                                                {
-                                                    label: "Conception Junior",
-                                                    value: "conception_junior",
-                                                },
-                                                {
-                                                    label: "Débats oratoires",
-                                                    value: "debats_oratoires",
-                                                },
-                                                {
-                                                    label: "Reingénierie",
-                                                    value: "reingenierie",
-                                                },
-                                                {
-                                                    label: "Génie Conseil",
-                                                    value: "genie_conseil",
-                                                },
-                                                {
-                                                    label: "Communication Scientifique",
-                                                    value: "communication_scientifique",
-                                                },
-                                                {
-                                                    label: "Programmation",
-                                                    value: "programmation",
-                                                },
-                                                {
-                                                    label: "Conception innovatrice",
-                                                    value: "conception_innovatrice",
-                                                },
-                                                {
-                                                    label: "Cycle supérieur",
-                                                    value: "cycle_superieur",
-                                                },
-                                            ]}
-                                            required
-                                        />
-                                    )}
-                                </Field>
-                            </td>
-                            <td class="p-2">
-                                <Field name="role">
-                                    {(field, props) => (
-                                        <Select
-                                            {...props}
-                                            value={field.value}
-                                            error={field.error}
-                                            options={getGivableRole()}
-                                            required
-                                        />
-                                    )}
-                                </Field>
-                            </td>
                             {localStorage.getItem("role") === "organizer" && (
+                                <th class="border-b border-gray-300 p-2 text-center">
+                                    Université
+                                </th>
+                            )}
+                            <th class="border-b border-gray-300 p-2 text-center">
+                                Actions
+                            </th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <For each={user()}>
+                            {(participant: ParticipantPreview) => (
+                                <ParticipantRow
+                                    participant={participant}
+                                    refetch={refetch}
+                                />
+                            )}
+                        </For>
+                        {localStorage.getItem("role") !== "volunteer" && (
+                            <tr class="border-b border-gray-200">
+                                <td class="p-2">
+                                    <Field name="first_name">
+                                        {(field, props) => (
+                                            <TextInput
+                                                {...props}
+                                                value={field.value}
+                                                error={field.error}
+                                                class="w-48"
+                                                type="text"
+                                                placeholder="Prénom"
+                                                required
+                                            />
+                                        )}
+                                    </Field>
+                                </td>
                                 <td class="p-2">
-                                    <Field name="university">
+                                    <Field name="last_name">
+                                        {(field, props) => (
+                                            <TextInput
+                                                {...props}
+                                                value={field.value}
+                                                error={field.error}
+                                                class="w-48"
+                                                type="text"
+                                                placeholder="Nom"
+                                                required
+                                            />
+                                        )}
+                                    </Field>
+                                </td>
+                                <td class="p-2">
+                                    <Field name="email">
+                                        {(field, props) => (
+                                            <TextInput
+                                                {...props}
+                                                value={field.value}
+                                                error={field.error}
+                                                type="email"
+                                                placeholder="exemple@courriel.com"
+                                                required
+                                            />
+                                        )}
+                                    </Field>
+                                </td>
+                                <td class="p-2">
+                                    <Field name="competition">
                                         {(field, props) => (
                                             <Select
                                                 {...props}
-                                                value="none"
+                                                value={field.value}
                                                 error={field.error}
                                                 options={[
                                                     {
-                                                        label: "UQAC",
-                                                        value: "uqac",
-                                                    },
-                                                    {
-                                                        label: "UQAR",
-                                                        value: "uqar",
-                                                    },
-                                                    {
-                                                        label: "UQAT",
-                                                        value: "uqat",
-                                                    },
-                                                    {
-                                                        label: "UQO",
-                                                        value: "uqo",
-                                                    },
-                                                    {
-                                                        label: "UQTR",
-                                                        value: "uqtr",
+                                                        label: "Aucune",
+                                                        value: "none",
                                                     },
                                                     {
-                                                        label: "McGill",
-                                                        value: "mcgill",
+                                                        label: "Conception Senior",
+                                                        value: "conception_senior",
                                                     },
                                                     {
-                                                        label: "McGill Macdonald",
-                                                        value: "mcgill_macdonald",
+                                                        label: "Conception Junior",
+                                                        value: "conception_junior",
                                                     },
                                                     {
-                                                        label: "Concordia",
-                                                        value: "concordia",
+                                                        label: "Débats oratoires",
+                                                        value: "debats_oratoires",
                                                     },
                                                     {
-                                                        label: "ETS",
-                                                        value: "ets",
+                                                        label: "Reingénierie",
+                                                        value: "reingenierie",
                                                     },
                                                     {
-                                                        label: "PolyMTL",
-                                                        value: "polymtl",
+                                                        label: "Génie Conseil",
+                                                        value: "genie_conseil",
                                                     },
                                                     {
-                                                        label: "ULaval",
-                                                        value: "ulaval",
+                                                        label: "Communication Scientifique",
+                                                        value: "communication_scientifique",
                                                     },
                                                     {
-                                                        label: "Drummondville",
-                                                        value: "drummond",
+                                                        label: "Programmation",
+                                                        value: "programmation",
                                                     },
                                                     {
-                                                        label: "UDS",
-                                                        value: "uds",
+                                                        label: "Conception innovatrice",
+                                                        value: "conception_innovatrice",
                                                     },
                                                     {
-                                                        label: "Aucune",
-                                                        value: "none",
+                                                        label: "Cycle supérieur",
+                                                        value: "cycle_superieur",
                                                     },
                                                 ]}
                                                 required
@@ -353,16 +305,102 @@ export default function ParticipantForm() {
                                         )}
                                     </Field>
                                 </td>
-                            )}
-                            <td class="p-2">
-                                <button class="rounded bg-green-500 p-1 font-bold text-white hover:bg-green-700">
-                                    <PlusCircle class="h-8 w-8"></PlusCircle>
-                                </button>
-                            </td>
-                        </tr>
-                    )}
-                </tbody>
-            </table>
-        </Form>
+                                <td class="p-2">
+                                    <Field name="role">
+                                        {(field, props) => (
+                                            <Select
+                                                {...props}
+                                                value={field.value}
+                                                error={field.error}
+                                                options={getGivableRole()}
+                                                required
+                                            />
+                                        )}
+                                    </Field>
+                                </td>
+                                {localStorage.getItem("role") ===
+                                    "organizer" && (
+                                    <td class="p-2">
+                                        <Field name="university">
+                                            {(field, props) => (
+                                                <Select
+                                                    {...props}
+                                                    value="none"
+                                                    error={field.error}
+                                                    options={[
+                                                        {
+                                                            label: "UQAC",
+                                                            value: "uqac",
+                                                        },
+                                                        {
+                                                            label: "UQAR",
+                                                            value: "uqar",
+                                                        },
+                                                        {
+                                                            label: "UQAT",
+                                                            value: "uqat",
+                                                        },
+                                                        {
+                                                            label: "UQO",
+                                                            value: "uqo",
+                                                        },
+                                                        {
+                                                            label: "UQTR",
+                                                            value: "uqtr",
+                                                        },
+                                                        {
+                                                            label: "McGill",
+                                                            value: "mcgill",
+                                                        },
+                                                        {
+                                                            label: "McGill Macdonald",
+                                                            value: "mcgill_macdonald",
+                                                        },
+                                                        {
+                                                            label: "Concordia",
+                                                            value: "concordia",
+                                                        },
+                                                        {
+                                                            label: "ETS",
+                                                            value: "ets",
+                                                        },
+                                                        {
+                                                            label: "PolyMTL",
+                                                            value: "polymtl",
+                                                        },
+                                                        {
+                                                            label: "ULaval",
+                                                            value: "ulaval",
+                                                        },
+                                                        {
+                                                            label: "Drummondville",
+                                                            value: "drummond",
+                                                        },
+                                                        {
+                                                            label: "UDS",
+                                                            value: "uds",
+                                                        },
+                                                        {
+                                                            label: "Aucune",
+                                                            value: "none",
+                                                        },
+                                                    ]}
+                                                    required
+                                                />
+                                            )}
+                                        </Field>
+                                    </td>
+                                )}
+                                <td class="p-2">
+                                    <button class="rounded bg-green-500 p-1 font-bold text-white hover:bg-green-700">
+                                        <PlusCircle class="h-8 w-8"></PlusCircle>
+                                    </button>
+                                </td>
+                            </tr>
+                        )}
+                    </tbody>
+                </table>
+            </Form>
+        </div>
     )
 }
-- 
GitLab