Skip to content

Commit 6a39cac

Browse files
authored
Merge pull request #212 from ystv/mia/committee-positions
Add committee positions
2 parents d9446e6 + 99961c1 commit 6a39cac

File tree

48 files changed

+3271
-38
lines changed

Some content is hidden

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

48 files changed

+3271
-38
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import ErrorPage from "@/components/ErrorPage";
2+
import { hasPermission } from "@/lib/auth/server";
3+
4+
export default async function AuthenticatedLayout({
5+
children,
6+
}: {
7+
children: React.ReactNode;
8+
}) {
9+
if (await hasPermission("Admin.Committee")) {
10+
return <>{children}</>;
11+
} else {
12+
return <ErrorPage code={403} message="Forbidden" />;
13+
}
14+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Button, Card, Group, Stack } from "@mantine/core";
2+
import Link from "next/link";
3+
4+
import { PageInfo } from "@/components/PageInfo";
5+
6+
export const dynamic = "force-dynamic";
7+
8+
export default function AdminPage() {
9+
return (
10+
<>
11+
<PageInfo title="Admin - Committee" />
12+
<Card>
13+
<Group>
14+
<Link href={"/admin/committee/positions"}>
15+
<Button variant="default">Positions</Button>
16+
</Link>
17+
<Link href={"/admin/committee/teams"}>
18+
<Button variant="default">Teams</Button>
19+
</Link>
20+
</Group>
21+
</Card>
22+
</>
23+
);
24+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import {
2+
ActionIcon,
3+
Card,
4+
Group,
5+
Menu,
6+
Stack,
7+
Text,
8+
Tooltip,
9+
} from "@mantine/core";
10+
import { modals } from "@mantine/modals";
11+
import { notifications } from "@mantine/notifications";
12+
import { type CommitteePosition, type Position } from "@prisma/client";
13+
import Link from "next/link";
14+
import { FaEdit, FaEye } from "react-icons/fa";
15+
import { MdDeleteForever, MdMoreHoriz } from "react-icons/md";
16+
import { type z } from "zod";
17+
18+
import { type FormResponse } from "@/components/Form";
19+
20+
import { type deleteCommitteePositionSchema } from "./schema";
21+
22+
export function CommitteePositionCard(props: {
23+
committeePosition: CommitteePosition & {
24+
_count: { committee_position_members: number };
25+
};
26+
editAction: () => void;
27+
deleteAction: (
28+
data: z.infer<typeof deleteCommitteePositionSchema>,
29+
) => Promise<FormResponse>;
30+
onDeleteSuccess: () => void;
31+
}) {
32+
return (
33+
<Card key={props.committeePosition.committee_position_id} withBorder>
34+
<Group>
35+
<Text>{props.committeePosition.name}</Text>
36+
<Stack gap={0}>
37+
<Text size="sm">{props.committeePosition.description}</Text>
38+
<Text size="xs" c={"dimmed"}>
39+
{props.committeePosition.seats} Seat
40+
{props.committeePosition.seats === 1 ? "" : "s"}
41+
</Text>
42+
<Text size="xs" c={"dimmed"}>
43+
{props.committeePosition._count.committee_position_members} Member
44+
{props.committeePosition._count.committee_position_members === 1
45+
? ""
46+
: "s"}
47+
</Text>
48+
</Stack>
49+
<Menu position="left">
50+
<Menu.Target>
51+
<ActionIcon ml={"auto"}>
52+
<MdMoreHoriz />
53+
</ActionIcon>
54+
</Menu.Target>
55+
<Menu.Dropdown miw={150} right={10} mr={10}>
56+
<Menu.Item onClick={props.editAction}>
57+
<Group>
58+
<FaEdit />
59+
Edit
60+
</Group>
61+
</Menu.Item>
62+
<Menu.Item
63+
aria-label="Delete position"
64+
color="red"
65+
onClick={() => {
66+
openDeleteModal({
67+
onCancel: () => {},
68+
onConfirm: async () => {
69+
const deletedPosition = await props.deleteAction({
70+
committee_position_id:
71+
props.committeePosition.committee_position_id,
72+
});
73+
74+
if (!deletedPosition.ok) {
75+
notifications.show({
76+
message: "Unable to delete position",
77+
color: "red",
78+
});
79+
} else {
80+
props.onDeleteSuccess();
81+
notifications.show({
82+
message: `Successfully deleted "${props.committeePosition.name}"`,
83+
color: "green",
84+
});
85+
}
86+
},
87+
positionName: props.committeePosition.name,
88+
});
89+
}}
90+
>
91+
<Group>
92+
<MdDeleteForever />
93+
Delete
94+
</Group>
95+
</Menu.Item>
96+
</Menu.Dropdown>
97+
</Menu>
98+
<Link
99+
href={`/admin/committee/positions/${props.committeePosition.committee_position_id}`}
100+
>
101+
<Tooltip label="View Committee Position">
102+
<ActionIcon variant="light">
103+
<FaEye />
104+
</ActionIcon>
105+
</Tooltip>
106+
</Link>
107+
</Group>
108+
</Card>
109+
);
110+
}
111+
112+
const openDeleteModal = (props: {
113+
onCancel: () => void;
114+
onConfirm: () => void;
115+
positionName: string;
116+
}) =>
117+
modals.openConfirmModal({
118+
title: `Delete position "${props.positionName}"`,
119+
centered: true,
120+
children: (
121+
<Text size="sm">
122+
Are you sure you want to delete the committee position &quot;
123+
{props.positionName}
124+
&quot;? This action is destructive.
125+
</Text>
126+
),
127+
labels: { confirm: "Delete committee position", cancel: "Cancel" },
128+
confirmProps: { color: "red" },
129+
onCancel: props.onCancel,
130+
onConfirm: props.onConfirm,
131+
});

0 commit comments

Comments
 (0)