Skip to content

Commit da9702b

Browse files
authored
Merge pull request #300 from HerrBertling/feat/add-csv-export
feat: add admin area with CSV download
2 parents 9fbaf78 + bfe551e commit da9702b

File tree

2 files changed

+91
-1
lines changed

2 files changed

+91
-1
lines changed

app/root.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default function App() {
8686

8787
useEffect(() => {
8888
setShouldTrack(track);
89-
}, [track]);
89+
}, []);
9090

9191
useEffect(() => {
9292
if (shouldTrack) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import type { ActionFunctionArgs } from "@remix-run/node";
2+
import { Form, useActionData } from "@remix-run/react";
3+
import { getCoaches } from "~/utils/contentful";
4+
5+
export async function action({ request }: ActionFunctionArgs) {
6+
const formData = await request.formData();
7+
const pw = formData.get("pw");
8+
9+
console.log({ pw, envPw: process.env.CSV_DOWNLOAD_PW });
10+
11+
if (pw !== process.env.CSV_DOWNLOAD_PW) {
12+
return { error: "wrong password", body: null };
13+
}
14+
const coaches = await getCoaches();
15+
16+
const csvHeader = "Name,E-Mail,Webseite,Telefon,Sprachen,Geschlechter,\n";
17+
18+
const csvBody = coaches
19+
.map(
20+
(coach) =>
21+
`${coach.fields?.name ?? ""},${coach.fields?.email ?? ""},${coach.fields?.url ?? ""},${coach.fields?.phone ?? ""},${coach.fields?.languages?.join("; ") ?? ""},${coach.fields.gender?.join("; ") ?? ""},`,
22+
)
23+
.join("\n");
24+
25+
const csv = `${csvHeader}${csvBody}`;
26+
27+
return { body: csv, error: null };
28+
}
29+
30+
export default function Component() {
31+
const actionData = useActionData<typeof action>();
32+
33+
const error = actionData?.error ?? null;
34+
35+
const downloadCSV = () => {
36+
if (!actionData?.body) return;
37+
const csvData = new Blob([actionData?.body], { type: "text/csv" });
38+
const csvURL = URL.createObjectURL(csvData);
39+
const link = document.createElement("a");
40+
link.href = csvURL;
41+
link.download = `coaches-data.csv`;
42+
document.body.appendChild(link);
43+
link.click();
44+
document.body.removeChild(link);
45+
};
46+
47+
return (
48+
<div className="container mx-auto max-w-6xl py-40 px-2 md:px-4 lg:px-8">
49+
<div className="flex flex-col gap-12">
50+
<h1 className="text-3xl">Daten herunterladen</h1>
51+
<Form
52+
reloadDocument
53+
method="POST"
54+
className="flex flex-col gap-4 max-w-md"
55+
>
56+
<label className="flex flex-col gap-2">
57+
<span>Download-Passwort</span>
58+
<input
59+
className="p-2 rounded-sm border border-slate-400 active:border-vsp-500"
60+
name="pw"
61+
type="password"
62+
/>
63+
{error ? (
64+
<span className="text-red-600 text-sm">
65+
Falsches Passwort, sorry!
66+
</span>
67+
) : null}
68+
</label>
69+
<button
70+
type="submit"
71+
className="font-inherit inline-flex items-center justify-center rounded-md py-2 px-4 text-white no-underline transition-opacity duration-300 hover:opacity-90 focus:opacity-90 active:opacity-90 md:text-lg bg-vsp-500"
72+
>
73+
Download anfragen
74+
</button>
75+
</Form>
76+
{actionData?.body ? (
77+
<button
78+
type="button"
79+
onClick={() => {
80+
downloadCSV();
81+
}}
82+
className="font-inherit inline-flex items-center justify-center rounded-md py-2 px-4 text-white no-underline transition-opacity duration-300 hover:opacity-90 focus:opacity-90 active:opacity-90 md:text-lg bg-vsp-500"
83+
>
84+
CSV herunterladen
85+
</button>
86+
) : null}
87+
</div>
88+
</div>
89+
);
90+
}

0 commit comments

Comments
 (0)