Skip to content

Commit 0ad967e

Browse files
committed
feat: useForm and enable academic year selection
1 parent dc432ab commit 0ad967e

4 files changed

Lines changed: 80 additions & 39 deletions

File tree

collection/app/(app)/PageActions.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1+
import { AcademicYear } from "@docsoc/eactivities";
12
import { Paper, MultiSelect, Group, Button } from "@mantine/core";
3+
import { UseFormReturnType } from "@mantine/form";
24
import React from "react";
35

4-
export const PageActions = () => {
6+
export const PageActions = ({
7+
formHook,
8+
academicYears,
9+
}: {
10+
formHook: UseFormReturnType<{ academicYears: AcademicYear[]; shortcode: string }>;
11+
academicYears: string[];
12+
}) => {
513
return (
614
<Paper p="lg" withBorder>
715
<Group justify="space-between">
816
<MultiSelect
917
label="Show purchases from academic years"
1018
description=" "
11-
defaultValue={["23-24"]}
12-
data={["23-24", "24-25"]}
19+
key={formHook.key("academicYears")}
20+
data={academicYears}
21+
{...formHook.getInputProps("academicYears")}
1322
/>
1423
<Button>Import data from eActivities</Button>{" "}
1524
</Group>

collection/app/(app)/UserSearch.tsx

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,31 @@
33
import { UserInfo } from "@/components/UserInfo";
44
import { BuyerItemsTable } from "@/components/tables/BuyerItemsTable";
55
import { OrderResponse } from "@/lib/crud/purchase";
6+
import { AcademicYear } from "@docsoc/eactivities";
67
import { Group, TextInput, Button, Alert, Container, Center, Stack } from "@mantine/core";
8+
import { useForm } from "@mantine/form";
79
import { useRouter, useSearchParams } from "next/navigation";
810
import React, { useCallback, useEffect, useMemo, useState, useTransition } from "react";
911
import { FaSearch, FaTimesCircle } from "react-icons/fa";
1012

1113
import { PageActions } from "./PageActions";
1214

13-
export const UserSearch = () => {
15+
interface UserSearchProps {
16+
currentAcademicYear: AcademicYear;
17+
validAcaemicYears: string[];
18+
}
19+
20+
export const UserSearch: React.FC<UserSearchProps> = ({
21+
currentAcademicYear,
22+
validAcaemicYears,
23+
}) => {
1424
const [error, setError] = useState<string | null>(null);
1525
const [purchases, setPurchases] = useState<OrderResponse[]>([]);
1626
const [isPending, startTransition] = useTransition();
1727

18-
// Need to allow clearing on route change to home
19-
const [shortcodeFormState, setSetshortcodeFormState] = useState("");
20-
2128
const searchParams = useSearchParams();
2229
const router = useRouter();
23-
const shortcode = useMemo(() => searchParams.get("shortcode") || "", [searchParams]);
30+
const shortcodeURLParam = useMemo(() => searchParams.get("shortcode") || "", [searchParams]);
2431

2532
const fetchPurchases = useCallback(async (shortcode: string) => {
2633
const res = await fetch(`/api/purchases/${shortcode}`, {
@@ -40,42 +47,45 @@ export const UserSearch = () => {
4047
}
4148
}, []);
4249

50+
// Form
51+
// 2 fields: shortcode & academic year purcahses.
52+
// Default value of shortcode is from the URL if possible
53+
const form = useForm({
54+
initialValues: { shortcode: shortcodeURLParam, academicYears: [currentAcademicYear] },
55+
56+
validate: {
57+
shortcode: (value) => {
58+
if (typeof value !== "string") return "Please enter a shortcode";
59+
return;
60+
},
61+
},
62+
});
4363
useEffect(() => {
44-
if (shortcode) {
45-
setSetshortcodeFormState(shortcode);
46-
fetchPurchases(shortcode);
64+
if (shortcodeURLParam) {
65+
fetchPurchases(shortcodeURLParam);
4766
} else {
4867
setPurchases([]);
49-
setSetshortcodeFormState("");
68+
form.setFieldValue("shortcode", "");
5069
}
51-
}, [shortcode, fetchPurchases]);
70+
// can't add form as would cause infinite loop
71+
// eslint-disable-next-line react-hooks/exhaustive-deps
72+
}, [shortcodeURLParam, fetchPurchases]);
5273

5374
const submitAction = useCallback(
54-
async (formState: FormData) => {
55-
setError(null);
56-
const shortcode = formState.get("shortcode")?.toString().trim();
75+
({ shortcode }: { shortcode: string }) => {
76+
startTransition(() => {
77+
setError(null);
5778

58-
if (typeof shortcode !== "string") {
59-
setError("Please enter a shortcode");
60-
return;
61-
}
62-
63-
router.push("/?shortcode=" + shortcode);
79+
router.push("/?shortcode=" + shortcode);
80+
});
6481
},
6582
[router],
6683
);
6784

68-
const submitActionWithTransition = useCallback(
69-
(formState: FormData) => {
70-
startTransition(async () => await submitAction(formState));
71-
},
72-
[submitAction],
73-
);
74-
7585
return (
7686
<Container w="70%">
7787
<Stack gap="lg">
78-
<PageActions />
88+
<PageActions academicYears={validAcaemicYears} formHook={form} />
7989
<Center>
8090
<Stack w="90%" justify="centre" align="centre">
8191
<form
@@ -85,7 +95,8 @@ export const UserSearch = () => {
8595
justifyContent: "center",
8696
alignItems: "center",
8797
}}
88-
action={submitActionWithTransition}
98+
onSubmit={form.onSubmit(submitAction)}
99+
action={() => {}}
89100
>
90101
<Group w="100%">
91102
<TextInput
@@ -96,14 +107,19 @@ export const UserSearch = () => {
96107
required
97108
name="shortcode"
98109
id="shortcode"
99-
onChange={(e) =>
100-
setSetshortcodeFormState(e.currentTarget.value)
101-
}
102-
value={shortcodeFormState}
110+
{...form.getInputProps("shortcode")}
103111
/>
104112
<Button loading={isPending} type="submit">
105113
Submit
106114
</Button>
115+
<Button
116+
color="pink"
117+
onClick={() => {
118+
router.push("/");
119+
}}
120+
>
121+
Clear
122+
</Button>
107123
</Group>
108124
</form>
109125
{error && (
@@ -114,14 +130,14 @@ export const UserSearch = () => {
114130
</Stack>
115131
</Center>
116132
<Center>
117-
<UserInfo shortcode={shortcode} />
133+
<UserInfo shortcode={shortcodeURLParam} />
118134
</Center>
119135

120136
{!error ? (
121137
<BuyerItemsTable
122-
shortcode={shortcode}
138+
shortcode={shortcodeURLParam}
123139
purchases={purchases}
124-
refresh={() => fetchPurchases(shortcode)}
140+
refresh={() => fetchPurchases(shortcodeURLParam)}
125141
/>
126142
) : (
127143
<></>

collection/app/(app)/page.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
import { auth } from "@/auth";
2+
import { getAcademicYear } from "@/lib/config";
23
import { Stack, Title } from "@mantine/core";
34
import { redirect } from "next/navigation";
45
import React from "react";
56

67
import { UserSearch } from "./UserSearch";
8+
import { getAcademicYearsInDB } from "@/lib/crud/academic-year";
79

810
export default async function Index() {
911
const session = await auth();
1012

1113
if (!session) redirect("/auth/login");
1214

15+
// Get academic years
16+
const currentAcademicYear = await getAcademicYear();
17+
const academicYears = await getAcademicYearsInDB();
18+
1319
return (
1420
<Stack gap="lg">
1521
<Title order={1}>DoCSoc Collection System</Title>
16-
<UserSearch />
22+
<UserSearch
23+
currentAcademicYear={currentAcademicYear}
24+
validAcaemicYears={academicYears}
25+
/>
1726
</Stack>
1827
);
1928
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use server";
2+
3+
import prisma from "../db";
4+
5+
export async function getAcademicYearsInDB() {
6+
return (await prisma.academicYear.findMany()).map((year) => year.year);
7+
}

0 commit comments

Comments
 (0)