Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Organizer Refactor - New useSWR hooks and abstraction #360

Merged
merged 30 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ced3b05
Comment to start PR
jacoblurie29 Sep 7, 2023
1660f63
Merge branch 'main' into organizer-refactor
jacoblurie29 Sep 7, 2023
b9d3cce
Generic customSWR and ScheduleTab abstracted
jacoblurie29 Sep 8, 2023
c85c673
Updated SWR package to v2.2.2
jacoblurie29 Sep 8, 2023
3a9f281
Refactor sample to potential and simplify UI logic
jacoblurie29 Sep 8, 2023
7c721f1
First run finished for refactor of ScheduleTab
jacoblurie29 Sep 8, 2023
14849be
Merge branch 'main' into organizer-refactor
jacoblurie29 Sep 8, 2023
ec9ae42
Update next-auth package to reflext SWR changes
jacoblurie29 Sep 8, 2023
818c9a9
Revert SWR package to previous version, next-auth package requires up…
jacoblurie29 Sep 8, 2023
9a07f67
Reverted important changes for reversion of swr package
jacoblurie29 Sep 8, 2023
959cb60
Reverted version of next-auth
jacoblurie29 Sep 8, 2023
fb9c343
One more minor change
jacoblurie29 Sep 8, 2023
9695ad3
Another small change
jacoblurie29 Sep 8, 2023
307ef4c
Added some ignores to test a theory about vercel breaking
jacoblurie29 Sep 8, 2023
e38951a
Remove ignores
jacoblurie29 Sep 8, 2023
bb70048
Fixed the issue
jacoblurie29 Sep 8, 2023
f27d6cb
Refactor and abstract JudgingTab
jacoblurie29 Sep 9, 2023
6ab88af
Refactor Manage Users tab
lisaliuu Sep 11, 2023
a18c21e
Run Prettier
lisaliuu Sep 11, 2023
d4d76a9
Refactor Pre-Add Users tab
lisaliuu Sep 11, 2023
7e67608
Merging unpulled changes
lisaliuu Sep 11, 2023
c52a704
Run Prettier
lisaliuu Sep 11, 2023
94b50c4
Refactor applicants tab and restructure folders
jacoblurie29 Sep 11, 2023
61eaed6
Refactor events tab SWR
jacoblurie29 Sep 11, 2023
28f043d
Merge main into organizer-refactor branch
jacoblurie29 Sep 12, 2023
a32639a
== to ===
jacoblurie29 Sep 12, 2023
70b3d2c
Move components into correct component folders
jacoblurie29 Sep 12, 2023
d93c940
More folder changes
jacoblurie29 Sep 12, 2023
0fe7dbc
Clean up if statements
lisaliuu Sep 12, 2023
a339fe1
Switch error and null data check
jacoblurie29 Sep 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/LeaveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button, Popconfirm } from 'antd';
import { useSWRConfig } from 'swr';
import { ScopedMutator } from 'swr/dist/types';

export default function LeaveButton({ onLeave }: { onLeave: (mutate: ScopedMutator<any>) => Promise<void> }) {
export default function LeaveButton({ onLeave }: { onLeave: (mutate: ScopedMutator) => Promise<void> }) {
const { mutate } = useSWRConfig();
return (
<Popconfirm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Table, Tag, Button, Checkbox, Modal, Input, Popover, Space } from 'antd';
import type { InputRef } from 'antd';
import React, { useState, useRef, useEffect } from 'react';
import { ApplicationStatus, UserData } from '../types/database';
import { ApplicationStatus, UserData } from '../../../types/database';
import {
CheckCircleOutlined,
EyeOutlined,
Expand All @@ -12,13 +12,10 @@ import {
import { DateTime } from 'luxon';
import type { ColumnsType, FilterValue, FilterConfirmProps } from 'antd/es/table/interface';
import { useSWRConfig } from 'swr';
import { ScopedMutator } from 'swr/dist/types';
import Highlighter from 'react-highlight-words';
import { handleSubmitFailure, handleSubmitSuccess } from '../lib/helpers';

export interface ApplicantsDisplayProps {
hackers: UserData[];
}
import { handleSubmitFailure, handleSubmitSuccess } from '../../../lib/helpers';
import { ScopedMutator } from 'swr/dist/types';
import { RequestType, useCustomSWR } from '../../../utils/request-utils';

const APPLICATION_STATUSES = [
'Created',
Expand Down Expand Up @@ -74,7 +71,7 @@ const APPLICATION_KEY_MAP = {
mlhComms: 'MLH Communications',
};

const acceptReject = (id: string, applicationStatus: ApplicationStatus, mutate: ScopedMutator, hackers: any) => {
const acceptReject = (id: string, applicationStatus: ApplicationStatus, mutate: ScopedMutator, hackers: UserData[]) => {
fetch('/api/accept-reject', {
method: 'POST',
headers: {
Expand All @@ -86,7 +83,7 @@ const acceptReject = (id: string, applicationStatus: ApplicationStatus, mutate:
}),
}).then(() => {
const newHackers: UserData[] = JSON.parse(JSON.stringify(hackers)); // Deep copies the object
const idx = newHackers.findIndex((x: any) => x._id === id);
const idx = newHackers.findIndex((x: UserData) => x._id.toString() === id);
newHackers[idx].applicationStatus = applicationStatus;
mutate(
'/api/users?usertype=HACKER',
Expand All @@ -98,7 +95,7 @@ const acceptReject = (id: string, applicationStatus: ApplicationStatus, mutate:
});
};

const createPopover = (record: any, mutate: ScopedMutator, hackers: any) => {
const createPopover = (record: any, mutate: ScopedMutator, hackers: UserData[]) => {
return (
<div>
<Button type="dashed" onClick={() => acceptReject(record._id, ApplicationStatus.REJECTED, mutate, hackers)}>
Expand All @@ -116,8 +113,15 @@ const createPopover = (record: any, mutate: ScopedMutator, hackers: any) => {
);
};

export default function ApplicantsDisplay(props: ApplicantsDisplayProps) {
const hackers = props.hackers.map(x => ({ ...x, key: x._id }));
export const ApplicantsTab = () => {
// Hacker data
const { data: hackersData, error: hackersError } = useCustomSWR<UserData>({
url: '/api/users?usertype=HACKER',
method: RequestType.GET,
errorMessage: 'Failed to get list of hackers.',
});

const hackers = hackersData?.map(x => ({ ...x, key: x._id })) || ([] as UserData[]);

const [isAppModalOpen, setIsAppModalOpen] = useState(false);
const [isCheckinModalOpen, setIsCheckinModalOpen] = useState(false);
Expand Down Expand Up @@ -324,7 +328,7 @@ export default function ApplicantsDisplay(props: ApplicantsDisplayProps) {
const statusName = APPLICATION_STATUSES[applicationStatus as number];
if (statusName === 'Submitted') {
return (
<Popover placement="left" content={createPopover(record, mutate, props.hackers)}>
<Popover placement="left" content={createPopover(record, mutate, hackers)}>
<Tag color={(STATUS_COLORS as any)[statusName]}>{statusName}</Tag>
</Popover>
);
Expand Down Expand Up @@ -432,4 +436,6 @@ export default function ApplicantsDisplay(props: ApplicantsDisplayProps) {
)}
</>
);
}
};

export default ApplicantsTab;
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Button, Input, InputRef, Modal, notification, Table } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { useEffect, useRef, useState } from 'react';
import { EventData } from '../types/database';
import { EventCountData, EventData } from '../../../types/database';
import { RequestType, useCustomSWR } from '../../../utils/request-utils';
import { mutate } from 'swr';

interface EventDisplay extends EventData {
setCurEvent: (open: EventDisplay) => void;
Expand Down Expand Up @@ -75,45 +77,58 @@ const columns: ColumnsType<EventDisplay> = [
},
];

export default function Events() {
const EventsTab = () => {
const [curEvent, setCurEvent] = useState<EventDisplay | null>(null);
const [events, setEvents] = useState<EventDisplay[]>([]);
const [nfcId, setNfcId] = useState<string>('');
const [loading, setLoading] = useState(false);

const input = useRef<InputRef>(null);

// get events count
const { data: eventsCountData, error: eventsCountError } = useCustomSWR<EventCountData>({
url: '/api/events-count',
method: RequestType.GET,
errorMessage: 'Failed to get count of events.',
});

// get events data
const { data: eventsData, error: eventsError } = useCustomSWR<EventData>({
url: '/api/events',
method: RequestType.GET,
errorMessage: 'Failed to get list of events.',
});

useEffect(() => {
if (eventsCountData && eventsCountData.length > 0 && eventsData && eventsData.length > 0) {
console.log(eventsCountData);
setEvents(
eventsData.map((event: EventData) => {
// TODO convert startTime and endTime here so we don't have to do it in the render function
const count = eventsCountData.find((e: any) => e._id === event._id);
return {
key: event._id,
...event,
setCurEvent,
count: count ? count.count : 0,
};
})
);
}
}, [eventsData, eventsCountData]);

useEffect(() => {
// wait for modal to open then focus input
if (curEvent) {
setTimeout(() => input.current?.focus());
}
}, [curEvent]);

const getData = () => {
// TODO: rethink this function: is it better to combine /events-count and /events api?
const result = fetch('/api/events-count')
.then(res => res.json())
.then(eventCount => {
return fetch('/api/events')
.then(res => res.json())
.then(events => {
setEvents(
events.map((event: EventData) => {
// TODO convert startTime and endTime here so we don't have to do it in the render function
const count = eventCount.find((e: any) => e._id === event._id);
return {
key: event._id,
...event,
setCurEvent,
count: count ? count.count : 0,
};
})
);
});
});

return result;
const refreshData = async () => {
setLoading(true);
mutate('/api/events-count', true)
.then(() => mutate('/api/events', true))
.finally(() => setLoading(false));
};

const handleCheckIn = async () => {
Expand All @@ -140,8 +155,7 @@ export default function Events() {
});
}
setNfcId('');
// TODO: use useSWR and mutate to update data for good practice
getData();
refreshData();
};

const handleCancel = () => {
Expand All @@ -152,13 +166,9 @@ export default function Events() {
const syncCalendar = async () => {
setLoading(true);
await fetch('/api/sync-calendar');
getData().then(() => setLoading(false));
refreshData();
};

useEffect(() => {
getData();
}, []);

return (
<>
<Button loading={loading} onClick={syncCalendar}>
Expand Down Expand Up @@ -187,4 +197,6 @@ export default function Events() {
</Modal>
</>
);
}
};

export default EventsTab;
45 changes: 45 additions & 0 deletions components/Organizer/JudgingTab/JudgingTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Skeleton } from 'antd';
import { TeamData, ScoreData, UserData } from '../../../types/database';
import { useCustomSWR, RequestType } from '../../../utils/request-utils';
import AllScores from '../../allScores';

const JudgingTab = () => {
// Teams data
const { data: teamsData, error: teamsError } = useCustomSWR<TeamData>({
url: '/api/teams',
method: RequestType.GET,
errorMessage: 'Failed to get list of teams.',
});

// Scores data
const { data: scoresData, error: scoresError } = useCustomSWR<ScoreData>({
url: '/api/scores',
method: RequestType.GET,
errorMessage: 'Failed to get list of scores.',
});

// Judge data
const { data: judgeData, error: judgeError } = useCustomSWR<UserData>({
url: '/api/users?usertype=JUDGE',
method: RequestType.GET,
errorMessage: 'Failed to get list of judges.',
});

// Combine all the loading, null, and error states
const error = teamsError || scoresError || judgeError;
const dataNull = !teamsData || !scoresData || !judgeData;

return (
<>
{dataNull ? (
<div>Loading...</div>
) : error ? (
<div>Failed to load data.</div>
) : (
<AllScores teamData={teamsData} scoreData={scoresData} userData={judgeData} />
)}
</>
);
};

export default JudgingTab;
30 changes: 30 additions & 0 deletions components/Organizer/ManageUsersTab/ManageUsersTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Empty, Skeleton } from 'antd';
import { useSWRConfig } from 'swr';
import { useCustomSWR, RequestType } from '../../../utils/request-utils';
import ManageRoleForm, { ManageFormFields } from '../../../components/manageRoleForm';
import { handleManageFormSubmit } from '../../../utils/organizer-utils';

const ManageUsersTab = () => {
const { mutate } = useSWRConfig();

// User data
const { data: userData, error } = useCustomSWR<ManageFormFields>({
url: '/api/manage-role',
method: RequestType.GET,
errorMessage: 'Failed to get list of all users.',
});

return (
<>
{!userData && <Skeleton />}
{userData && userData.length == 0 && (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={<span>No users lmao</span>} />
)}
{userData && userData.length > 0 && (
<ManageRoleForm formData={userData} onSubmit={formData => handleManageFormSubmit(formData, mutate)} />
)}
</>
);
};

export default ManageUsersTab;
32 changes: 32 additions & 0 deletions components/Organizer/PreAddUsersTab/PreAddUsersTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Empty, Skeleton } from 'antd';
import { useSWRConfig } from 'swr';
import { useCustomSWR, RequestType } from '../../../utils/request-utils';
import { handlePreAddDelete } from '../../../utils/organizer-utils';
import { PreAddData } from '../../../types/database';
import PreAddForm from '../../../components/preAddForm';
import PreAddDisplay from '../../../components/preAddDisplay';

const PreAddUsersTab = () => {
const { mutate } = useSWRConfig();

// Preadd data
const { data: preAddData, error: preAddError } = useCustomSWR<PreAddData>({
url: '/api/preadd',
method: RequestType.GET,
errorMessage: 'Failed to get list of preadded users.',
});

return (
<>
{preAddData && preAddData.length == 0 && (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={<span>No preadded users lmao</span>} />
)}
{preAddData && preAddData.length > 0 && (
<PreAddDisplay data={preAddData!} onDelete={user => handlePreAddDelete(user, mutate)} />
)}
<PreAddForm />
</>
);
};

export default PreAddUsersTab;
Loading