Skip to content

Commit

Permalink
Merge branch 'main' into sean/fulfill
Browse files Browse the repository at this point in the history
  • Loading branch information
SheepTester authored May 5, 2024
2 parents f93f5d1 + 958f5e5 commit 31432e7
Show file tree
Hide file tree
Showing 20 changed files with 484 additions and 61 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"cookies-next": "^2.1.1",
"cypress": "13.6.2",
"ics": "^3.7.2",
"jszip": "^3.10.1",
"luxon": "^3.3.0",
"next": "^13.2.5-canary.30",
"next-pwa": "^5.6.0",
Expand Down
46 changes: 46 additions & 0 deletions src/components/admin/Resume/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { GifSafeImage, Typography } from '@/components/common';
import { config } from '@/lib';
import { PublicResume } from '@/lib/types/apiResponses';
import { getFileName, getProfilePicture } from '@/lib/utils';
import Link from 'next/link';
import { BsDownload } from 'react-icons/bs';
import styles from './style.module.scss';

interface ResumeProps {
resume: PublicResume;
}

const Resume = ({ resume }: ResumeProps) => {
const fileName = getFileName(resume.url, 'resume.pdf');

return (
<div className={styles.wrapper}>
{resume.user ? (
<Link href={`${config.userProfileRoute}${resume.user.handle}`} className={styles.user}>
<GifSafeImage
src={getProfilePicture(resume.user)}
width={48}
height={48}
alt={`Profile picture for ${resume.user.firstName} ${resume.user.lastName}`}
className={styles.image}
/>
<Typography variant="label/large" className={styles.name}>
{resume.user.firstName} {resume.user.lastName}
</Typography>
<p className={styles.info}>
{resume.user.major} ({resume.user.graduationYear})
</p>
</Link>
) : null}
<Link href={resume.url} className={styles.resume}>
<BsDownload className={styles.image} />
<p className={styles.name}>{fileName}</p>
<p className={styles.info}>
Uploaded {new Date(resume.lastUpdated).toLocaleDateString('en-US', { dateStyle: 'full' })}
</p>
</Link>
</div>
);
};

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

.wrapper {
background-color: var(--theme-elevated-background);
border: 1px solid var(--theme-elevated-stroke);
border-radius: 0.5rem;
display: flex;
gap: 1rem;
padding: 1rem;
word-break: break-word;

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

.user,
.resume {
align-items: center;
display: grid;
gap: 0.5rem 1rem;
grid-template-areas:
'image name'
'image info';
grid-template-columns: auto 1fr;

.image {
grid-area: image;
}

.name {
grid-area: name;
}

&:hover .name {
text-decoration: underline;
}

.info {
grid-area: info;
}
}

.user {
flex: 1 0 0;

.image {
border-radius: 5rem;
object-fit: contain;
}
}

.resume {
flex: 1.5 0 0;

.image {
height: 1.5rem;
width: 1.5rem;

@media (max-width: vars.$breakpoint-md) {
width: 3rem;
}
}

.name {
color: var(--theme-blue-text-button);
}

.info {
color: var(--theme-text-on-background-2);
}
}
}
14 changes: 14 additions & 0 deletions src/components/admin/Resume/style.module.scss.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type Styles = {
image: string;
info: string;
name: string;
resume: string;
user: string;
wrapper: string;
};

export type ClassNames = keyof Styles;

declare const styles: Styles;

export default styles;
5 changes: 3 additions & 2 deletions src/components/store/CollectionSlider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import ItemCard from '@/components/store/ItemCard';
import StoreEditButton from '@/components/store/StoreEditButton';
import { config } from '@/lib';
import { PublicMerchItem } from '@/lib/types/apiResponses';
import { getDefaultMerchItemPhoto } from '@/lib/utils';
import Link from 'next/link';
import styles from './style.module.scss';

Expand Down Expand Up @@ -40,7 +39,9 @@ const CollectionSlider = ({
.map(item => (
<ItemCard
className={styles.card}
image={getDefaultMerchItemPhoto(item)}
images={[...item.merchPhotos]
.sort((a, b) => a.position - b.position)
.map(photo => photo.uploadedPhoto)}
title={item.itemName}
href={`${config.store.itemRoute}${item.uuid}`}
cost={item.options[0]?.price ?? 0}
Expand Down
10 changes: 7 additions & 3 deletions src/components/store/ItemCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Diamonds from '@/components/store/Diamonds';
import NoImage from '@/public/assets/graphics/cat404.png';
import Image from 'next/image';
import Link from 'next/link';
import { PropsWithChildren } from 'react';
import styles from './style.module.scss';

interface CommonOptions {
image: string;
images: string[];
title: string;
href: string;
className?: string;
Expand All @@ -29,18 +30,21 @@ type ItemCardProps = (StoreItemOptions | CollectionOptions) & CommonOptions;
* - a collection with a `description`.
*/
const ItemCard = ({
image,
images,
title,
href,
className,
children,
...props
}: PropsWithChildren<ItemCardProps>) => {
const [first = NoImage, second = first] = images;

return (
<article className={`${styles.itemCard} ${className}`}>
<Link href={href} className={styles.linkWrapper}>
<div className={styles.imageWrapper}>
<Image src={image} alt={title} fill />
<Image className={styles.first} src={first} alt={title} fill />
<Image src={second} alt="" aria-hidden fill />
</div>
<div className={styles.details}>
<p className={styles.title}>{title}</p>
Expand Down
10 changes: 10 additions & 0 deletions src/components/store/ItemCard/style.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@
img {
object-fit: cover;
}

.first {
transition: opacity 0.5s;
z-index: 1;
}
}

&:hover .imageWrapper .first {
opacity: 0;
}

.details {
Expand Down Expand Up @@ -63,5 +72,6 @@
position: absolute;
right: 0.5rem;
top: 0.5rem;
z-index: 2;
}
}
1 change: 1 addition & 0 deletions src/components/store/ItemCard/style.module.scss.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type Styles = {
cost: string;
details: string;
first: string;
icons: string;
imageWrapper: string;
itemCard: string;
Expand Down
26 changes: 22 additions & 4 deletions src/lib/api/ResumeAPI.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { config } from '@/lib';
import type { UUID } from '@/lib/types';
import { PatchResumeRequest } from '@/lib/types/apiRequests';
import type {
PatchResumeResponse,
PublicResume,
UpdateResumeResponse,
import {
GetVisibleResumesResponse,
type PatchResumeResponse,
type PublicResume,
type UpdateResumeResponse,
} from '@/lib/types/apiResponses';
import axios from 'axios';

Expand Down Expand Up @@ -76,3 +77,20 @@ export const deleteResume = async (token: string, uuid: UUID): Promise<void> =>
},
});
};

/**
* Get all visible resumes
* @param token Authorization bearer token. User needs to be admin or
* sponsorship manager
*/
export const getResumes = async (token: string): Promise<PublicResume[]> => {
const requestUrl = `${config.api.baseUrl}${config.api.endpoints.resume}`;

const response = await axios.get<GetVisibleResumesResponse>(requestUrl, {
headers: {
Authorization: `Bearer ${token}`,
},
});

return response.data.resumes;
};
22 changes: 13 additions & 9 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
type StaticImport,
type StaticRequire,
} from 'next/dist/shared/lib/get-img-props';
import { useEffect, useState } from 'react';
import { useEffect, useMemo } from 'react';

/**
* Get next `num` years from today in a number array to generate dropdown options for future selections
Expand Down Expand Up @@ -170,18 +170,15 @@ export const isSrcAGif = (src: string | StaticImport): boolean => {
* @returns The object URL. Defaults an empty string if `file` is empty.
*/
export function useObjectUrl(file?: Blob | null): string {
const [url, setUrl] = useState('');
const url = useMemo(() => (file ? URL.createObjectURL(file) : ''), [file]);

useEffect(() => {
if (!file) {
return undefined;
}
const url = URL.createObjectURL(file);
setUrl(url);
return () => {
URL.revokeObjectURL(url);
if (url !== '') {
URL.revokeObjectURL(url);
}
};
}, [file]);
}, [url]);

return url;
}
Expand Down Expand Up @@ -473,3 +470,10 @@ export function seededRandom(a: number, b: number, c: number, d: number): () =>
/* eslint-enable no-bitwise, no-param-reassign */
};
}

/**
* Gets the file name from a URL by taking the last part of the URL.
*/
export function getFileName(url: string, defaultName: string): string {
return decodeURIComponent(url.split('/').at(-1) ?? defaultName);
}
Loading

0 comments on commit 31432e7

Please sign in to comment.