Skip to content

Commit 6d35650

Browse files
committed
#4102 frontend add sponsor
1 parent 826f9b6 commit 6d35650

3 files changed

Lines changed: 80 additions & 8 deletions

File tree

src/frontend/src/pages/FinancePage/FinanceComponents/CreateSponsorPage.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useForm } from 'react-hook-form';
22
import LoadingIndicator from '../../../components/LoadingIndicator';
3-
import { SponsorPayload, useCreateSponsor } from '../../../hooks/finance.hooks';
3+
import { SponsorPayload, useCreateSponsor, useUploadSponsorLogo } from '../../../hooks/finance.hooks';
44
import sponsorSchema, { SponsorForm } from './SponsorForm';
55
import { yupResolver } from '@hookform/resolvers/yup';
66
import { Box } from '@mui/system';
@@ -18,6 +18,7 @@ interface CreateSponsorPageProps {
1818
const CreateSponsorPage = ({ showPage, handleClose }: CreateSponsorPageProps) => {
1919
const toast = useToast();
2020
const { isLoading, mutateAsync } = useCreateSponsor();
21+
const { mutateAsync: uploadLogo } = useUploadSponsorLogo();
2122

2223
const {
2324
handleSubmit,
@@ -48,12 +49,17 @@ const CreateSponsorPage = ({ showPage, handleClose }: CreateSponsorPageProps) =>
4849
});
4950

5051
const [submitError, setSubmitError] = useState<string | null>(null);
52+
const [logoImage, setLogoImage] = useState<File | null>(null);
53+
5154
if (isLoading) return <LoadingIndicator />;
5255

5356
const onFormSubmit = async (formData: SponsorPayload) => {
5457
try {
5558
setSubmitError(null);
56-
await mutateAsync({ ...formData });
59+
const sponsor = await mutateAsync(formData);
60+
if (logoImage) {
61+
await uploadLogo({ sponsorId: sponsor.sponsorId, logoImage });
62+
}
5763
toast.success('Sponsor created successfully!');
5864
handleClose();
5965
} catch (err: unknown) {
@@ -71,7 +77,7 @@ const CreateSponsorPage = ({ showPage, handleClose }: CreateSponsorPageProps) =>
7177
title="Add Sponsor"
7278
component={
7379
<Box display="flex" flexDirection="column" alignItems="flex-end">
74-
<SponsorForm control={control} errors={errors} setValue={setValue} />
80+
<SponsorForm control={control} errors={errors} setValue={setValue} onLogoImageChange={setLogoImage} />
7581
{submitError && (
7682
<Box color="error.main" mb={2} fontWeight="bold">
7783
{submitError}

src/frontend/src/pages/FinancePage/FinanceComponents/EditSponsorPage.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CreateSponsorTask, Sponsor } from 'shared';
22
import LoadingIndicator from '../../../components/LoadingIndicator';
3-
import { SponsorPayload, useEditSponsor } from '../../../hooks/finance.hooks';
3+
import { SponsorPayload, useEditSponsor, useUploadSponsorLogo } from '../../../hooks/finance.hooks';
44
import SidePage from './SidePagePopup';
55
import sponsorSchema, { SponsorForm } from './SponsorForm';
66

@@ -21,6 +21,7 @@ interface EditSponsorPageProps {
2121
const EditSponsorPage = ({ showPage, handleClose, sponsor }: EditSponsorPageProps) => {
2222
const toast = useToast();
2323
const { isLoading, mutateAsync } = useEditSponsor();
24+
const { mutateAsync: uploadLogo } = useUploadSponsorLogo();
2425

2526
const defaultSponsorTasks: CreateSponsorTask[] = (
2627
sponsor.sponsorTasks?.map((task) => ({
@@ -61,12 +62,17 @@ const EditSponsorPage = ({ showPage, handleClose, sponsor }: EditSponsorPageProp
6162
}
6263
});
6364
const [submitError, setSubmitError] = useState<string | null>(null);
65+
const [logoImage, setLogoImage] = useState<File | null>(null);
66+
6467
if (isLoading) return <LoadingIndicator />;
6568

6669
const onSubmit = async (formData: SponsorPayload) => {
6770
try {
6871
setSubmitError(null);
6972
await mutateAsync({ sponsorId: sponsor.sponsorId, ...formData });
73+
if (logoImage) {
74+
await uploadLogo({ sponsorId: sponsor.sponsorId, logoImage });
75+
}
7076
toast.success('Sponsor updated successfully!');
7177
handleClose();
7278
} catch (err: unknown) {
@@ -84,7 +90,7 @@ const EditSponsorPage = ({ showPage, handleClose, sponsor }: EditSponsorPageProp
8490
title="Edit Sponsor"
8591
component={
8692
<Box display="flex" flexDirection="column" alignItems="flex-end">
87-
<SponsorForm control={control} errors={errors} setValue={setValue} defaultValues={sponsor}></SponsorForm>
93+
<SponsorForm control={control} errors={errors} setValue={setValue} defaultValues={sponsor} onLogoImageChange={setLogoImage} />
8894
{submitError && (
8995
<Box color="error.main" mb={2} fontWeight="bold">
9096
{submitError}

src/frontend/src/pages/FinancePage/FinanceComponents/SponsorForm.tsx

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as yup from 'yup';
22
import { SponsorPayload, useGetAllSponsorTiers } from '../../../hooks/finance.hooks';
3+
import { useGetImageUrl } from '../../../hooks/onboarding.hook';
34
import ErrorPage from '../../ErrorPage';
45
import LoadingIndicator from '../../../components/LoadingIndicator';
56
import { Control, Controller, FieldErrors, FieldValues, UseFormSetValue, useFieldArray, useWatch } from 'react-hook-form';
@@ -14,13 +15,18 @@ import {
1415
Checkbox,
1516
Autocomplete,
1617
TextField,
17-
Chip
18+
Chip,
19+
Stack
1820
} from '@mui/material';
21+
import FileUploadIcon from '@mui/icons-material/FileUpload';
22+
import ImageIcon from '@mui/icons-material/Image';
1923
import ReactHookTextField from '../../../components/ReactHookTextField';
2024
import { DatePicker } from '@mui/x-date-pickers';
2125
import { useAllMembers } from '../../../hooks/users.hooks';
2226
import React, { useEffect, useRef, useState } from 'react';
2327
import { Box } from '@mui/system';
28+
import { useToast } from '../../../hooks/toasts.hooks';
29+
import { MAX_FILE_SIZE } from 'shared';
2430
import { AddCircle } from '@mui/icons-material';
2531
import AttachMoneyIcon from '@mui/icons-material/AttachMoney';
2632
import SponsorTaskCard from './SponsorTaskCard';
@@ -31,6 +37,7 @@ interface SponsorFormProps {
3137
errors: FieldErrors<SponsorPayload>;
3238
setValue: UseFormSetValue<SponsorPayload>;
3339
defaultValues?: Sponsor;
40+
onLogoImageChange?: (file: File | null) => void;
3441
}
3542

3643
const getYears = (startYear = 1950) => {
@@ -103,15 +110,25 @@ const sponsorSchema = yup.object().shape(
103110
done: yup.boolean().optional()
104111
})
105112
)
106-
.required('Sponsor Tasks are Required')
113+
.required('Sponsor Tasks are Required'),
114+
logoImageId: yup.string().trim().optional()
107115
},
108116
[['contactEmail', 'contactPhone']]
109117
);
110118

111-
export const SponsorForm: React.FC<SponsorFormProps> = ({ control, errors, setValue, defaultValues }: SponsorFormProps) => {
119+
export const SponsorForm: React.FC<SponsorFormProps> = ({
120+
control,
121+
errors,
122+
setValue,
123+
defaultValues,
124+
onLogoImageChange
125+
}: SponsorFormProps) => {
112126
const yearsOptions = getYears();
127+
const toast = useToast();
113128

129+
const [logoImage, setLogoImage] = useState<File | null>(null);
114130
const [datePickerOpenJoin, setDatePickerOpenJoin] = useState(false);
131+
const { data: currentLogoUrl } = useGetImageUrl(defaultValues?.logoImageId ?? null);
115132

116133
const { isLoading: membersLoading, isError: membersIsError, error: membersError, data: members } = useAllMembers();
117134

@@ -444,6 +461,49 @@ export const SponsorForm: React.FC<SponsorFormProps> = ({ control, errors, setVa
444461
<FormHelperText error> {errors.discountCode?.message}</FormHelperText>
445462
</FormControl>
446463
</Grid>
464+
<Grid item xs={12} sm={6}>
465+
<FormControl fullWidth>
466+
<Typography variant="h5" color="#EF4345">
467+
Sponsor Logo:
468+
</Typography>
469+
<Box sx={{ display: 'flex', flexDirection: 'row', gap: 1, alignItems: 'center', mt: 1 }}>
470+
<Button
471+
variant="contained"
472+
color="error"
473+
component="label"
474+
startIcon={<FileUploadIcon />}
475+
sx={{ width: 'fit-content', textTransform: 'none', color: 'black' }}
476+
>
477+
Upload Logo
478+
<input
479+
onChange={(e) => {
480+
if (e.target.files && e.target.files[0]) {
481+
const [file] = e.target.files;
482+
if (file.size > MAX_FILE_SIZE) {
483+
toast.error(`File "${file.name}" exceeds the maximum size limit of ${MAX_FILE_SIZE / 1024 / 1024} MB`);
484+
return;
485+
}
486+
setLogoImage(file);
487+
onLogoImageChange?.(file);
488+
}
489+
}}
490+
type="file"
491+
accept="image/png, image/jpeg"
492+
hidden
493+
/>
494+
</Button>
495+
{logoImage && (
496+
<Stack direction="row" spacing={1} alignItems="center">
497+
<ImageIcon />
498+
<Typography>{logoImage.name}</Typography>
499+
</Stack>
500+
)}
501+
</Box>
502+
{!logoImage && currentLogoUrl && (
503+
<Box component="img" src={currentLogoUrl} alt="Sponsor Logo" sx={{ maxWidth: '200px', mt: 1 }} />
504+
)}
505+
</FormControl>
506+
</Grid>
447507
<Grid item xs={12}>
448508
<FormControl fullWidth>
449509
<Typography variant="h5" color="#EF4345">

0 commit comments

Comments
 (0)