Skip to content

Commit 90cd27d

Browse files
emily-yarvisEmily Yarvis
authored andcommitted
Merge branch 'develop' into fix/event-styles-folder
2 parents 7fb8ed7 + 8a572ca commit 90cd27d

File tree

19 files changed

+731
-356
lines changed

19 files changed

+731
-356
lines changed

src/app/admin-account/page.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"use client";
2+
3+
import { useClerk } from "@clerk/nextjs";
4+
import { useRouter } from "next/navigation";
5+
import NavBar from "@/components/Navbar";
6+
7+
// admin only account
8+
9+
export default function AccountPage() {
10+
const { signOut } = useClerk();
11+
const router = useRouter();
12+
13+
async function handleLogout() {
14+
await signOut();
15+
router.push("/login");
16+
}
17+
18+
return (
19+
<div>
20+
<NavBar mode="Admin" />
21+
<main className="p-8 font-lora">
22+
<h1 className="text-4xl font-bold">My Account</h1>
23+
<div className="mt-8">
24+
<button
25+
type="button"
26+
onClick={handleLogout}
27+
className="rounded-full border border-[#d9a7a7] bg-[#f8d7d7] px-6 py-2 font-semibold text-black transition hover:brightness-95 active:brightness-90"
28+
>
29+
Log Out
30+
</button>
31+
</div>
32+
</main>
33+
</div>
34+
);
35+
}

src/app/admin-calendar/page.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Button } from "@mui/material";
88
import EventCard from "../../components/EventCard";
99
import "@/app/globals.css";
1010
import Navbar from "../../components/Navbar";
11+
import { CALENDAR_CARD_EVENTS, MOCK_EVENTS } from "@/data/events";
1112

1213
export default function CalendarPage() {
1314
const calendarRef = useRef<FullCalendar>(null);
@@ -50,18 +51,28 @@ export default function CalendarPage() {
5051
}
5152
};
5253

53-
const events = [];
54-
for (let i = 0; i < 10; i++) {
55-
events[i] = { eventTitle: "Gardening", date: new Date() };
56-
}
54+
const events = CALENDAR_CARD_EVENTS;
55+
const calendarEvents = MOCK_EVENTS.map((event) => ({
56+
id: event.id,
57+
title: event.title,
58+
date: event.date,
59+
}));
5760
return (
5861
<div>
5962
<Navbar mode={"Admin"} />
6063
<div className="p-8 font-lora">
6164
<div className="text-4xl font-bold">Upcoming Events</div>
6265
<div className="flex justify-start flex-nowrap overflow-x-scroll">
63-
{events.map((event, idx) => {
64-
return <EventCard key={idx} eventTitle={event.eventTitle} date={event.date} />;
66+
{events.map((event) => {
67+
return (
68+
<EventCard
69+
key={event.id}
70+
eventId={event.id}
71+
eventTitle={event.eventTitle}
72+
date={event.date}
73+
detailsRouteBase="/admin-events"
74+
/>
75+
);
6576
})}
6677
</div>
6778
<div className="flex justify-between items-center mb-6">
@@ -97,6 +108,7 @@ export default function CalendarPage() {
97108
<FullCalendar
98109
ref={calendarRef}
99110
plugins={[dayGridPlugin, interactionPlugin]}
111+
events={calendarEvents}
100112
initialView="dayGridMonth"
101113
headerToolbar={false}
102114
height="auto"

src/app/admin-event-details/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ export default function AdminEventDetailsPage() {
1919
<section className={styles.grid}>
2020
{/* Left card: Event Details */}
2121
<div className={styles.eventCard}>
22-
<EventDetailsCard />
22+
<EventDetailsCard isEditable={true} />
2323
</div>
2424
{/* Right card: Volunteers */}
2525
<div className={styles.card}>
26-
<VolunteerList />
26+
<VolunteerList canViewVolunteers={true} />
2727
</div>
2828
</section>
2929
</main>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"use client";
2+
3+
import { useParams, useRouter } from "next/navigation";
4+
import NavBar from "@/components/Navbar";
5+
import EventDetails from "@/components/EventDetails";
6+
import VolunteerList from "@/components/VolunteerList";
7+
import styles from "@/styles/events.module.css";
8+
import { MOCK_EVENTS } from "@/data/events";
9+
10+
function toDateTime(date: Date, time: string) {
11+
const match = time.trim().match(/^(\d{1,2}):(\d{2})\s*(am|pm)$/i);
12+
if (!match) return new Date(date);
13+
14+
let hours = Number(match[1]);
15+
const minutes = Number(match[2]);
16+
const meridiem = match[3].toLowerCase();
17+
18+
if (meridiem === "pm" && hours !== 12) hours += 12;
19+
if (meridiem === "am" && hours === 12) hours = 0;
20+
21+
const value = new Date(date);
22+
value.setHours(hours, minutes, 0, 0);
23+
return value;
24+
}
25+
26+
export default function AdminEventPage() {
27+
const isAdminView = true;
28+
const router = useRouter();
29+
const params = useParams();
30+
const eventId = params?.eventId as string;
31+
const event = MOCK_EVENTS.find((item) => item.id === eventId);
32+
const eventDetailsData = event
33+
? {
34+
name: event.title,
35+
description: `Admin view for ${event.school}. Manage details, attendance, and follow-ups.`,
36+
location: event.school,
37+
startDateTime: toDateTime(event.date, event.startTime),
38+
endDateTime: toDateTime(event.date, event.endTime),
39+
imageUrl: event.imageUrl,
40+
}
41+
: undefined;
42+
43+
return (
44+
<div className={styles.page}>
45+
<NavBar mode="Admin" />
46+
<main className={styles.main}>
47+
{isAdminView && (
48+
<div className="mb-3 flex justify-end">
49+
<button
50+
type="button"
51+
className={styles.iconBtn}
52+
onClick={() => router.push(`/admin-events/${eventId}/qr`)}
53+
aria-label="Go to QR code"
54+
title="Go to QR code"
55+
>
56+
<span className="grid h-[18px] w-[18px] grid-cols-2 gap-[3px]" aria-hidden="true">
57+
<span className="rounded-[2px] bg-[#111827]" />
58+
<span className="rounded-[2px] bg-[#111827]" />
59+
<span className="rounded-[2px] bg-[#111827]" />
60+
<span className="rounded-[2px] bg-[#111827]" />
61+
</span>
62+
</button>
63+
</div>
64+
)}
65+
66+
<h1 className={styles.pageTitle}>{event?.title ?? "Event Not Found"}</h1>
67+
68+
<section className={styles.grid}>
69+
<div className={styles.eventCard}>
70+
<EventDetails eventData={eventDetailsData} isEditable={isAdminView} />
71+
</div>
72+
<div className={styles.card}>
73+
<VolunteerList canViewVolunteers={isAdminView} />
74+
</div>
75+
</section>
76+
</main>
77+
</div>
78+
);
79+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
"use client";
2+
3+
import React, { useEffect, useMemo, useState } from "react";
4+
import { useParams, useRouter } from "next/navigation";
5+
import QRCode from "qrcode";
6+
import { MOCK_EVENTS } from "@/data/events";
7+
8+
function formatDate(date: Date) {
9+
return `${date.getMonth() + 1}/${date.getDate()}/${String(date.getFullYear()).slice(-2)}`;
10+
}
11+
12+
export default function AdminEventQrPage() {
13+
const router = useRouter();
14+
const params = useParams();
15+
const eventId = params?.eventId as string;
16+
const event = MOCK_EVENTS.find((item) => item.id === eventId);
17+
18+
const [qrDataUrl, setQrDataUrl] = useState("");
19+
20+
const checkinUrl = useMemo(() => {
21+
if (typeof window === "undefined") return "";
22+
return `${window.location.origin}/checkin/${eventId}`;
23+
}, [eventId]);
24+
25+
useEffect(() => {
26+
if (!checkinUrl) return;
27+
28+
(async () => {
29+
const dataUrl = await QRCode.toDataURL(checkinUrl, {
30+
width: 280,
31+
margin: 1,
32+
errorCorrectionLevel: "M",
33+
});
34+
setQrDataUrl(dataUrl);
35+
})();
36+
}, [checkinUrl]);
37+
38+
return (
39+
<div style={styles.shell}>
40+
<div style={styles.topRow}>
41+
<div>
42+
<h1 style={styles.title}>{event?.title ?? "Garden Workday"}</h1>
43+
<div style={styles.meta}>{event?.school ?? "Unknown Location"}</div>
44+
<div style={styles.meta}>{event ? formatDate(event.date) : ""}</div>
45+
</div>
46+
47+
<button onClick={() => router.push(`/admin-events/${eventId}`)} style={styles.backBtn}>
48+
← Back to Event
49+
</button>
50+
</div>
51+
52+
<div style={styles.center}>
53+
{qrDataUrl && <img src={qrDataUrl} alt="Event QR" style={styles.qr} />}
54+
<div style={styles.caption}>Please scan to log attendance!</div>
55+
</div>
56+
57+
<button onClick={() => window.print()} style={styles.printBtn} aria-label="Print" title="Print">
58+
🖨
59+
</button>
60+
</div>
61+
);
62+
}
63+
64+
const styles: Record<string, React.CSSProperties> = {
65+
shell: {
66+
minHeight: "100vh",
67+
background: "white",
68+
padding: "24px 26px",
69+
position: "relative",
70+
fontFamily: "Lora, Georgia, serif",
71+
},
72+
73+
topRow: {
74+
display: "flex",
75+
justifyContent: "space-between",
76+
alignItems: "flex-start",
77+
},
78+
79+
title: {
80+
fontSize: 44,
81+
margin: 0,
82+
lineHeight: 1.05,
83+
fontWeight: 700,
84+
},
85+
86+
meta: {
87+
marginTop: 6,
88+
fontSize: 16,
89+
},
90+
91+
backBtn: {
92+
border: "1px solid #568264",
93+
background: "transparent",
94+
color: "#568264",
95+
borderRadius: 6,
96+
padding: "6px 12px",
97+
fontWeight: 700,
98+
cursor: "pointer",
99+
height: 34,
100+
marginTop: 6,
101+
whiteSpace: "nowrap",
102+
},
103+
104+
center: {
105+
display: "grid",
106+
placeItems: "center",
107+
marginTop: 70,
108+
},
109+
110+
qr: {
111+
width: 280,
112+
height: 280,
113+
imageRendering: "pixelated",
114+
},
115+
116+
caption: {
117+
marginTop: 12,
118+
fontWeight: 700,
119+
fontSize: 16,
120+
},
121+
122+
printBtn: {
123+
position: "absolute",
124+
right: 26,
125+
bottom: 26,
126+
border: "none",
127+
background: "transparent",
128+
cursor: "pointer",
129+
fontSize: 52,
130+
lineHeight: 1,
131+
},
132+
};

src/app/calendar/page.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Button } from "@mui/material";
88
import EventCard from "../../components/EventCard";
99
import "@/app/globals.css";
1010
import Navbar from "../../components/Navbar";
11+
import { CALENDAR_CARD_EVENTS, MOCK_EVENTS } from "@/data/events";
1112

1213
export default function CalendarPage() {
1314
const calendarRef = useRef<FullCalendar>(null);
@@ -50,18 +51,20 @@ export default function CalendarPage() {
5051
}
5152
};
5253

53-
const events = [];
54-
for (let i = 0; i < 10; i++) {
55-
events[i] = { eventTitle: "Gardening", date: new Date() };
56-
}
54+
const events = CALENDAR_CARD_EVENTS;
55+
const calendarEvents = MOCK_EVENTS.map((event) => ({
56+
id: event.id,
57+
title: event.title,
58+
date: event.date,
59+
}));
5760
return (
5861
<div>
5962
<Navbar mode={"VolunteerLoggedIn"} />
6063
<div className="p-8 font-lora">
6164
<div className="text-4xl font-bold">Upcoming Events</div>
6265
<div className="flex justify-start flex-nowrap overflow-x-scroll">
63-
{events.map((event, idx) => {
64-
return <EventCard key={idx} eventTitle={event.eventTitle} date={event.date} />;
66+
{events.map((event) => {
67+
return <EventCard key={event.id} eventId={event.id} eventTitle={event.eventTitle} date={event.date} />;
6568
})}
6669
</div>
6770
<div className="flex justify-between items-center mb-6">
@@ -97,6 +100,7 @@ export default function CalendarPage() {
97100
<FullCalendar
98101
ref={calendarRef}
99102
plugins={[dayGridPlugin, interactionPlugin]}
103+
events={calendarEvents}
100104
initialView="dayGridMonth"
101105
headerToolbar={false}
102106
height="auto"

0 commit comments

Comments
 (0)