Skip to content
This repository has been archived by the owner on Sep 29, 2024. It is now read-only.

feat: Add bare events page #223

Merged
merged 36 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
19a1d07
feat(client): Create createevents.ts
ArshiLamba Jul 21, 2024
55899cf
feat(client): Create eventlist.tsx
ArshiLamba Jul 21, 2024
8547dff
feat(client): Create schema event.ts
ArshiLamba Jul 21, 2024
194b1e4
refactor(client): Add test data for eventlist.tsx
ArshiLamba Jul 21, 2024
4accce6
refactor: adding schemas for create events and event list page
ArshiLamba Jul 21, 2024
65e9a31
refactor: update route-map.tsx to include eventlist page
ArshiLamba Jul 26, 2024
40a7ad4
Add missing event schema to api-types in server
ArshiLamba Jul 26, 2024
f2e2d03
Synchronize shared/api-types with server and client
ArshiLamba Jul 26, 2024
40138cf
updated all relevant server side files for events
ArshiLamba Jul 26, 2024
7f35b91
Sync server api-types with shared/api-types
ArshiLamba Jul 26, 2024
07a8382
Sync server api-types with shared/api-types
ArshiLamba Jul 26, 2024
4854c9f
update the API routes for event.ts
ArshiLamba Jul 26, 2024
c85d138
lint issues #898
ArshiLamba Jul 26, 2024
6a82e1b
Merge branch 'main' into feature/events
ArshiLamba Jul 26, 2024
09b2008
refactor: rectify errors
ArshiLamba Jul 27, 2024
3d31a69
Merge branch 'main' into feature/events
ArshiLamba Jul 31, 2024
b7a09a9
refactor: fix linting errors
ArshiLamba Jul 31, 2024
b9fa957
fix: fix build errors
ArshiLamba Jul 31, 2024
e343a33
refactor: do up the API for events
ArshiLamba Jul 31, 2024
f697514
add changes to event.ts
ArshiLamba Jul 31, 2024
29f23c4
refactor: fix changes to API
ArshiLamba Jul 31, 2024
401416c
refactor: fix code
ArshiLamba Jul 31, 2024
7023483
refactor: sync api types with shared api types
ArshiLamba Aug 1, 2024
c93f7eb
feat: add API for delete events
ArshiLamba Aug 1, 2024
1cf6b31
fix: fix all formatting and pr errors
ArshiLamba Aug 1, 2024
e2c325a
Merge branch 'feature/staffhomepage' into feature/AccountSettings
ArshiLamba Aug 1, 2024
361aac8
fix:fix formatting errors
ArshiLamba Aug 1, 2024
fea2800
fix: made changes for application to function
ArshiLamba Aug 1, 2024
6a9d66f
feat: add /v1/event DELETE
ArshiLamba Aug 1, 2024
3727846
chore: Fix package lock
caffeine-addictt Aug 1, 2024
d1a7b13
Merged 'main' in
caffeine-addictt Aug 1, 2024
285e1ee
chore: Add license header
caffeine-addictt Aug 1, 2024
d78eda8
fix: DO NOT ADD TYPES IN API ONLY
caffeine-addictt Aug 1, 2024
b6abc6d
fix: Merge artifacts
caffeine-addictt Aug 1, 2024
62a40ee
chore: Get rid of useless comments
caffeine-addictt Aug 1, 2024
26725ef
fix: DO NOT TRY CATCH A PROMISE
caffeine-addictt Aug 1, 2024
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
8 changes: 8 additions & 0 deletions client/src/lib/api-types/schemas/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ export const eventSchema = z.object({
location: z.string().min(1),
description: z.string().optional(),
});

export const eventRequestObject = z.object({
title: z.string().min(1),
date: z.string().min(1), // ISO string format
time: z.string().min(1),
location: z.string(),
description: z.string().optional(),
});
4 changes: 2 additions & 2 deletions client/src/pages/auth/route-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import ActivatePage from './activate';
import { PasskeyLoginPage, PasskeyRegisterPage } from './passkey';
import RedirectToAuth from './auth-redirect';

const authrouteMap: RouteMap = {
const authRouteMap: RouteMap = {
'/register': {
title: 'Register',
description: 'Register a new account with us!',
Expand Down Expand Up @@ -93,4 +93,4 @@ const authrouteMap: RouteMap = {
accessLevel: 'authenticated',
},
} as const;
export default authrouteMap;
export default authRouteMap;
10 changes: 5 additions & 5 deletions client/src/pages/route-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
*/

import * as React from 'react';
import authrouteMap from '@pages/auth/route-map';
import userrouteMap from '@pages/user/route-map';
import foodRouteMap from './food/route-map';

// Page imports
import NotFound from '@pages/404';
import RootPage from '@pages/root';
import authRouteMap from '@pages/auth/route-map';
import userRouteMap from './user/route-map';
import foodRouteMap from './food/route-map';

export type PathStr = `/${string}`;
export type RootPathStr = '*' | '/' | PathStr;
Expand Down Expand Up @@ -41,8 +41,8 @@ const routes: RootRouteMap = {
accessLevel: 'admin',
title: 'Admin',
},
...authrouteMap,
...userrouteMap,
...authRouteMap,
...userRouteMap,
...foodRouteMap,
} as const;
export default routes;
58 changes: 31 additions & 27 deletions client/src/pages/user/create-events.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
/**
* SPDX-FileCopyrightText: 2024 Ng Jun Xiang <[email protected]>
*
* SPDX-License-Identifier: GPL-3.0-only
*/

import { useState } from 'react';
import { useForm, useFormState } from 'react-hook-form';
import httpClient from '@utils/http';
import { Button } from '@components/ui/button';
import { Input } from '@components/ui/input';

import {
Form,
FormField,
Expand All @@ -12,8 +16,12 @@ import {
FormDescription,
FormMessage,
} from '@components/ui/form';
import { Input } from '@components/ui/input';
import { Button } from '@components/ui/button';

import { z } from 'zod';
import { eventSchema } from '@lib/api-types/schemas/event'; // Adjust the import path as needed
import httpClient from '@utils/http';
import { eventSchema } from '@lib/api-types/schemas/event';
import { PageComponent } from '@pages/route-map';

// Define the Event type using z.infer and eventSchema
Expand All @@ -26,7 +34,7 @@ const EventCreationPage: PageComponent = () => {
const eventForm = useForm<Event>({
mode: 'onBlur',
defaultValues: {
date: new Date(), // Default value set as today
date: new Date(),
time: '',
location: '',
description: '',
Expand All @@ -39,31 +47,27 @@ const EventCreationPage: PageComponent = () => {
});

const handleSave = async (data: Event) => {
try {
// Convert date to ISO string and combine with time
const formattedDateTime = `${new Date(data.date).toISOString().split('T')[0]}T${data.time}`;

// Prepare the payload with Date object
const payload = {
...data,
date: new Date(formattedDateTime), // Convert to Date object for payload
};

console.log('Sending request with data:', payload); // Log the data being sent
const response = await httpClient.post<Event, Event>({
const response = await httpClient
.post<unknown, Event>({
uri: '/event',
payload: payload,
payload: {
...data,
date: new Date(
`${new Date(data.date).toISOString().split('T')[0]}T${data.time}`,
),
},
withCredentials: 'access',
})
.then(() => {
console.log('Response received:', response);
setSuccessMessage('Event created successfully');
setError(null);
eventForm.reset();
})
.catch((err) => {
console.error('Error creating event:', err);
setError('Failed to create event');
});

console.log('Response received:', response);
setSuccessMessage('Event created successfully');
setError(null);
eventForm.reset();
} catch (err) {
console.error('Error creating event:', err);
setError('Failed to create event');
}
};

return (
Expand Down
15 changes: 11 additions & 4 deletions client/src/pages/user/event-list.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
/**
* SPDX-FileCopyrightText: 2024 Ng Jun Xiang <[email protected]>
*
* SPDX-License-Identifier: GPL-3.0-only
*/

import { z } from 'zod';
import { useEffect, useState } from 'react';

import httpClient from '@utils/http';
import { z } from 'zod';
import { eventSchema } from '@lib/api-types/schemas/event'; // Adjust the import path as needed
import { PageComponent } from '@pages/route-map';
import { eventSchema } from '@lib/api-types/schemas/event';

// Define the Event type using z.infer and eventSchema
type Event = z.infer<typeof eventSchema>;
Expand All @@ -17,7 +24,7 @@ const EventList: PageComponent = () => {
try {
const response = await httpClient.get<{ data: Event[] }>({
uri: `/event`,
withCredentials: 'access', // Adjust if needed
withCredentials: 'access',
});
setEvents(response.data);
} catch (err) {
Expand All @@ -33,7 +40,7 @@ const EventList: PageComponent = () => {
try {
await httpClient.delete<{ id: number }>({
uri: `/event/${id}`,
withCredentials: 'access', // Adjust if needed
withCredentials: 'access',
});

// Remove the deleted event from the list
Expand Down
8 changes: 8 additions & 0 deletions server/src/lib/api-types/schemas/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ export const eventSchema = z.object({
location: z.string().min(1),
description: z.string().optional(),
});

export const eventRequestObject = z.object({
title: z.string().min(1),
date: z.string().min(1), // ISO string format
time: z.string().min(1),
location: z.string(),
description: z.string().optional(),
});
127 changes: 45 additions & 82 deletions server/src/v1/event.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
/**
* SPDX-FileCopyrightText: 2024 Ng Jun Xiang <[email protected]>
*
* SPDX-License-Identifier: GPL-3.0-only
*/

import { db } from '../db';
import { eq } from 'drizzle-orm';
import { eventTable } from '../db/schemas';
import { IAuthedRouteHandler } from '../route-map';

import {
GetEventSuccAPI,
GetEventFailAPI,
Expand All @@ -9,14 +16,11 @@ import {
DeleteEventSuccAPI,
DeleteEventFailAPI,
} from '../lib/api-types/event';
import { z } from 'zod';
import { eq } from 'drizzle-orm';

// API handler for fetching events
export const getEvent: IAuthedRouteHandler = async (req, res) => {
// Example usage of req (e.g., logging user info or other data)
console.log('Fetching events for:', req.user.id); // Assuming `req.user` exists
import { eventRequestObject } from '../lib/api-types/schemas/event';
import { IAuthedRouteHandler } from '../route-map';

// Handle /v1/event GET
export const getEvent: IAuthedRouteHandler = async (_, res) => {
// Fetch all events
const events = await db.select().from(eventTable);

Expand All @@ -32,46 +36,24 @@ export const getEvent: IAuthedRouteHandler = async (req, res) => {
const formattedEvents = events.map((event) => ({
id: event.id,
title: event.title,
date: new Date(event.date), // Ensure date is a Date object
date: new Date(event.date),
time: event.time,
location: event.location,
description: event.description || undefined, // Ensure description is optional
description: event.description || undefined,
}));

// Construct the response object
const response: GetEventSuccAPI = {
// Send the response
return res.status(200).json({
status: 200,
data: formattedEvents,
};

// Send the response
return res.status(200).json(response);
} satisfies GetEventSuccAPI);
};

// Define Zod objects for validation and response formatting
const eventRequestObject = z.object({
title: z.string().min(1),
date: z.string().min(1), // ISO string format
time: z.string().min(1),
location: z.string(),
description: z.string().optional(),
});

const eventResponseObject = z.object({
id: z.number(),
title: z.string(),
date: z.date(), // Use string to match ISO format for date
time: z.string(),
location: z.string(),
description: z.string().optional(),
});

// Handle /v1/event POST
export const createEvent: IAuthedRouteHandler = async (req, res) => {
// Validate request body
const validationResult = eventRequestObject.safeParse(req.body);

if (!validationResult.success) {
const errorStack = validationResult.error.errors.map((error) => ({
const validated = eventRequestObject.safeParse(req.body);
if (!validated.success) {
const errorStack = validated.error.errors.map((error) => ({
message: error.message,
context: {
property: error.path.join('.'),
Expand All @@ -85,56 +67,37 @@ export const createEvent: IAuthedRouteHandler = async (req, res) => {
} satisfies CreateEventFailAPI);
}

try {
const { title, date, time, location, description } = validationResult.data;

// Convert date string to Date object
const dateObject = new Date(date);

// Create a new event and return the created event including its ID
const [newEvent] = await db
.insert(eventTable)
.values({
userId: req.user.id, // Make sure `req.user.id` is defined
title,
date: dateObject, // Ensure date is a Date object
time,
location: location || '', // Default to empty string if undefined
description: description || '', // Default to empty string if undefined
})
.returning();
// Insert to DB
const [newEvent] = await db
.insert(eventTable)
.values({
userId: req.user.id,
title: validated.data.title,
date: new Date(validated.data.date),
time: validated.data.time,
location: validated.data.location || '',
description: validated.data.description || '',
})
.returning();

// Format the response data using eventResponseObject
const responseData = eventResponseObject.parse({
id: newEvent.id,
title: newEvent.title,
date: newEvent.date, // Convert Date object to ISO string
time: newEvent.time,
location: newEvent.location || '', // Default to empty string if undefined
description: newEvent.description || '', // Default to empty string if undefined
});

return res.status(200).json({
status: 200,
data: responseData,
} satisfies CreateEventSuccAPI);
} catch (error) {
console.error('Server error:', error); // Log unexpected errors
return res.status(500).json({
status: 500,
errors: [
{ message: 'An unexpected error occurred. Please try again later.' },
],
});
}
return res.status(200).json({
status: 200,
data: {
...newEvent,
title: newEvent.title ?? '',
description: newEvent.description ?? '',
},
} satisfies CreateEventSuccAPI);
};

// Handle /v1/event/:id DELETE
export const deleteEvent: IAuthedRouteHandler = async (req, res) => {
const eventId = parseInt(req.params.id, 10);

if (isNaN(eventId)) {
if (isNaN(eventId) || eventId < 0) {
return res.status(400).json({
status: 400,
errors: [{ message: 'Invalid event ID' }], // Using 'errors' array with message
errors: [{ message: 'Invalid event ID' }],
} satisfies DeleteEventFailAPI);
}

Expand Down
8 changes: 8 additions & 0 deletions shared/api-types/src/schemas/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ export const eventSchema = z.object({
location: z.string().min(1),
description: z.string().optional(),
});

export const eventRequestObject = z.object({
title: z.string().min(1),
date: z.string().min(1), // ISO string format
time: z.string().min(1),
location: z.string(),
description: z.string().optional(),
});