Skip to content

Commit 9404dc1

Browse files
committed
#3873 separate links, update guest home page image deafult, update pnm button, move things to guest view
1 parent 434093f commit 9404dc1

8 files changed

Lines changed: 234 additions & 184 deletions

File tree

src/frontend/src/pages/AdminToolsPage/EditGuestView/GuestViewConfig.tsx

Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,186 @@
1-
import { Stack, Grid } from '@mui/material';
1+
import { Box, FormControl, Grid, Stack, Typography } from '@mui/material';
2+
import { useState } from 'react';
3+
import { useForm } from 'react-hook-form';
4+
import { yupResolver } from '@hookform/resolvers/yup';
5+
import * as yup from 'yup';
26
import EditDescription from './EditDescription';
37
import EditFeaturedProjects from './EditFeaturedProjects';
48
import EditLogo from './EditLogo';
9+
import {
10+
useCurrentOrganization,
11+
useSetOrganizationPlatformLogoImage,
12+
useSetPlatformDescription
13+
} from '../../../hooks/organizations.hooks';
14+
import { useGetImageUrl } from '../../../hooks/onboarding.hook';
15+
import LoadingIndicator from '../../../components/LoadingIndicator';
16+
import NERUploadButton from '../../../components/NERUploadButton';
17+
import NERSuccessButton from '../../../components/NERSuccessButton';
18+
import ReactHookTextField from '../../../components/ReactHookTextField';
19+
import { useToast } from '../../../hooks/toasts.hooks';
20+
import { MAX_FILE_SIZE } from 'shared';
21+
import UsefulLinksTable from '../OnboardingConfig/UsefulLinks/UsefulLinksTable';
22+
import LinkTypeTable from '../ProjectsConfig/LinkTypes/LinkTypeTable';
23+
24+
const platformDescriptionSchema = yup.object().shape({
25+
platformDescription: yup.string().required()
26+
});
527

628
const GuestViewConfig: React.FC = () => {
29+
const { data: organization } = useCurrentOrganization();
30+
const { mutateAsync: setPlatformLogoImage, isLoading: platformLogoLoading } = useSetOrganizationPlatformLogoImage();
31+
const { mutateAsync: setPlatformDescriptionMutation, isLoading: platformDescriptionSaving } = useSetPlatformDescription();
32+
const { data: platformLogoImageUrl } = useGetImageUrl(organization?.platformLogoImageId ?? null);
33+
const toast = useToast();
34+
35+
const [addedPlatformLogo, setAddedPlatformLogo] = useState<File | undefined>(undefined);
36+
37+
const formKey = organization?.organizationId ?? 'loading';
38+
39+
const { control, handleSubmit, reset } = useForm<{ platformDescription: string }>({
40+
resolver: yupResolver(platformDescriptionSchema),
41+
defaultValues: { platformDescription: organization?.platformDescription ?? '' }
42+
});
43+
44+
const onPlatformDescriptionSubmit = async (data: { platformDescription: string }) => {
45+
try {
46+
const updated = await setPlatformDescriptionMutation(data.platformDescription);
47+
reset({ platformDescription: updated.platformDescription });
48+
toast.success('Platform description saved.');
49+
} catch (e) {
50+
toast.error(e instanceof Error ? e.message : 'Failed to save platform description');
51+
}
52+
};
53+
54+
const handlePlatformLogoUpload = async () => {
55+
if (!addedPlatformLogo) return;
56+
if (addedPlatformLogo.size >= MAX_FILE_SIZE) {
57+
toast.error(
58+
`Error uploading ${addedPlatformLogo.name}; file must be less than ${MAX_FILE_SIZE / 1024 / 1024} MB`,
59+
5000
60+
);
61+
return;
62+
}
63+
try {
64+
await setPlatformLogoImage(addedPlatformLogo);
65+
toast.success('Platform logo uploaded successfully.');
66+
setAddedPlatformLogo(undefined);
67+
} catch (e) {
68+
toast.error(e instanceof Error ? e.message : 'Failed to upload image');
69+
}
70+
};
71+
772
return (
873
<Grid container spacing={2}>
974
<Grid item xs={6}>
1075
<Stack spacing={2}>
1176
<EditDescription />
1277
<EditFeaturedProjects />
78+
<Box>
79+
<Typography variant="h5" gutterBottom borderBottom={1} color="#ef4345" borderColor="white">
80+
Platform Description
81+
</Typography>
82+
<form
83+
id="platform-description-form"
84+
key={formKey}
85+
onSubmit={(e) => {
86+
e.preventDefault();
87+
e.stopPropagation();
88+
handleSubmit(onPlatformDescriptionSubmit)(e);
89+
}}
90+
onKeyPress={(e) => {
91+
e.key === 'Enter' && e.preventDefault();
92+
}}
93+
>
94+
<FormControl sx={{ width: '100%' }}>
95+
<ReactHookTextField
96+
name="platformDescription"
97+
control={control}
98+
multiline
99+
rows={5}
100+
fullWidth
101+
required
102+
placeholder="Enter platform description for the guest home page..."
103+
sx={{ mb: 1 }}
104+
/>
105+
</FormControl>
106+
<Box sx={{ display: 'flex', justifyContent: 'end' }}>
107+
<NERSuccessButton
108+
type="submit"
109+
form="platform-description-form"
110+
variant="contained"
111+
disabled={platformDescriptionSaving}
112+
sx={{ mt: 1 }}
113+
>
114+
{platformDescriptionSaving ? 'Saving...' : 'Save'}
115+
</NERSuccessButton>
116+
</Box>
117+
</form>
118+
</Box>
13119
</Stack>
14120
</Grid>
15121
<Grid item xs={6}>
16-
<EditLogo />
122+
<Stack spacing={2}>
123+
<EditLogo />
124+
<Box>
125+
<Typography variant="h5" gutterBottom borderBottom={1} color="#ef4345" borderColor="white">
126+
Platform Logo
127+
</Typography>
128+
{platformLogoLoading ? (
129+
<Box sx={{ height: '200px', display: 'flex', alignItems: 'center' }}>
130+
<LoadingIndicator />
131+
</Box>
132+
) : (
133+
<Box>
134+
<NERUploadButton
135+
dataTypeId="platformLogo"
136+
handleFileChange={(e) => {
137+
if (e.target.files?.[0]) setAddedPlatformLogo(e.target.files[0]);
138+
}}
139+
onSubmit={handlePlatformLogoUpload}
140+
addedImage={addedPlatformLogo}
141+
setAddedImage={setAddedPlatformLogo}
142+
/>
143+
{!addedPlatformLogo && platformLogoImageUrl && (
144+
<Box
145+
component="img"
146+
src={platformLogoImageUrl}
147+
alt="Platform Logo"
148+
sx={{ display: 'block', maxWidth: '200px', mb: 1, mt: 1 }}
149+
/>
150+
)}
151+
</Box>
152+
)}
153+
</Box>
154+
</Stack>
155+
</Grid>
156+
<Grid item>
157+
<Typography variant="h5" gutterBottom borderBottom={1} color="#ef4345" borderColor={'white'}>
158+
Links Config
159+
</Typography>
160+
<LinkTypeTable isOnGuestHomePage={true} />
161+
</Grid>
162+
<Grid item>
163+
<Box
164+
sx={{
165+
backgroundColor: (theme) => theme.palette.background.paper,
166+
height: '100%',
167+
borderRadius: '10px',
168+
padding: '16px',
169+
width: '100%'
170+
}}
171+
>
172+
<Typography
173+
variant="h6"
174+
sx={{
175+
color: 'white',
176+
fontWeight: 'bold',
177+
marginBottom: '12px'
178+
}}
179+
>
180+
Guest Page Links
181+
</Typography>
182+
<UsefulLinksTable isOnGuestHomePage={true} />
183+
</Box>
17184
</Grid>
18185
</Grid>
19186
);

src/frontend/src/pages/AdminToolsPage/OnboardingConfig/UsefulLinks/UsefulLinksTable.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,26 @@ import EditUsefulLinkModal from './EditUsefulLinkModal';
2626
import { linkToLinkCreateArgs } from '../../../../utils/link.utils';
2727
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
2828

29-
const UsefulLinksTable = () => {
29+
interface UsefulLinksTableProps {
30+
isOnGuestHomePage?: boolean;
31+
}
32+
33+
const UsefulLinksTable = ({ isOnGuestHomePage }: UsefulLinksTableProps) => {
3034
const currentUser = useCurrentUser();
3135
const {
32-
data: usefulLinks,
36+
data: links,
3337
isLoading: usefulLinksIsLoading,
3438
isError: usefulLinksIsError,
3539
error: usefulLinksError
3640
} = useAllUsefulLinks();
3741
const { mutateAsync } = useSetUsefulLinks();
38-
const { data: linkTypes, isLoading: linkTypesIsLoading } = useAllLinkTypes();
42+
const { data: linkTypesBeforeFilter, isLoading: linkTypesIsLoading } = useAllLinkTypes();
3943

4044
const [linkToDelete, setLinkToDelete] = useState<Link>();
4145
const [editingLink, setEditingLink] = useState<Link>();
4246
const [showCreateModel, setShowCreateModel] = useState<boolean>(false);
4347

44-
if (!usefulLinks || usefulLinksIsLoading || !linkTypes || linkTypesIsLoading) return <LoadingIndicator />;
48+
if (!links || usefulLinksIsLoading || !linkTypesBeforeFilter || linkTypesIsLoading) return <LoadingIndicator />;
4549
if (usefulLinksIsError) return <ErrorPage message={usefulLinksError.message} />;
4650

4751
const handleDelete = (allLinks: Link[], linkToDelete: Link) => {
@@ -50,13 +54,24 @@ const UsefulLinksTable = () => {
5054
setLinkToDelete(undefined);
5155
};
5256

57+
const linkTypes = linkTypesBeforeFilter.filter((linkType) =>
58+
isOnGuestHomePage ? linkType.isOnGuestHomePage : !linkType.isOnGuestHomePage
59+
);
60+
61+
const usefulLinks = links.filter((link) =>
62+
isOnGuestHomePage ? link.linkType?.isOnGuestHomePage : !link.linkType?.isOnGuestHomePage
63+
);
64+
65+
console.log('Links: ', links);
66+
console.log('Links after filter: ', usefulLinks);
67+
console.log('isOnGuestHomePage:', isOnGuestHomePage);
5368
return (
5469
<Box>
5570
<CreateUsefulLinkModal
5671
open={showCreateModel}
5772
handleClose={() => setShowCreateModel(false)}
5873
linkTypes={linkTypes}
59-
currentLinks={usefulLinks}
74+
currentLinks={links}
6075
/>
6176
{editingLink && (
6277
<EditUsefulLinkModal
@@ -66,7 +81,7 @@ const UsefulLinksTable = () => {
6681
}}
6782
linkType={editingLink}
6883
linkTypes={linkTypes}
69-
currentLinks={usefulLinks}
84+
currentLinks={links}
7085
/>
7186
)}
7287

src/frontend/src/pages/AdminToolsPage/ProjectsConfig/LinkTypes/CreateLinkTypeModal.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,24 @@ interface CreateLinkTypeModalProps {
88
open: boolean;
99
handleClose: () => void;
1010
linkTypes: LinkType[];
11+
isOnGuestHomePage?: boolean;
1112
}
1213

13-
const CreateLinkTypeModal = ({ open, handleClose, linkTypes }: CreateLinkTypeModalProps) => {
14+
const CreateLinkTypeModal = ({ open, handleClose, linkTypes, isOnGuestHomePage }: CreateLinkTypeModalProps) => {
1415
const { isLoading, isError, error, mutateAsync } = useCreateLinkType();
1516

1617
if (isError) return <ErrorPage message={error?.message} />;
1718
if (isLoading) return <LoadingIndicator />;
1819

19-
return <LinkTypeFormModal open={open} handleClose={handleClose} onSubmit={mutateAsync} linkTypes={linkTypes} />;
20+
return (
21+
<LinkTypeFormModal
22+
open={open}
23+
handleClose={handleClose}
24+
onSubmit={mutateAsync}
25+
linkTypes={linkTypes}
26+
isOnGuestHomePage={isOnGuestHomePage}
27+
/>
28+
);
2029
};
2130

2231
export default CreateLinkTypeModal;

src/frontend/src/pages/AdminToolsPage/ProjectsConfig/LinkTypes/EditLinkTypeModal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const EditLinkTypeModal = ({ open, handleClose, linkType, linkTypes }: EditLinkT
2424
onSubmit={mutateAsync}
2525
defaultValues={linkType}
2626
linkTypes={linkTypes}
27+
isOnGuestHomePage={linkType.isOnGuestHomePage}
2728
/>
2829
);
2930
};

src/frontend/src/pages/AdminToolsPage/ProjectsConfig/LinkTypes/LinkTypeFormModal.tsx

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,17 @@ interface LinkTypeFormModalProps {
1616
defaultValues?: LinkType;
1717
onSubmit: (data: LinkTypeCreatePayload) => void;
1818
linkTypes: LinkType[];
19+
isOnGuestHomePage?: boolean;
1920
}
2021

21-
const LinkTypeFormModal = ({ open, handleClose, defaultValues, onSubmit, linkTypes }: LinkTypeFormModalProps) => {
22+
const LinkTypeFormModal = ({
23+
open,
24+
handleClose,
25+
defaultValues,
26+
onSubmit,
27+
linkTypes,
28+
isOnGuestHomePage
29+
}: LinkTypeFormModalProps) => {
2230
const toast = useToast();
2331
const creatingNew = defaultValues === undefined;
2432

@@ -49,7 +57,7 @@ const LinkTypeFormModal = ({ open, handleClose, defaultValues, onSubmit, linkTyp
4957
name: defaultValues?.name ?? '',
5058
iconName: defaultValues?.iconName ?? '',
5159
required: defaultValues?.required ?? false,
52-
isOnGuestHomePage: defaultValues?.required ?? false
60+
isOnGuestHomePage: isOnGuestHomePage ?? false
5361
}
5462
});
5563

@@ -101,19 +109,6 @@ const LinkTypeFormModal = ({ open, handleClose, defaultValues, onSubmit, linkTyp
101109
<FormHelperText error>{errors.required?.message}</FormHelperText>
102110
</FormControl>
103111
</Grid>
104-
<Grid item xs={6}>
105-
<FormControl fullWidth>
106-
<FormLabel sx={{ '&.Mui-focused': { color: theme.palette.text.secondary } }}>
107-
Appears on Guest Home Page
108-
</FormLabel>
109-
<Controller
110-
name="isOnGuestHomePage"
111-
control={control}
112-
render={({ field }) => <Switch {...field} checked={field.value} />}
113-
/>
114-
<FormHelperText error>{errors.required?.message}</FormHelperText>
115-
</FormControl>
116-
</Grid>
117112
<Grid item xs={6}>
118113
<FormControl fullWidth>
119114
<Box style={{ display: 'flex', verticalAlign: 'middle', alignItems: 'center' }}>

src/frontend/src/pages/AdminToolsPage/ProjectsConfig/LinkTypes/LinkTypeTable.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,22 @@ import NERTable from '../../../../components/NERTable';
1010
import { isAdmin, LinkType } from 'shared';
1111
import { useCurrentUser } from '../../../../hooks/users.hooks';
1212

13-
const LinkTypeTable = () => {
13+
interface LinkTypeTableProps {
14+
isOnGuestHomePage?: boolean;
15+
}
16+
17+
const LinkTypeTable = ({ isOnGuestHomePage }: LinkTypeTableProps) => {
1418
const currentUser = useCurrentUser();
15-
const {
16-
data: linkTypes,
17-
isLoading: linkTypeIsLoading,
18-
isError: linkTypeIsError,
19-
error: linkTypeError
20-
} = useAllLinkTypes();
19+
const { data: links, isLoading: linkTypeIsLoading, isError: linkTypeIsError, error: linkTypeError } = useAllLinkTypes();
2120
const [createModalShow, setCreateModalShow] = useState<boolean>(false);
2221
const [showEditModal, setShowEditModal] = useState<boolean>(false);
2322
const [clickedLinkType, setClickedLinkType] = useState<LinkType>();
2423

25-
if (!linkTypes || linkTypeIsLoading) return <LoadingIndicator />;
24+
if (!links || linkTypeIsLoading) return <LoadingIndicator />;
2625
if (linkTypeIsError) return <ErrorPage message={linkTypeError.message} />;
26+
const linkTypes = links.filter((linkType) =>
27+
isOnGuestHomePage ? linkType.isOnGuestHomePage : !linkType.isOnGuestHomePage
28+
);
2729

2830
const linkTypeTableRows = linkTypes.map((linkType, index) => (
2931
<TableRow
@@ -52,7 +54,12 @@ const LinkTypeTable = () => {
5254

5355
return (
5456
<Box>
55-
<CreateLinkTypeModal open={createModalShow} handleClose={() => setCreateModalShow(false)} linkTypes={linkTypes} />
57+
<CreateLinkTypeModal
58+
open={createModalShow}
59+
handleClose={() => setCreateModalShow(false)}
60+
linkTypes={linkTypes}
61+
isOnGuestHomePage={isOnGuestHomePage}
62+
/>
5663
{clickedLinkType && (
5764
<EditLinkTypeModal
5865
open={showEditModal}

0 commit comments

Comments
 (0)