Skip to content

Commit

Permalink
Clean up codebase
Browse files Browse the repository at this point in the history
  • Loading branch information
farisashai committed Apr 4, 2023
1 parent e05d436 commit 33a1a33
Show file tree
Hide file tree
Showing 38 changed files with 274 additions and 377 deletions.
3 changes: 1 addition & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
NEXT_PUBLIC_ACM_API_URL="https://api.acmucsd.com/api/v2"
NEXT_PUBLIC_NOTION_API_TOKEN="""
NEXT_PUBLIC_ACM_API_URL="https://api.acmucsd.com/api/v2"
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,18 +186,18 @@ default, you must have the VSCode setting `"editor.formatOnSave": true` enabled.
- `/public`: Our public folder stores all static assets which will be hosted on the website domain
and accessible to any users directly. E.g. `public/pic.jpg` would be viewable to any user at
`localhost:3000/pic.jpg`
- `/assets`: Our assets folder stores all static images which are necessary for our
application. By storing them statically, we don't have to include them in our JavaScript
application bundle and can identify images with relative paths such as
`<img src='/assets/icons/acm-icon.svg'>` and make them accessible to any users directly. E.g.
`public/pic.jpg` would be viewable to any user at `localhost:3000/pic.jpg`
- `/assets`: Our assets folder stores all static images which are necessary for our application.
By storing them statically, we don't have to include them in our JavaScript application bundle
and can identify images with relative paths such as `<img src='/assets/icons/acm-icon.svg'>` and
make them accessible to any users directly. E.g. `public/pic.jpg` would be viewable to any user
at `localhost:3000/pic.jpg`
- `/src`: Our source folder is where all client-side code for our application is stored.

- `/pages`: The pages folder is a Next.js feature which allows us to take advantage of a built-in
routing tool that comes with the framework. When we visit a page
`localhost:3000/portal/leaderboard`, the data that is rendered is the component that returns
from `pages/portal/leaderboard.tsx`. The only components that live in this folder are those that
have a 1:1 correspondance to a certain route on our application's sitemap.
routing tool that comes with the framework. When we visit a page `localhost:3000/leaderboard`,
the data that is rendered is the component that returns from `pages/leaderboard.tsx`. The only
components that live in this folder are those that have a 1:1 correspondance to a certain route
on our application's sitemap.
- `/styles`: The styles folder is where we define any top-level SCSS stylesheets. This includes
those used globally across the application to function properly and styles used directly in
top-level page components. Individual component-level styles will be defined in a folder
Expand Down Expand Up @@ -230,7 +230,7 @@ default, you must have the VSCode setting `"editor.formatOnSave": true` enabled.
- `/types`: The types folder stores all reusable type declarations. This includes API request
body types and API response types for every endpoint on the ACM Portal API and any other API
services. It also details sets of options such as account access types for the application
state and generic alias types such as `Uuid` and `Url` used to better signify string values.
state and generic alias types such as `UUID` and `URL` used to better signify string values.
If you are working in development, we still recommend adds types to as many values as you can.
Rather than leaving values untyped, feel free to use the `any` alias type `FillInLater` to
bypass TypeScript security and validation checks. However, any merged pull requests must
Expand Down
10 changes: 3 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "membership-portal-v2",
"version": "2.0.0",
"version": "1.0.0",
"private": true,
"packageManager": "[email protected]",
"engines": {
Expand All @@ -24,12 +24,9 @@
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@mui/material": "^5.11.6",
"@notionhq/client": "^2.2.3",
"@svgr/webpack": "^6.5.1",
"@types/validator": "^13.7.12",
"axios": "^1.1.3",
"axios-middleware": "^0.3.1",
"cookie": "^0.5.0",
"cookies-next": "^2.1.1",
"lodash": "^4.17.21",
"next": "12.3.1",
Expand All @@ -48,6 +45,7 @@
"@types/lodash": "^4.14.191",
"@types/node": "18.8.1",
"@types/react": "18.0.21",
"@types/validator": "^13.7.14",
"@typescript-eslint/eslint-plugin": "^5.40.1",
"eslint": "8.24.0",
"eslint-config-airbnb": "^19.0.4",
Expand All @@ -58,12 +56,10 @@
"eslint-plugin-jsdoc": "^39.3.13",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react": "^7.32.2",
"prettier": "^2.7.1",
"stylelint": "^14.13.0",
"stylelint-config-sass-guidelines": "^9.0.1",
"stylelint-config-standard": "^28.0.0",
"stylelint-order": "^5.0.0",
"typed-scss-modules": "^7.0.2"
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/auth/SignInButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Url } from '@/lib/types';
import type { URL } from '@/lib/types';
import Link from 'next/link';
import type { CSSProperties, ReactNode } from 'react';
import styles from './style.module.scss';

interface LinkInterface {
type: 'link';
href: Url;
href: URL;
text: string | ReactNode;
}

Expand Down
1 change: 0 additions & 1 deletion src/components/auth/SignInFormItem/style.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
border-bottom: 1px solid var(--theme-text-on-background-1);
display: grid;
grid-template-columns: 2.25rem 1fr;
// height: 1.5rem;
width: 100%;

.iconContainer {
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// TODO: Implement
// TODO: Implement generic buttons that follow our design system
const Button = () => {
return <button type="button">todo</button>;
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/VerticalForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const VerticalForm = (props: PropsWithChildren<VerticalFormProps>) => {
className={styleModule.verticalForm}
onKeyDown={e => {
if (e.key === 'Enter') {
if (onEnterPress) onEnterPress();
onEnterPress?.();
}
}}
role="presentation"
Expand Down
12 changes: 6 additions & 6 deletions src/components/layout/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,32 @@ import styles from './style.module.scss';
const navItems = {
portal: [
{
route: '/portal',
route: '/',
title: 'Dashboard',
image: <DashboardIcon />,
},
{
route: '/portal/leaderboard',
route: '/leaderboard',
title: 'Leaderboard',
image: <LeaderboardIcon />,
},
{
route: '/portal/profile',
route: '/profile',
title: 'Profile',
image: <ProfileIcon />,
},
{
route: '/portal/about',
route: '/about',
title: 'Explore ACM',
image: <ACMIcon />,
},
{
route: '/portal/discord',
route: '/discord',
title: 'Discord',
image: <DiscordIcon />,
},
{
route: '/portal/admin',
route: '/admin',
title: 'Admin',
image: <SettingIcon />,
},
Expand Down
3 changes: 2 additions & 1 deletion src/components/layout/PageHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import DarkModeToggle from '@/components/layout/DarkModeToggle';
import { config } from '@/lib';
import Image from 'next/image';
import Link from 'next/link';
import styles from './style.module.scss';

const PageHeader = () => (
<header className={styles.header}>
<div className={styles.content}>
<Link href="/portal" passHref>
<Link href={config.homeRoute} passHref>
<a href="replace" className={styles.navLeft}>
<Image
src="/assets/acm-logos/general/light-mode.png"
Expand Down
8 changes: 3 additions & 5 deletions src/lib/api/AuthAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default class AuthAPI {
* @returns Bearer token on successful login
*/
static async login(data: LoginRequest): Promise<string> {
const requestUrl = `${config.acmApi.baseUrl}${config.acmApi.endpoints.auth.login}`;
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.auth.login}`;

const response = await axios.post<LoginResponse>(requestUrl, data);

Expand All @@ -21,11 +21,9 @@ export default class AuthAPI {
/**
* Send a password reset email request to the server for the given email
* @param {string} email The email address to send the password reset email to
* @returns {Promise<void>} Promise that resolves with no value on successful completion
* @throws {Error} If the request to the server fails or returns an error response
*/
static async sendPasswordResetEmailRequest(email: string): Promise<void> {
const requestUrl = `${config.acmApi.baseUrl}${config.acmApi.endpoints.auth.resetPassword}/${email}`;
static async sendPasswordResetEmail(email: string): Promise<void> {
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.auth.resetPassword}/${email}`;

await axios.get<SendPasswordResetEmailResponse>(requestUrl);
}
Expand Down
16 changes: 8 additions & 8 deletions src/lib/api/EventAPI.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { config } from '@/lib';
import { Uuid } from '@/lib/types';
import { UUID } from '@/lib/types';
import {
GetAllEventsResponse,
GetFutureEventsResponse,
Expand All @@ -10,13 +10,13 @@ import axios from 'axios';

export default class EventAPI {
/**
* Get single event by uuid
* @param uuid Search query
* Get a single event by UUID
* @param uuid Search query uuid
* @param token Bearer token
* @returns Event info
*/
static async getEvent(uuid: Uuid, token: string): Promise<PublicEvent> {
const requestUrl = `${config.acmApi.baseUrl}${config.acmApi.endpoints.event.event}/${uuid}`;
static async getEvent(uuid: UUID, token: string): Promise<PublicEvent> {
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.event.event}/${uuid}`;

const response = await axios.get<GetOneEventResponse>(requestUrl, {
headers: {
Expand All @@ -33,7 +33,7 @@ export default class EventAPI {
* @returns List of event info
*/
static async getAllFutureEvents(token: string): Promise<PublicEvent[]> {
const requestUrl = `${config.acmApi.baseUrl}${config.acmApi.endpoints.event.future}`;
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.event.future}`;

const response = await axios.get<GetFutureEventsResponse>(requestUrl, {
headers: {
Expand All @@ -50,7 +50,7 @@ export default class EventAPI {
* @returns List of event info
*/
static async getAllPastEvents(token: string): Promise<PublicEvent[]> {
const requestUrl = `${config.acmApi.baseUrl}${config.acmApi.endpoints.event.past}`;
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.event.past}`;

const response = await axios.get<GetFutureEventsResponse>(requestUrl, {
headers: {
Expand All @@ -67,7 +67,7 @@ export default class EventAPI {
* @returns List of event info
*/
static async getAllEvents(token: string): Promise<PublicEvent[]> {
const requestUrl = `${config.acmApi.baseUrl}${config.acmApi.endpoints.event.event}`;
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.event.event}`;

const response = await axios.get<GetAllEventsResponse>(requestUrl, {
headers: {
Expand Down
7 changes: 3 additions & 4 deletions src/lib/api/UserAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import config from '@/lib/config';
import { GetCurrentUserResponse, PrivateProfile } from '@/lib/types/apiResponses';
import axios from 'axios';

// TODO: Add some middleware to handle
export default class UserAPI {
/**
* Get current user's private profile
* @param token Bearer token
* @returns User profile
* @param token Authorization bearer token
* @returns User's full profile
*/
static async getCurrentUser(token: string): Promise<PrivateProfile> {
const requestUrl = `${config.acmApi.baseUrl}${config.acmApi.endpoints.user.user}`;
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.user.user}`;

const response = await axios.get<GetCurrentUserResponse>(requestUrl, {
headers: {
Expand Down
62 changes: 4 additions & 58 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,8 @@
import type { Url } from '@/lib/types';

const env = process.env.NODE_ENV;
const isDevelopment = env !== 'production';

export interface ConfigType {
acmApi: {
baseUrl: Url;
endpoints: {
user: {
user: '/user';
activity: '/user/activity';
profilepicture: '/user/picture';
};
activity: '/activity';
auth: {
register: '/auth/registration';
login: '/auth/login';
verification: '/auth/verification';
resetPassword: '/auth/passwordReset';
emailVerification: '/auth/emailVerification';
emailModification: '/auth/emailModification';
};
admin: {
attendance: '/admin/attendance';
bonus: '/admin/bonus';
emails: '/admin/email';
};
event: {
event: '/event';
past: '/event/past';
future: '/event/future';
picture: '/event/picture';
};
attendance: '/attendance';
leaderboard: '/leaderboard';
store: {
collection: '/merch/collection';
item: '/merch/item';
itemPicture: '/merch/item/picture';
option: '/merch/option';
verification: '/merch/order/verification';
order: '/merch/order';
orders: '/merch/orders';
pickup: {
future: '/merch/order/pickup/future';
past: '/merch/order/pickup/past';
single: '/merch/order/pickup';
};
};
};
};
homeRoute: '/portal/';
isDevelopment: boolean;
marketingWebhookUrl: string;
}

const config: ConfigType = {
acmApi: {
const config = {
api: {
baseUrl: process.env.NEXT_PUBLIC_ACM_API_URL || 'https://testing.api.acmucsd.com/api/v2',
endpoints: {
user: {
Expand Down Expand Up @@ -102,9 +48,9 @@ const config: ConfigType = {
},
},
},
homeRoute: '/portal/',
homeRoute: '/',
loginRoute: '/login',
isDevelopment,
marketingWebhookUrl: process.env.NEXT_PUBLIC_MARKETING_WEBHOOK_URL ?? '',
};

export default config;
9 changes: 7 additions & 2 deletions src/lib/hoc/protectPage.ts → src/lib/hoc/withAccessType.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import config from '@/lib/config';
import { CookieService } from '@/lib/services';
import { PrivateProfile } from '@/lib/types/apiResponses';
import { CookieType, UserAccessType } from '@/lib/types/enums';
import * as _ from 'lodash';
import { GetServerSideProps, GetServerSidePropsContext } from 'next';

// TODO: We will need a third argument to specify where to send them if they don't have access later so the admin pages can send regular members back to the home page instead of login

/**
* Redirects to login if user is not logged in and allowed to see the specified page
* @param gssp Server-side props function to run afterwards
* @param validAccessTypes Access types that can see this page
* @returns
*/
export default function protectPage(
export default function withAccessType(
gssp: GetServerSideProps,
validAccessTypes: UserAccessType[]
): GetServerSideProps {
// Generate a new getServerSideProps function by taking the return value of the original function and appending the user prop onto it if the user cookie exists, otherwise force user to login page
const modified: GetServerSideProps = async (context: GetServerSidePropsContext) => {
// Initialize variables
const { req, res } = context;

const originalReturnValue = await gssp(context);
// TODO: Save current URL so user can return here after logging in
const safeRedirectToLogin = {
redirect: {
destination: `/?redirect=`, // TODO: Save previous URL to redirect on login
destination: `${config.loginRoute}?redirect=`,
permanent: false,
},
};
Expand Down
5 changes: 5 additions & 0 deletions src/lib/managers/AccountManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { PrivateProfile } from '@/lib/types/apiResponses';
import { CookieType } from '@/lib/types/enums';

export default class AccountManager {
/**
* Handle request to get current user's full profile
* @param data Request paramaters object
* @returns Full user profile
*/
static async getCurrentUser(data: AuthAPIHandlerProps): Promise<PrivateProfile | undefined> {
const { token, onSuccessCallback, onFailCallback } = data;

Expand Down
Loading

1 comment on commit 33a1a33

@vercel
Copy link

@vercel vercel bot commented on 33a1a33 Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.