-
Notifications
You must be signed in to change notification settings - Fork 7
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
#3060-Edit Organization Logo #3065
Changes from 11 commits
db222ba
32e23a4
907e54d
f2e10f9
e59e07b
eec9ffd
ed36d21
3ffe659
b466c25
61b6cf8
0e2fed5
8477e87
53d5e72
55f17f4
2722e7d
b9206bd
06df950
a1d8637
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should not be here |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,12 @@ import { | |
getFeaturedProjects, | ||
getCurrentOrganization, | ||
setOrganizationDescription, | ||
getOrganizationLogo, | ||
setOrganizationLogo, | ||
setOrganizationFeaturedProjects | ||
} from '../apis/organizations.api'; | ||
import { downloadGoogleImage } from '../apis/finance.api'; | ||
import { getDefaultImageData } from '../utils/image.utils'; | ||
|
||
interface OrganizationProvider { | ||
organizationId: string; | ||
|
@@ -84,3 +88,24 @@ export const useSetFeaturedProjects = () => { | |
} | ||
); | ||
}; | ||
|
||
export const useSetOrganizationLogo = () => { | ||
const queryClient = useQueryClient(); | ||
return useMutation<Organization, Error, File>(['reimbursement-requsts', 'edit'], async (file: File) => { | ||
const { data } = await setOrganizationLogo(file); | ||
queryClient.invalidateQueries(['organizations']); | ||
return data; | ||
}); | ||
}; | ||
|
||
export const useOrganizationLogo = () => { | ||
return useQuery<Blob, Error>(['organizations', 'logo'], async () => { | ||
try { | ||
const { data: fileId } = await getOrganizationLogo(); | ||
return await downloadGoogleImage(fileId); | ||
} catch { | ||
// return default logo if fileId was not found | ||
return await getDefaultImageData(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no lets not do this. return undefined if it. doesnt exist |
||
} | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import React, { useState } from 'react'; | ||
import { useCurrentOrganization, useOrganizationLogo, useSetOrganizationLogo } from '../../../hooks/organizations.hooks'; | ||
import LoadingIndicator from '../../../components/LoadingIndicator'; | ||
import EditLogoForm, { EditLogoInput } from './EditLogoForm'; | ||
import { useToast } from '../../../hooks/toasts.hooks'; | ||
import { Box, Card, Typography, useTheme } from '@mui/material'; | ||
import LogoDisplay from '../../HomePage/components/LogoDisplay'; | ||
import { NERButton } from '../../../components/NERButton'; | ||
|
||
const EditLogo = () => { | ||
const { data: organization, isLoading: organizationIsLoading } = useCurrentOrganization(); | ||
const { data: imageData, isLoading: imageDataIsLoading } = useOrganizationLogo(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add error handling for these queries. Ensure all useQueries added have proper error handling. |
||
const { mutateAsync, isLoading } = useSetOrganizationLogo(); | ||
const toast = useToast(); | ||
const [isEditMode, setIsEditMode] = useState(false); | ||
const theme = useTheme(); | ||
|
||
if (isLoading || !mutateAsync || organizationIsLoading || !organization || !imageData || imageDataIsLoading) | ||
return <LoadingIndicator />; | ||
|
||
const handleClose = () => { | ||
setIsEditMode(false); | ||
}; | ||
|
||
const onSubmit = async (logoInput: EditLogoInput) => { | ||
try { | ||
console.log(logoInput); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove console |
||
if (!logoInput.logoImage) { | ||
handleClose(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should probably toast why were closing |
||
return; | ||
} | ||
await mutateAsync(logoInput.logoImage); | ||
toast.success('Logo updated successfully!'); | ||
} catch (e) { | ||
if (e instanceof Error) { | ||
toast.error(e.message); | ||
} | ||
} | ||
handleClose(); | ||
}; | ||
|
||
return ( | ||
<Card | ||
sx={{ | ||
width: '100%', | ||
background: 'transparent', | ||
padding: 2, | ||
...(isEditMode && { | ||
background: theme.palette.background.paper, | ||
padding: 1.9, | ||
variant: 'outlined' | ||
}) | ||
}} | ||
variant={isEditMode ? 'outlined' : undefined} | ||
> | ||
<Typography variant="h4" mb={1}> | ||
{organization.name} Logo | ||
</Typography> | ||
{isEditMode ? ( | ||
<EditLogoForm | ||
onSubmit={onSubmit} | ||
onHide={handleClose} | ||
orgLogo={new File([imageData], imageData.name, { type: imageData.type })} | ||
/> | ||
) : ( | ||
<> | ||
<Box sx={{ display: 'flex', flexDirection: 'column', height: 350, width: 300 }}> | ||
<LogoDisplay imageUrl={URL.createObjectURL(imageData)} /> | ||
<Box | ||
sx={{ | ||
display: 'flex', | ||
justifyContent: 'end' | ||
}} | ||
> | ||
<NERButton variant="contained" sx={{ my: 2 }} onClick={() => setIsEditMode(true)}> | ||
Update | ||
</NERButton> | ||
</Box> | ||
</Box> | ||
</> | ||
)} | ||
</Card> | ||
); | ||
}; | ||
|
||
export default EditLogo; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import React from 'react'; | ||
import { Box, Button, FormControl, Stack, Typography } from '@mui/material'; | ||
import FileUploadIcon from '@mui/icons-material/FileUpload'; | ||
import { Controller, useForm } from 'react-hook-form'; | ||
import NERFailButton from '../../../components/NERFailButton'; | ||
import NERSuccessButton from '../../../components/NERSuccessButton'; | ||
import ImageIcon from '@mui/icons-material/Image'; | ||
|
||
export interface EditLogoInput { | ||
logoImage: File; | ||
} | ||
|
||
interface EditLogoFormProps { | ||
onSubmit: (logoImage: EditLogoInput) => Promise<void>; | ||
onHide: () => void; | ||
orgLogo: File; | ||
} | ||
|
||
const EditLogoForm: React.FC<EditLogoFormProps> = ({ onSubmit, orgLogo, onHide }) => { | ||
const { handleSubmit, control, reset } = useForm({ | ||
defaultValues: { | ||
logoImage: orgLogo | ||
} | ||
}); | ||
|
||
const onSubmitWrapper = async (data: EditLogoInput) => { | ||
await onSubmit(data); | ||
reset(); | ||
}; | ||
|
||
const onHideWrapper = () => { | ||
onHide(); | ||
reset(); | ||
}; | ||
|
||
return ( | ||
<form | ||
id="edit-organization-logo" | ||
onSubmit={(e) => { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
handleSubmit(onSubmit)(e); | ||
reset(); | ||
}} | ||
onKeyPress={(e) => { | ||
e.key === 'Enter' && e.preventDefault(); | ||
}} | ||
> | ||
<Stack spacing={2}> | ||
<FormControl sx={{ width: '100%' }}> | ||
<Controller | ||
name={'logoImage'} | ||
control={control} | ||
rules={{ required: true }} | ||
render={({ field: { onChange, value } }) => ( | ||
<Box | ||
sx={{ | ||
display: 'flex', | ||
flexDirection: 'row', | ||
gap: 1 | ||
}} | ||
> | ||
<Button | ||
variant="contained" | ||
color="error" | ||
component="label" | ||
startIcon={<FileUploadIcon />} | ||
sx={{ | ||
width: 'fit-content', | ||
textTransform: 'none', | ||
mt: '9.75px', | ||
color: 'black' | ||
}} | ||
> | ||
Upload | ||
<input | ||
onChange={(e) => { | ||
onChange(!!e.target.files && e.target.files[0]); | ||
}} | ||
type="file" | ||
id="logo-image" | ||
accept="image/png, image/jpeg" | ||
name="logoImageFile" | ||
multiple | ||
hidden | ||
/> | ||
</Button> | ||
{value.name !== 'undefined' && ( | ||
<Stack direction={'row'} spacing={1} mt={2}> | ||
<ImageIcon /> | ||
<Typography>{value.name}</Typography> | ||
</Stack> | ||
)} | ||
</Box> | ||
)} | ||
/> | ||
</FormControl> | ||
<Box | ||
sx={{ | ||
display: 'flex', | ||
justifyContent: 'end' | ||
}} | ||
> | ||
<NERFailButton sx={{ mx: 1 }} form={'edit-organization-logo'} onClick={onHideWrapper}> | ||
Cancel | ||
</NERFailButton> | ||
<NERSuccessButton | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is weird. were gonna be submitting twice. Wee should remove this onclick prop and delete the onSubmitWrapper. |
||
sx={{ mx: 1 }} | ||
type="submit" | ||
form={'edit-organization-logo'} | ||
onClick={handleSubmit(onSubmitWrapper)} | ||
> | ||
Save | ||
</NERSuccessButton> | ||
</Box> | ||
</Stack> | ||
</form> | ||
); | ||
}; | ||
|
||
export default EditLogoForm; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Box, useTheme, Card } from '@mui/material'; | ||
import React from 'react'; | ||
|
||
interface LogoDisplayProps { | ||
imageUrl: string; | ||
} | ||
|
||
const LogoDisplay: React.FC<LogoDisplayProps> = ({ imageUrl }) => { | ||
const theme = useTheme(); | ||
return ( | ||
<Card | ||
variant={'outlined'} | ||
sx={{ | ||
background: theme.palette.background.paper, | ||
height: '100%', | ||
width: '100%', | ||
borderRadius: 2 | ||
}} | ||
> | ||
<Box | ||
component="img" | ||
src={imageUrl} | ||
sx={{ | ||
height: '100%', | ||
width: '100%', | ||
borderRadius: 2 | ||
}} | ||
/> | ||
</Card> | ||
); | ||
}; | ||
|
||
export default LogoDisplay; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ensure at some point you consolidate all of your migration files to just one migration called home-page-redesign