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

Express checkin #150

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
65 changes: 65 additions & 0 deletions src/components/auth/CheckInModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import ModalCard from '@/components/auth/ModalCard';
import { Modal } from '@/components/common';
import { useRouter } from 'next/router';
import { useState } from 'react';
import styles from './style.module.scss';

interface CheckInModalProps {
open: boolean;
}

const CheckInModal = ({ open }: CheckInModalProps) => {
const router = useRouter();

const [isModalOpen, setIsModalOpen] = useState(open);

const handleModalClose = () => {
setIsModalOpen(false);
};

const CardContents = [
{
title: 'Have an account?',
subtitle: 'We missed you, welcome back. Login to get your points!',
onClick: () => {
handleModalClose();
},
},
{
title: 'New to ACM?',
subtitle: 'Express check-in to your first event with just your email!',
onClick: () => {
// Push to /express with the current query params
router.push({ pathname: '/express', query: router.query });
},
},
];

return (
// Justification for disabling eslint rules: Clicking on the faded area
// isn't the only way to close the modal; you can also click the close
// button. HTML already supports pressing the escape key to close modals, so
// I don't need to add my own.
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
<Modal
title="To finish checking in, sign in to your account!"
open={isModalOpen}
onClose={handleModalClose}
>
<div className={styles.contents}>
<div className={styles.cardContainer}>
{CardContents.map(card => (
<ModalCard
key={card.subtitle}
title={card.title}
subtitle={card.subtitle}
onClick={card.onClick}
/>
))}
</div>
</div>
</Modal>
);
};

export default CheckInModal;
59 changes: 59 additions & 0 deletions src/components/auth/CheckInModal/style.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@use 'src/styles/vars.scss' as vars;

.contents {
display: flex;
flex-direction: column;
gap: 2.5rem;
max-width: 40rem;
padding: 2.5rem;
width: 70vw;

.cardContainer {
align-items: center;
display: flex;
flex-direction: row;
gap: 1rem;
justify-content: center;

@media (max-width: vars.$breakpoint-md) {
flex-direction: column;
}

.card {
background: transparent;
border: 1px solid var(--theme-primary-6);
border-radius: 10px;
display: flex;
flex-direction: column;
min-height: 15rem;
-ms-overflow-style: none;
overflow-x: hidden;
padding: 0;
position: relative;
text-align: left;
transition: all 0.3s;
width: 20rem;

&:hover {
box-shadow: 0 0 8px var(--theme-primary-6);
transform: scale(1.025);
}

.cardHeader {
border-radius: 10px 10px 0 0;
font-size: 1.5rem;
font-weight: 600;
padding: 0.5rem 1rem;
text-align: left;
}

.cardSubHeader {
border-radius: 10px 10px 0 0;
font-size: 1.5rem;
font-weight: 600;
padding: 0.5rem 1rem;
text-align: left;
}
}
}
}
13 changes: 13 additions & 0 deletions src/components/auth/CheckInModal/style.module.scss.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type Styles = {
card: string;
cardContainer: string;
cardHeader: string;
cardSubHeader: string;
contents: string;
};

export type ClassNames = keyof Styles;

declare const styles: Styles;

export default styles;
28 changes: 28 additions & 0 deletions src/components/auth/ModalCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Typography } from '@/components/common';
import RightArrowIcon from '@/public/assets/icons/page-right-icon.svg';
import styles from './styles.module.scss';

interface ModalCardProps {
title: string;
subtitle: string;
onClick: () => void;
}

const ModalCard = ({ title, subtitle, onClick }: ModalCardProps) => {
return (
<button type="button" className={styles.card} onClick={onClick}>
<Typography className={styles.cardHeader} variant="title/medium" style={{ fontWeight: 700 }}>
{title}
</Typography>
<Typography className={styles.cardBody} variant="h5/regular">
{subtitle}
</Typography>
{/* Circle with arrow on the bottom right of the card */}
<div className={styles.cardFooter}>
<RightArrowIcon />
</div>
</button>
);
};

export default ModalCard;
38 changes: 38 additions & 0 deletions src/components/auth/ModalCard/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.card {
background: transparent;
border: 1px solid var(--theme-primary-6);
border-radius: 10px;
display: flex;
flex-direction: column;
height: 200px;
overflow-x: hidden;
padding: 0;
position: relative;
text-align: left;
transition: all 0.3s;
width: 400px;

&:hover {
box-shadow: 0 0 8px var(--theme-primary-6);
transform: scale(1.025);
}

.cardHeader {
padding: 0.5rem 1rem;
text-align: left;
}

.cardBody {
margin-bottom: auto;
padding: 0.5rem 1rem;
text-align: left;
}

.cardFooter {
align-items: center;
display: flex;
justify-content: end;
padding: 0.5rem 1rem;
width: 100%;
}
}
12 changes: 12 additions & 0 deletions src/components/auth/ModalCard/styles.module.scss.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type Styles = {
card: string;
cardBody: string;
cardFooter: string;
cardHeader: string;
};

export type ClassNames = keyof Styles;

declare const styles: Styles;

export default styles;
4 changes: 3 additions & 1 deletion src/components/auth/SignInFormItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ interface FormItemProps {
formRegister: UseFormRegisterReturn;
error: any;
inputHeight?: string;
value?: string;
}

type SignInFormProps = FormItemProps & (InputTypeProps | SelectTypeProps);

const SignInFormItem = (props: SignInFormProps) => {
const { icon, placeholder, formRegister, element, error, inputHeight } = props;
const { icon, placeholder, formRegister, element, error, inputHeight, value } = props;

if (element === 'input') {
const { type } = props;
Expand All @@ -35,6 +36,7 @@ const SignInFormItem = (props: SignInFormProps) => {
className={styles.inputField}
required
type={type}
value={value}
placeholder={placeholder}
style={{
lineHeight: inputHeight,
Expand Down
18 changes: 16 additions & 2 deletions src/lib/api/EventAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AttendEventRequest, Event } from '@/lib/types/apiRequests';
import {
AttendEventResponse,
CreateEventResponse,
ExpressCheckInResponse,
GetAllEventsResponse,
GetAttendancesForUserResponse,
GetFutureEventsResponse,
Expand Down Expand Up @@ -73,7 +74,7 @@ export const getAllEvents = async (): Promise<PublicEvent[]> => {
};

export const getAttendancesForUser = async (token: string): Promise<PublicAttendance[]> => {
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.attendance}`;
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.attendance.attendance}`;

const response = await axios.get<GetAttendancesForUserResponse>(requestUrl, {
headers: {
Expand All @@ -88,7 +89,7 @@ export const attendEvent = async (
token: string,
attendanceCode: string
): Promise<AttendEventResponse> => {
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.attendance}`;
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.attendance.attendance}`;

const requestBody = { attendanceCode, asStaff: false } as AttendEventRequest;

Expand All @@ -101,6 +102,19 @@ export const attendEvent = async (
return response.data;
};

export const expressCheckin = async (
email: string,
attendanceCode: string
): Promise<ExpressCheckInResponse> => {
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.attendance.expressCheckIn}`;

const requestBody = { email, attendanceCode } as AttendEventRequest;

const response = await axios.post<ExpressCheckInResponse>(requestUrl, requestBody);

return response.data;
};

export const createEvent = async (token: string, event: Event): Promise<PublicEvent> => {
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.event.event}`;

Expand Down
6 changes: 5 additions & 1 deletion src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ const config = {
future: '/event/future',
picture: '/event/picture',
},
attendance: '/attendance',
attendance: {
attendance: '/attendance',
expressCheckIn: '/attendance/expressCheckin',
},
leaderboard: '/leaderboard',
store: {
collection: '/merch/collection',
Expand Down Expand Up @@ -71,6 +74,7 @@ const config = {
},
homeRoute: '/',
eventsRoute: '/events',
expressCheckin: '/express',
loginRoute: '/login',
logoutRoute: '/logout',
leaderboardRoute: '/leaderboard',
Expand Down
21 changes: 20 additions & 1 deletion src/lib/managers/EventManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { EventAPI } from '@/lib/api';
import type { APIHandlerProps, AuthAPIHandlerProps } from '@/lib/types';
import type { AttendEventRequest, GetEventRequest } from '@/lib/types/apiRequests';
import type {
AttendEventRequest,
ExpressCheckInRequest,
GetEventRequest,
} from '@/lib/types/apiRequests';
import type { CustomErrorBody, PublicEvent } from '@/lib/types/apiResponses';

/**
Expand Down Expand Up @@ -57,3 +61,18 @@ export const attendEvent = async (
return e.response.data.error;
}
};

export const expressCheckIn = async (
data: ExpressCheckInRequest & APIHandlerProps
): Promise<PublicEvent | CustomErrorBody> => {
const { email, attendanceCode, onSuccessCallback, onFailCallback } = data;

try {
const response = await EventAPI.expressCheckin(email, attendanceCode);
onSuccessCallback?.(response.event);
return response.event;
} catch (e: any) {
onFailCallback?.(e.response.data.error);
return e.response.data.error;
}
};
5 changes: 5 additions & 0 deletions src/lib/types/apiRequests.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ export interface AttendEventRequest {
asStaff?: boolean;
}

export interface ExpressCheckInRequest {
attendanceCode: string;
email: string;
}

export interface SubmitEventFeedbackRequest {
feedback: string[];
}
Expand Down
4 changes: 4 additions & 0 deletions src/lib/types/apiResponses.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export interface AttendEventResponse extends ApiResponse {
event: PublicEvent;
}

export interface ExpressCheckInResponse extends ApiResponse {
event: PublicEvent;
}

// AUTH

export interface RegistrationResponse extends ApiResponse {
Expand Down
Loading
Loading