Skip to content

Commit 4860385

Browse files
ui: Add persistent search parameters for date on events page
Also a few miscellaneous changes: - Disables auto-focus for a few dialogs - Slightly improves mobile navigation bar - Fixes font weight on `LocalLoginButton` - Use actual `Select` component for `IgModal`
1 parent 1b98034 commit 4860385

11 files changed

Lines changed: 125 additions & 42 deletions

File tree

components/DailyViewModal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ export default function DailyViewModal({
3030
}: DailyViewModalProps) {
3131
return (
3232
<Dialog open={isOpen} onOpenChange={setIsOpen}>
33-
<DialogContent aria-describedby={undefined}>
33+
<DialogContent
34+
aria-describedby={undefined}
35+
onOpenAutoFocus={(e) => e.preventDefault()}
36+
>
3437
<DialogHeader>
3538
<DialogTitle>
3639
{format(date, 'd MMMM').toUpperCase()} EVENTS

components/EventModal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ export default function EventModal({
9090
if (!open) handleClose();
9191
}}
9292
>
93-
<DialogContent aria-describedby={undefined}>
93+
<DialogContent
94+
aria-describedby={undefined}
95+
onOpenAutoFocus={(e) => e.preventDefault()}
96+
>
9497
<form
9598
onSubmit={form.handleSubmit(handleSubmitEvent)}
9699
className='flex flex-col gap-3 sm:max-w-md'

components/Header.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ import { useAuth } from '@/lib/hooks/useAuth';
3434
import CustomDropdown from './custom-dropdown';
3535

3636
export default function Header() {
37-
const [mobileSubmenuOpen, setMobileSubmenuOpen] = useState(false);
38-
const [adminSubmenuOpen, setAdminSubmenuOpen] = useState(false);
37+
const [mobileSubmenuOpen, setMobileSubmenuOpen] = useState(true);
38+
const [adminSubmenuOpen, setAdminSubmenuOpen] = useState(true);
3939
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
4040

4141
const isAuthenticated = useAuth();
@@ -89,7 +89,7 @@ export default function Header() {
8989
<NavigationMenuItem className='w-full'>
9090
{/* Student Life */}
9191
<button
92-
className='flex w-full items-center justify-between'
92+
className='mb-1 flex w-full items-center justify-between'
9393
onClick={() => setMobileSubmenuOpen((open) => !open)}
9494
>
9595
<span>STUDENT LIFE</span>
@@ -153,7 +153,7 @@ export default function Header() {
153153
<NavigationMenuItem className='w-full'>
154154
{/* Student Life */}
155155
<button
156-
className='flex w-full items-center justify-between'
156+
className='mb-1 flex w-full items-center justify-between'
157157
onClick={() => setAdminSubmenuOpen((open) => !open)}
158158
>
159159
<span>ADMIN</span>

components/admin/OrganisationModal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ export default function OrganisationModal({
9191
</Button>
9292
)}
9393
</DialogTrigger>
94-
<DialogContent aria-describedby={undefined}>
94+
<DialogContent
95+
aria-describedby={undefined}
96+
onOpenAutoFocus={(e) => e.preventDefault()}
97+
>
9598
<form
9699
onSubmit={form.handleSubmit(handleSubmitOrganisation)}
97100
className='flex flex-col gap-3 sm:max-w-md'

components/auth/LocalLoginButton.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import './telegram-login-button.css';
22

3+
import { Roboto } from 'next/font/google';
4+
5+
export const roboto = Roboto({
6+
subsets: ['latin'],
7+
weight: '400',
8+
});
9+
310
export default function LocalLoginButton() {
411
return (
5-
<body className={`widget_frame_base tgme_widget body_widget_login`}>
12+
<div
13+
className={`${roboto.className} widget_frame_base tgme_widget body_widget_login`}
14+
>
615
<div className='tgme_widget_login large'>
716
<button
817
className='btn tgme_widget_login_button'
@@ -17,6 +26,6 @@ export default function LocalLoginButton() {
1726
onClick={() => window.location.replace('/api/auth/callback')}
1827
/>
1928
</div>
20-
</body>
29+
</div>
2130
);
2231
}

components/booking/BookingModal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ export default function BookingModal({
9898
if (!open) handleClose();
9999
}}
100100
>
101-
<DialogContent aria-describedby={undefined}>
101+
<DialogContent
102+
aria-describedby={undefined}
103+
onOpenAutoFocus={(e) => e.preventDefault()}
104+
>
102105
<form
103106
onSubmit={form.handleSubmit(handleSubmitBooking)}
104107
className='flex flex-col gap-3 sm:max-w-md'

components/booking/Bookings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ export default function Bookings({
231231
form.setValue('startTime', start);
232232
form.setValue('endTime', end);
233233
},
234-
[],
234+
[form, venues],
235235
);
236236

237237
return (

components/event/CalendarGrid.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { isSameMonth, isSameWeek } from 'date-fns';
2-
import type { Dispatch, SetStateAction } from 'react';
2+
import type { Dispatch } from 'react';
33

44
interface CalendarGridProps {
55
currentDate: Date;
6-
setCurrentDate: Dispatch<SetStateAction<Date>>;
6+
setCurrentDate: Dispatch<Date>;
77
viewMode: 'MONTH' | 'WEEK';
88
calendarDays: Date[];
99
}

components/event/Events.tsx

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import {
1616
subWeeks,
1717
} from 'date-fns';
1818
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react';
19-
import { useActionState, useState } from 'react';
19+
import { useSearchParams } from 'next/navigation';
20+
import { useActionState, useMemo, useState } from 'react';
2021
import { useForm } from 'react-hook-form';
2122
import { toast } from 'sonner';
2223
import { z } from 'zod/v4';
@@ -54,6 +55,7 @@ const today = getNext30Minutes();
5455

5556
export default function Events({ events, userOrgs }: EventsProps) {
5657
const isAuthenticated = useAuth();
58+
const searchParams = useSearchParams();
5759

5860
const [createEventDate, setCreateEventDate] = useState<Date | null>(null);
5961
const [selectedEvent, setSelectedEvent] = useState<EventView | null>(null);
@@ -62,7 +64,21 @@ export default function Events({ events, userOrgs }: EventsProps) {
6264

6365
const [viewMode, setViewMode] = useState<'MONTH' | 'WEEK'>('MONTH');
6466

65-
const [currentDate, setCurrentDate] = useState<Date>(new Date());
67+
const currentDate = useMemo(() => {
68+
let date = new Date();
69+
try {
70+
let searchParamsDate: number | undefined = undefined;
71+
const searchParamsMonth = searchParams.get('month');
72+
if (searchParamsMonth) searchParamsDate = Date.parse(searchParamsMonth);
73+
74+
const searchParamsWeek = searchParams.get('date');
75+
if (searchParamsWeek) searchParamsDate = Date.parse(searchParamsWeek);
76+
77+
if (searchParamsDate !== undefined && !isNaN(searchParamsDate))
78+
date = new Date(searchParamsDate);
79+
} catch {}
80+
return date;
81+
}, [searchParams]);
6682

6783
const [isDailyViewOpen, setIsDailyViewOpen] = useState(false);
6884

@@ -198,15 +214,41 @@ export default function Events({ events, userOrgs }: EventsProps) {
198214
form.setValue('endTime', addHours(date, 2));
199215
};
200216

201-
const handlePrevious = () =>
202-
setCurrentDate((prev) =>
203-
viewMode === 'MONTH' ? subMonths(prev, 1) : subWeeks(prev, 1),
217+
const handlePrevious = () => {
218+
const newDate =
219+
viewMode === 'MONTH'
220+
? subMonths(currentDate, 1)
221+
: subWeeks(currentDate, 1);
222+
const params = new URLSearchParams(
223+
viewMode === 'MONTH'
224+
? {
225+
[viewMode.toLowerCase()]: newDate.toLocaleString('en-sg', {
226+
month: 'long',
227+
year: 'numeric',
228+
}),
229+
}
230+
: { date: newDate.toDateString() },
204231
);
232+
window.history.pushState(null, '', `?${params.toString()}`);
233+
};
205234

206-
const handleNext = () =>
207-
setCurrentDate((prev) =>
208-
viewMode === 'MONTH' ? addMonths(prev, 1) : addWeeks(prev, 1),
235+
const handleNext = () => {
236+
const newDate =
237+
viewMode === 'MONTH'
238+
? addMonths(currentDate, 1)
239+
: addWeeks(currentDate, 1);
240+
const params = new URLSearchParams(
241+
viewMode === 'MONTH'
242+
? {
243+
[viewMode.toLowerCase()]: newDate.toLocaleString('en-sg', {
244+
month: 'long',
245+
year: 'numeric',
246+
}),
247+
}
248+
: { date: newDate.toDateString() },
209249
);
250+
window.history.pushState(null, '', `?${params.toString()}`);
251+
};
210252

211253
const calendarStart = startOfWeek(startOfMonth(currentDate), {
212254
weekStartsOn: 1,
@@ -321,7 +363,19 @@ export default function Events({ events, userOrgs }: EventsProps) {
321363
<div className='relative grid grid-cols-7 gap-1'>
322364
<CalendarGrid
323365
currentDate={currentDate}
324-
setCurrentDate={setCurrentDate}
366+
// TODO: Update this
367+
setCurrentDate={(newDate) => {
368+
if (viewMode === 'WEEK') {
369+
const params = new URLSearchParams({
370+
date: newDate.toDateString(),
371+
});
372+
window.history.pushState(
373+
null,
374+
'',
375+
`?${params.toString()}`,
376+
);
377+
}
378+
}}
325379
viewMode={viewMode}
326380
// TODO: This is problematic
327381
calendarDays={calendarDays}
@@ -382,15 +436,15 @@ export default function Events({ events, userOrgs }: EventsProps) {
382436
<Button
383437
variant='ghost'
384438
size='icon'
385-
className='h-[38px] w-[38px] rounded-full bg-[#FF7D4E] text-white hover:bg-[#FF7D4E]/90'
439+
className='h-9.5 w-9.5 rounded-full bg-[#FF7D4E] text-white hover:bg-[#FF7D4E]/90'
386440
onClick={handlePrevious}
387441
>
388442
<ChevronLeftIcon className='h-4 w-4' />
389443
</Button>
390444
<Button
391445
variant='ghost'
392446
size='icon'
393-
className='h-[38px] w-[38px] rounded-full bg-[#FF7D4E] text-white hover:bg-[#FF7D4E]/90'
447+
className='h-9.5 w-9.5 rounded-full bg-[#FF7D4E] text-white hover:bg-[#FF7D4E]/90'
394448
onClick={handleNext}
395449
>
396450
<ChevronRightIcon className='h-4 w-4' />

components/student-group/IgModal.tsx

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useEffect, useMemo, useState } from 'react';
3+
import { useMemo, useState } from 'react';
44

55
import CopyButton from '@/components/CopyButton';
66
import {
@@ -10,6 +10,14 @@ import {
1010
DialogTitle,
1111
DialogTrigger,
1212
} from '@/components/ui/dialog';
13+
import {
14+
Select,
15+
SelectContent,
16+
SelectGroup,
17+
SelectItem,
18+
SelectTrigger,
19+
SelectValue,
20+
} from '@/components/ui/select';
1321
import type { OrganisationView } from '@/lib/utils/server/organisation';
1422

1523
interface ShowIGModalProps {
@@ -38,7 +46,7 @@ export default function ShowIGModal({ organisation }: ShowIGModalProps) {
3846

3947
return (
4048
<Dialog>
41-
<DialogTrigger className='mt-auto mr-auto rounded-md border-[#0C2C47] bg-[#0C2C47] px-6 py-1 text-xs text-white hover:bg-[#0C2C47]/90'>
49+
<DialogTrigger className='mr-auto rounded-md border-[#0C2C47] bg-[#0C2C47] px-6 py-1 text-xs text-white hover:bg-[#0C2C47]/90'>
4250
SHOW MORE
4351
</DialogTrigger>
4452
<DialogContent aria-describedby={undefined}>
@@ -61,7 +69,7 @@ export default function ShowIGModal({ organisation }: ShowIGModalProps) {
6169
<label className='mb-2 block text-sm font-medium text-gray-700'>
6270
Description
6371
</label>
64-
<div className='min-h-[80px] rounded-md border bg-gray-50 p-3'>
72+
<div className='min-h-20 rounded-md border bg-gray-50 p-3'>
6573
<p className='text-sm leading-relaxed text-gray-900'>
6674
{organisation.description || 'No description available.'}
6775
</p>
@@ -76,20 +84,20 @@ export default function ShowIGModal({ organisation }: ShowIGModalProps) {
7684
IG Head
7785
</label>
7886
{igHeads.length > 1 ? (
79-
<select
80-
value={selectedIGHead}
81-
onChange={(e) => setSelectedIGHead(e.target.value)}
82-
className='w-full rounded-md border border-gray-300 bg-white p-3 text-sm text-gray-900 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-hidden'
83-
>
84-
<option value=''>Select an IG Head</option>
85-
{igHeads.map((head) => (
86-
<option key={head.name} value={head.name}>
87-
{head.name}
88-
</option>
89-
))}
90-
</select>
87+
<Select value={selectedIGHead} onValueChange={setSelectedIGHead}>
88+
<SelectTrigger className='rounded-md border border-gray-300 bg-white p-3 text-sm text-gray-900 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-hidden'>
89+
<SelectValue placeholder='Select an IG Head' />
90+
</SelectTrigger>
91+
<SelectContent>
92+
{igHeads.map((head) => (
93+
<SelectItem key={head.name} value={head.name}>
94+
{head.name}
95+
</SelectItem>
96+
))}
97+
</SelectContent>
98+
</Select>
9199
) : (
92-
<div className='rounded-md border bg-gray-50 p-3'>
100+
<div className='rounded-md border bg-gray-50 p-3 py-2'>
93101
<p className='text-sm text-gray-900'>
94102
{igHeads.length > 0 ? igHeads[0].name : 'No IG Head assigned'}
95103
</p>
@@ -104,7 +112,7 @@ export default function ShowIGModal({ organisation }: ShowIGModalProps) {
104112
IG Head&apos;s Telegram
105113
</label>
106114
<div className='flex items-center gap-2'>
107-
<div className='flex-1 rounded-md border bg-gray-50 p-3'>
115+
<div className='flex-1 rounded-md border bg-gray-50 p-3 py-2'>
108116
<p className='text-sm text-gray-900'>
109117
{getSelectedIGHeadTelegram()}
110118
</p>

0 commit comments

Comments
 (0)