Skip to content

Commit

Permalink
admin photos for board members (#274)
Browse files Browse the repository at this point in the history
* Added retroactive attendance / milestone for admin

* Fixed feedback from Sean

* fixed feedback

* fixed feedback

* Reordered checkout layout for store (#263)

* Added new component for pickup dropdown for desktop view

* Conditionally adds visible overflow for desktop

* Removed min height

* fulfill (#244)

* Add fulfill button

* Remove Fulfill view and move fulfill buttons to prepare view

* Do not show fulfill button if it's already fulfilled

* make order status component

* hide fulfill button if pickup event hasnt started yet

* Apparently partially fulfilled means they are missing some items

* move order row to separate component

* fulfill selected

* fix type errors

* always show fulfilled items

* sorting things and other misc changes:

- show green "fulfilled" when fulfilling a previously partially fulfilled order
- sort orders by PLACED first
- sort items alphabetically
- item breakdown: ignore fulfilled. (hmm, this isn't the right thing to do for rescheduled orders)
- indicate when no items were picked up

* exclude fulfilled items from item breakdown

* default checkboxes to unchecked

* fix fulfilling selected. always indicate whether an item was fulfilled

* Show fulfilled badges

* Labels back to emoji, have checkbox select all of an entire variant at a time

If we want granularity in the future then that can be added later

* Display "fulfilled" on fulfilled items in fulfilled/partially fulfilled orders

* Make "picked up" badge green

* Address feedback

* Use Pacific Time on server side (#264)

* feat: Onboarding (#265)

* set up a modal for onboarding

* Show steps and progress bar

* Change design of buttons

* Create onboarding page

* Add animations for steps 1 and 5

* temporary images for intro

* add rainbow bar to top of page previews

* Quiet navbar, perhaps?

* address some feedback

* Move event list to its own component

* separate event filter into its own component

* try annotating badges on event card

* store collection

* Combine Store and Events components

* Make annotations look nicer

* Leaderboard rows

* start onboarding after checking in

* fix type error

* un-component EventList so the diff is cleaner

* move EventsPageProps back where it used to be

* Remove periods and exclamation marks from onboarding headings

* Reduce onboarding frequency (#266)

* Don't show onboarding when user has already attended events

* remove vim tab

* Add A.S. funded food field to event form (#269)

* Add A.S. funded food field to event form

* Make food items nullable

* Make food items nullable in apiRequests.ts as well

Mostly out of laIziness (idk if backend truly would enjoy receiving a null) but it's in line with Discord events

* Minor updates to onboarding (#267)

* add potential photos for onboarding first slide from justin

* increase leaderboard row height on mobile??

* fmt

* fmt

* use css var for positioning

* Arrange onboarding photos

* 3d effect for mouse users

* mobile collage layout

* improve leaderboard on mobile

* use backend support for whether onboarding seen

* refactor intro images

* [ImgBot] Optimize images (#271)

*Total -- 56,903.46kb -> 54,747.72kb (3.79%)

/public/assets/graphics/onboarding/Fall23Allocation-JustinLu.jpg -- 4,898.92kb -> 4,580.26kb (6.5%)
/public/assets/graphics/onboarding/Fall24Kickoff_1-JustinLu.jpg -- 9,357.62kb -> 8,933.71kb (4.53%)
/public/assets/graphics/onboarding/Fall24Bonfire_2-JustinLu.jpg -- 7,035.56kb -> 6,719.17kb (4.5%)
/public/assets/graphics/onboarding/Fall24BitByteInfo_1-JustinLu.jpg -- 7,678.81kb -> 7,348.67kb (4.3%)
/public/assets/graphics/onboarding/Fall24Bonfire_1-JustinLu.jpg -- 8,132.33kb -> 7,887.40kb (3.01%)
/public/assets/graphics/onboarding/Fall24Kickoff_3-JustinLu.jpg -- 7,072.95kb -> 6,867.79kb (2.9%)
/public/assets/graphics/onboarding/Fall24Kickoff_2-JustinLu.jpg -- 9,441.28kb -> 9,170.39kb (2.87%)
/public/assets/graphics/onboarding/Fall24BitByteInfo_2-JustinLu.jpg -- 3,285.99kb -> 3,240.33kb (1.39%)

Signed-off-by: ImgBotApp <[email protected]>
Co-authored-by: ImgBotApp <[email protected]>

* fixed errors

* fixed feedback

* pushed progress

* fixed

* cors error

* fixed errors

* fixed chrome bug

* fixed sean's feedback

---------

Signed-off-by: ImgBotApp <[email protected]>
Co-authored-by: Angela Hu <[email protected]>
Co-authored-by: Sean <[email protected]>
Co-authored-by: imgbot[bot] <31301654+imgbot[bot]@users.noreply.github.com>
Co-authored-by: ImgBotApp <[email protected]>
Co-authored-by: = <[email protected]>
  • Loading branch information
6 people authored Jan 31, 2025
1 parent 4dc9393 commit aca2546
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 52 deletions.
38 changes: 19 additions & 19 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"esbenp.prettier-vscode",
"rvest.vs-code-prettier-eslint",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"hex-ci.stylelint-plus",
"formulahendry.auto-rename-tag",
"formulahendry.auto-close-tag",
"vincaslt.highlight-matching-tag",
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
"burkeholland.simple-react-snippets"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
}
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"esbenp.prettier-vscode",
"rvest.vs-code-prettier-eslint",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"hex-ci.stylelint-plus",
"formulahendry.auto-rename-tag",
"formulahendry.auto-close-tag",
"vincaslt.highlight-matching-tag",
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
"burkeholland.simple-react-snippets"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
}
16 changes: 4 additions & 12 deletions cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"es5",
"dom"
],
"types": [
"cypress",
"node"
]
"lib": ["es5", "dom"],
"types": ["cypress", "node"]
},
"include": [
"**/*.ts"
]
}
"include": ["**/*.ts"]
}
34 changes: 17 additions & 17 deletions public/site.webmanifest
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
1 change: 1 addition & 0 deletions src/components/common/Cropper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const Cropper = ({
const handleImageError = useCallback(() => {
if (file !== null) {
showToast('This image format is not supported.');

onClose();
}
}, [file, onClose]);
Expand Down
4 changes: 0 additions & 4 deletions src/components/common/VerticalFormItem/style.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@
padding: 2px;
transition: 0.3s ease;
width: 100%;


}


}

.formError {
Expand Down
14 changes: 14 additions & 0 deletions src/lib/api/KlefkiAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,17 @@ export const generateACMURL = async (acmurlInfo: GenerateACMURLRequest): Promise
},
});
};

export const uploadBoardPhoto = async (file: File): Promise<KlefkiAPIResponse> => {
const { klefki } = config;
const formData = new FormData();
formData.append('file', file);
const requestUrl = `${klefki.baseUrl}${klefki.endpoints.board.photoUpload}`;
const response = await axios.post<KlefkiAPIResponse>(requestUrl, formData, {
headers: {
Authorization: `Bearer ${generateToken(klefki.key)}`,
},
});

return response.data;
};
4 changes: 4 additions & 0 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ const config = {
acmurl: {
generate: '/acmurl/generate',
},
board: {
photoUpload: '/board/uploadPhoto',
},
},
},
defaultEventImage: Cat,
Expand Down Expand Up @@ -110,6 +113,7 @@ const config = {
awardMilestone: '/admin/milestone',
viewResumes: '/admin/resumes',
manageUserAccess: '/admin/access',
updateProfile: '/admin/profile',
store: {
items: '/admin/store/items',
pickup: '/admin/store/pickup',
Expand Down
1 change: 1 addition & 0 deletions src/lib/types/apiResponses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ export interface DeleteResumeResponse extends ApiResponse {}
export interface KlefkiAPIResponse {
message: string;
error: string;
url?: string;
}

export interface NotionEventDetails {
Expand Down
1 change: 1 addition & 0 deletions src/pages/admin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const AdminPage = ({ user: { accessType }, preview }: AdminProps) => {
<>
<LinkButton href={config.admin.viewResumes}>View User Resumes</LinkButton>
<LinkButton href={config.admin.manageUserAccess}>Manage User Access</LinkButton>
<LinkButton href={config.admin.updateProfile}>Update Board Website Photo</LinkButton>
</>
) : (
'Restricted Access'
Expand Down
163 changes: 163 additions & 0 deletions src/pages/admin/profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { VerticalForm, VerticalFormButton, VerticalFormTitle, Cropper } from '@/components/common';
import { showToast, config } from '@/lib';
import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType';
import { PermissionService } from '@/lib/services';
import { PrivateProfile } from '@/lib/types/apiResponses';
import { reportError } from '@/lib/utils';
import { KlefkiAPI } from '@/lib/api';

import type { NextPage } from 'next';
import Image from 'next/image';
import { SubmitHandler, useForm } from 'react-hook-form';

import { ChangeEvent, useEffect, useState, useRef } from 'react';

interface UploadBoardPhotoProps {
user: PrivateProfile;
}
interface FormValues {
photo: File;
}
const UpdateProfilePage: NextPage<UploadBoardPhotoProps> = ({
user: { firstName, lastName },
}: UploadBoardPhotoProps) => {
const { handleSubmit } = useForm<FormValues>();
const [file, setFile] = useState<Blob | null>(null);
const [croppedImg, setCroppedImg] = useState<Blob | null>(null);
const [uploadedURL, setUploadedURL] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null); // Reference to the hidden file input
const [previewURL, setPreviewURL] = useState<string>('');

const handleUpload = (e: ChangeEvent<HTMLInputElement>) => {
const uploadedFile = e.target.files?.[0] || null;
e.currentTarget.value = '';
const maxFileSize = 5 * 1024 * 1024;

if (uploadedFile) {
if (!uploadedFile.type.startsWith('image/')) {
showToast('Unsupported file type. Please upload an image.');
return;
}
if (uploadedFile.size > maxFileSize) {
showToast('File size exceeds the 5MB limit. Please upload a smaller image.');
return;
}
setUploadedURL(null);
setFile(uploadedFile);
}
};

const handleCrop = async (croppedFile: Blob) => {
setCroppedImg(croppedFile);

setPreviewURL(URL.createObjectURL(croppedFile));
};

const triggerFileInput = () => {
if (fileInputRef.current) {
fileInputRef.current.click(); // Simulate a click on the hidden file input
}
};

const onSubmit: SubmitHandler<FormValues> = async () => {
if (!croppedImg) {
showToast('Please crop your image to submit!');
return;
}
try {
const croppedFile = new File(
[croppedImg],
`${firstName}_${lastName}.${croppedImg.type.split('/')[1]}`,
{
type: croppedImg.type,
}
);

const response = await KlefkiAPI.uploadBoardPhoto(croppedFile);
showToast('Photo uploaded successfully!');
if (response.url) {
setUploadedURL(response.url);
}
} catch (error) {
reportError('Error found!', error);
setUploadedURL(null);
}
};

useEffect(() => {
return () => {
if (previewURL) {
URL.revokeObjectURL(previewURL); // Clean up the blob URL
}
};
}, [previewURL]);

return (
<VerticalForm onEnterPress={handleSubmit(onSubmit)}>
<VerticalFormTitle
text="Upload Board Photo"
description="Upload your Board Photo on the ACM Website"
/>
{uploadedURL && (
<div>
<h1>Your Image URL is: </h1>
<a href={uploadedURL}>{uploadedURL}</a>
</div>
)}
{!uploadedURL && croppedImg && (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<Image src={previewURL} width={200} height={200} alt="preview photo" />
</div>
)}

<VerticalFormButton
type="button"
display="button2"
text="Upload New Photo"
onClick={triggerFileInput}
/>

<input
type="file"
accept="image/*"
ref={fileInputRef}
style={{ display: 'none' }}
onChange={handleUpload}
/>

<Cropper
file={file}
aspectRatio={1}
circle={false}
maxFileHeight={5000}
maxSize={5 * 1024 * 1024}
onCrop={handleCrop}
onClose={() => setFile(null)}
/>

{!uploadedURL && croppedImg && (
<VerticalFormButton
type="button"
display="button1"
text="Submit Photo"
onClick={handleSubmit(onSubmit)}
/>
)}
</VerticalForm>
);
};
export default UpdateProfilePage;

const getServerSidePropsFunc: GetServerSidePropsWithAuth = async () => {
return {
props: {
title: 'Upload Board Photo',
},
};
};

export const getServerSideProps = withAccessType(
getServerSidePropsFunc,
PermissionService.canViewAdminPage,
{ redirectTo: config.admin.homeRoute }
);

0 comments on commit aca2546

Please sign in to comment.