Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import EventStatsPage from 'Pages/Events/EventStats';
import TicketUserList from './Pages/Ticket/TicketUserList';
import TicketDescPage from './Pages/Ticket/TicketDesc';
import EventScheduleCreate from 'Pages/Events/EventScheduleCreate';
import EventResults from 'Pages/Events/EventResults';

function App() {
return (
Expand Down Expand Up @@ -168,6 +169,16 @@ function EventsRoutes() {
</ProtectedRoute>
}
/>,
<Route
path="/events/results/:id"
element={
<ProtectedRoute
allowedRoles={[...allEventEditRoles, ...allEventViewRoles, ...specificEventViewRoles]}
>
<EventResults />
</ProtectedRoute>
}
/>,

<Route
path="/events/registrations/statistics"
Expand Down
7 changes: 7 additions & 0 deletions src/Components/Events/EventDesc/EventData/EventData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ export default function EventData({ event }: { event: IEvent }) {
<Typography>{event?.eventStatus}</Typography>
</Grid>

<Grid item xs={6}>
<Typography>Results Published</Typography>
</Grid>
<Grid item xs={6}>
<Typography>{event?.results && event.results.length > 0 ? 'YES' : 'NO'}</Typography>
</Grid>

<Grid item xs={6}>
<Typography>Number of Rounds</Typography>
</Grid>
Expand Down
4 changes: 2 additions & 2 deletions src/Components/Events/EventDesc/ToolBar/ToolBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ export default function ToolBar({ eventId }: { eventId: number }) {

<Button
variant="contained"
color="primary"
color={'primary'}
startIcon={<MilitaryTechIcon />}
className="toolbutton"
onClick={() => {
alert('Coming Soon!');
navigate(`/events/results/${eventId}`);
}}
>
Results
Expand Down
9 changes: 8 additions & 1 deletion src/Hooks/Event/create-update/eventValidation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ import { InferType, ObjectSchema, boolean, date, mixed, number, object, string }
export const eventValidationSchema: ObjectSchema<
Omit<
IEvent,
'eventStatus' | 'category' | 'eventType' | 'eventHead1' | 'eventHead2' | 'icon' | 'id'
| 'eventStatus'
| 'category'
| 'eventType'
| 'eventHead1'
| 'eventHead2'
| 'icon'
| 'id'
| 'results'
> & { icon: File | undefined }
> = object().shape({
name: string()
Expand Down
13 changes: 13 additions & 0 deletions src/Hooks/Event/eventTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,24 @@ export interface IEvent extends IEventListItem {
registrationEndDate?: Date;
button: TEventButton;
registrationLink?: string;
results: IResult[];

// rounds: [];
// registration: null;
}

export interface IResult {
id: number;
eventId: number;
excelId: number;
teamId?: number | null;
event?: IEvent;
position: number;
name: string;
teamName?: string | null;
teamMembers?: string | null;
}

export interface IEventHead {
id: number;
name: string;
Expand Down
27 changes: 27 additions & 0 deletions src/Hooks/Event/results/resultValidation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IResult } from '../eventTypes';
import { InferType, ObjectSchema, number, object, string } from 'yup';

export const resultValidationSchema: ObjectSchema<Omit<IResult, 'id' | 'eventId' | 'event'>> =
object().shape({
excelId: number()
.transform((val) => (isNaN(val) ? undefined : val))
.required('Excel ID is required'),
teamId: number()
.transform((val) => (isNaN(val) ? undefined : val))
.notRequired(),
position: number().required('Position is required').min(1, 'Position must be at least 1'),
name: string().required('Name is required'),
teamName: string().notRequired().min(2, 'Team Name must be at least 2 characters'),
teamMembers: string().notRequired().min(2, 'Team Members must be at least 2 characters'),
});

export interface IValidateResult extends InferType<typeof resultValidationSchema> {}

export const defaultResult: IValidateResult = {
excelId: 0,
teamId: 0,
position: 1,
name: '',
teamName: '',
teamMembers: '',
} as unknown as IValidateResult;
102 changes: 102 additions & 0 deletions src/Hooks/Event/results/useEventResultsCrud.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { useContext, useState } from 'react';
import { ApiContext } from 'Contexts/Api/ApiContext';
import { getErrMsg } from 'Hooks/errorParser';
import { IValidateResult, resultValidationSchema } from './resultValidation';
import { ValidationError } from 'yup';

export function useEventResultsCrud() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const { axiosEventsPrivate } = useContext(ApiContext);

async function addResult(eventId: number, result: IValidateResult) {
try {
setLoading(true);
setError('');
await resultValidationSchema.validate(result, { abortEarly: false });
const payload = {
...result,
eventId,
teamId: result.teamId ?? 0,
teamName: result.teamName ?? '',
teamMembers: result.teamMembers ?? '',
};
await axiosEventsPrivate.post('/api/Result', payload);
return true;
} catch (err: any) {
if (err instanceof ValidationError) {
setError(err.inner.map((e) => e.message).join(', '));
} else {
setError(getErrMsg(err));
}
return false;
} finally {
setLoading(false);
}
}

async function updateResult(resultId: number, eventId: number, result: IValidateResult) {
try {
setLoading(true);
setError('');
await resultValidationSchema.validate(result, { abortEarly: false });
const payload = {
...result,
id: resultId,
eventId,
teamId: result.teamId ?? 0,
teamName: result.teamName ?? '',
teamMembers: result.teamMembers ?? '',
};
await axiosEventsPrivate.put('/api/Result', payload);
return true;
} catch (err: any) {
if (err instanceof ValidationError) {
setError(err.inner.map((e) => e.message).join(', '));
} else {
setError(getErrMsg(err));
}
return false;
} finally {
setLoading(false);
}
}

async function deleteResult(resultId: number) {
try {
setLoading(true);
setError('');
await axiosEventsPrivate.delete('/api/Result', { data: { id: resultId } });
return true;
} catch (err) {
setError(getErrMsg(err));
return false;
} finally {
setLoading(false);
}
}

async function deleteAllResults(eventId: number) {
try {
setLoading(true);
setError('');
await axiosEventsPrivate.delete(`/api/Result/event/${eventId}`);
return true;
} catch (err) {
setError(getErrMsg(err));
return false;
} finally {
setLoading(false);
}
}

return {
loading,
error,
setError,
addResult,
updateResult,
deleteResult,
deleteAllResults,
};
}
16 changes: 15 additions & 1 deletion src/Hooks/Event/useEventDesc.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useContext, useState } from 'react';
import { ApiContext } from '../../Contexts/Api/ApiContext';
import { getErrMsg } from '../errorParser';
import { IEvent } from './eventTypes';
import { IEvent, IResult } from './eventTypes';

export function useEventDesc() {
const [event, setEvent] = useState<IEvent>();
Expand All @@ -19,12 +19,26 @@ export function useEventDesc() {
registrationEndDate: string;
}

interface IResultResponse {
isTeam: boolean;
results: IResult[];
}

const response = await axiosEventsPrivate.get<IEventResponse>(`/api/events/${eventId}`);

const resultsResponse = await axiosEventsPrivate.get<IResultResponse>(
`/api/Result/event/${eventId}`,
);

resultsResponse.data.results.sort((a: IResult, b: IResult) => {
return a.position - b.position;
});

const eventData: IEvent = {
...response.data,
datetime: new Date(response.data?.datetime),
registrationEndDate: new Date(response.data?.registrationEndDate),
results: resultsResponse.data.results,
};

setEvent(eventData);
Expand Down
6 changes: 3 additions & 3 deletions src/Hooks/Event/useScheduleList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useState } from 'react';
import { useCallback, useContext, useState } from 'react';
import { ApiContext } from 'Contexts/Api/ApiContext';
import {
getErrMsg,
Expand Down Expand Up @@ -209,7 +209,7 @@ export function useScheduleList() {
}
};

function validateSchedule(): boolean {
const validateSchedule = useCallback((): boolean => {
try {
createEventScheduleValidationSchema.validateSync(newEvent, {
abortEarly: false,
Expand All @@ -227,7 +227,7 @@ export function useScheduleList() {
}
return false;
}
}
}, [newEvent]);

async function createSchedule(): Promise<TupdateFnReturn> {
try {
Expand Down
Loading