Skip to content

Commit

Permalink
Feature/deseng668: Added authoring side nav and bottom nav, modified …
Browse files Browse the repository at this point in the history
…routing, added authoring context, added base page for banner (#2581)

* Router restructuring for engagement detail tabs

* Fixes to links between routes

* feature/deseng668: Integrated side nav, bottom nav, context, and base page for authoring section.

* feature/deseng668: Revised some routing code after merge, fixed conflicting action names, changed some data fetches.

* feature/deseng668: Revised routes and authoring components as per PR comments.

* feature/deseng668: Added authoring template and skeletons.

* feature/deseng668: Revised for accessibility, made some merge fixes.

* feature/deseng668: Added further screen reader support.

* feature/deseng668: Set image picker back to original design.

---------

Co-authored-by: NatSquared <[email protected]>
  • Loading branch information
jareth-whitney and NatSquared authored Sep 4, 2024
1 parent 69524c5 commit 9e5a4f8
Show file tree
Hide file tree
Showing 29 changed files with 2,620 additions and 83 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## September 3, 2024
- **Feature** New authoring content section [🎟️ DESENG-668](https://citz-gdx.atlassian.net/browse/DESENG-668)
- Implemented authoring side nav
- Implemented authoring bottom nav
- Implemented authoring section context
- Added skeletons for the required sections

## August 28, 2024

- **Bugfix** Fix keyboard focus being trapped on buttons on authoring page [🎟️ DESENG-687](https://citz-gdx.atlassian.net/browse/DESENG-687)
Expand Down
Binary file added met-web/src/assets/images/pagePreview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion met-web/src/components/common/RichTextEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ const RichTextEditor = ({
onEditorStateChange={handleChange}
handlePastedText={() => false}
editorStyle={{
height: '10em',
padding: '1em',
resize: 'vertical',
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const engagementUpdateAction: ActionFunction = async ({ request, params }
console.error('Error updating team members', e);
}

return redirect(`/engagements/${engagementId}/view`);
return redirect(`/engagements/${engagementId}/details/config`);
};

export default engagementUpdateAction;
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const ConfigForm = () => {
}),
{
method: 'patch',
action: `/engagements/${engagement.id}/config/`,
action: `/engagements/${engagement.id}/details/config/edit`,
},
);
};
Expand Down Expand Up @@ -131,7 +131,7 @@ const ConfigForm = () => {
>
<Grid container direction="row" item xs={12}>
<Grid xs={12}>
<Header2 sx={{ mb: 0 }}>We're just looking over your configuration.</Header2>
<Header2 sx={{ mb: 0 }}>We're saving your configuration.</Header2>
</Grid>
</Grid>
<Grid container direction="row" item xs={12}>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import React from 'react';
import { AppBar, Theme, ThemeProvider, Box, useMediaQuery, Select, MenuItem, SelectChangeEvent } from '@mui/material';
import { Palette, colors, DarkTheme, BaseTheme } from 'styles/Theme';
import { When, Unless } from 'react-if';
import { BodyText } from 'components/common/Typography';
import { elevations } from 'components/common';
import { Button } from 'components/common/Input';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCheck } from '@fortawesome/pro-regular-svg-icons';
import { StatusCircle } from '../../view/AuthoringTab';
import pagePreview from 'assets/images/pagePreview.png';
import { AuthoringBottomNavProps, LanguageSelectorProps } from './types';
import { getLanguageValue } from './AuthoringTemplate';

const AuthoringBottomNav = ({
isDirty,
isValid,
isSubmitting,
currentLanguage,
setCurrentLanguage,
languages,
pageTitle,
}: AuthoringBottomNavProps) => {
const isMediumScreenOrLarger = useMediaQuery((theme: Theme) => theme.breakpoints.up('md'));
const padding = { xs: '1rem 1rem', md: '1rem 1.5rem 1rem 2rem', lg: '1rem 3rem 1rem 2rem' };

const buttonStyles = {
height: '2.6rem',
borderRadius: '8px',
border: 'none',
padding: '0 1rem',
minWidth: '8.125rem',
fontSize: '0.9rem',
};

return (
<AppBar
component={'nav'}
position="fixed"
sx={{
backgroundColor: 'transparent',
borderTopRightRadius: '16px',
minHeight: '5rem',
backgroundClip: 'padding-box',
overflow: 'hidden',
top: 'auto',
left: 0,
bottom: 0,
boxShadow: elevations.default,
}}
data-testid="appbar-authoring-bottom-nav"
>
<Box
sx={{
background: colors.surface.blue[90],
minHeight: '5rem',
justifyContent: 'flex-start',
padding: padding,
display: 'flex',
alignItems: 'center',
flexWrap: isMediumScreenOrLarger ? 'nowrap' : 'wrap',
}}
>
<ThemeProvider theme={DarkTheme}>
<Box
sx={{
width: '18.8rem',
minWidth: '18.8rem',
marginBottom: isMediumScreenOrLarger ? '0' : '1rem',
}}
>
<BodyText bold>Currently Authoring</BodyText>
<BodyText sx={{ fontSize: '0.7rem', alignItems: 'center', marginTop: '-5px', display: 'flex' }}>
<span>{pageTitle}</span>
<span style={{ fontSize: '0.4rem', paddingLeft: '0.4rem', paddingRight: '0.4rem' }}>
{'\u2B24'}
</span>
{getLanguageValue(currentLanguage, languages)}
</BodyText>
</Box>
<Box
sx={{
width: '43.75rem',
justifyContent: 'flex-start',
display: 'flex',
}}
>
<ThemeProvider theme={BaseTheme}>
<LanguageSelector
currentLanguage={currentLanguage}
setCurrentLanguage={setCurrentLanguage}
languages={languages}
isDirty={isDirty}
isSubmitting={isSubmitting}
/>
</ThemeProvider>

<Button
disabled={!isValid || !isDirty || isSubmitting}
type="submit"
name="request_type"
value="update"
sx={{
...buttonStyles,
margin: '0 1.2rem',
}}
>
Save Section
</Button>
<Button
disabled={!isValid || !isDirty || isSubmitting}
type="submit"
name="request_type"
value="preview"
sx={{
...buttonStyles,
marginLeft: 'auto',
}}
>
<img
style={{
paddingRight: '0.3rem',
filter: !isValid || !isDirty || isSubmitting ? 'opacity(40%)' : 'opacity(100%)',
}}
src={pagePreview}
alt=""
aria-hidden="true"
/>
Preview
</Button>
</Box>
<Box style={{ width: '25rem', display: 'flex' }}></Box>
</ThemeProvider>
</Box>
</AppBar>
);
};

const LanguageSelector = ({
currentLanguage,
setCurrentLanguage,
languages,
isDirty,
isSubmitting,
}: LanguageSelectorProps) => {
const handleSelectChange = (event: SelectChangeEvent<string>) => {
const newLanguageCode = event.target.value;
if (isDirty && !isSubmitting)
// todo: Replace this message with our stylized modal message.
window.confirm(
`Are you sure you want to switch to ${
getLanguageValue(newLanguageCode, languages) || 'another language'
}? You have unsaved changes for the ${
getLanguageValue(currentLanguage, languages) || 'current'
} language.`,
);
setCurrentLanguage(newLanguageCode);
};
return (
<Select
value={currentLanguage}
onChange={handleSelectChange}
sx={{
height: '2.6rem',
borderRadius: '8px',
width: '9.375rem',
backgroundColor: colors.surface.gray[10],
border: 'none',
color: Palette.text.primary,
fontSize: '0.9rem',
cursor: 'pointer',
'& .MuiSelect-icon': {
color: Palette.text.primary,
},
}}
renderValue={(value) => {
const completed = false; // todo: Replace with real "completed" boolean value once it is available.
return (
<span>
<When condition={completed}>
<FontAwesomeIcon style={{ marginRight: '0.3rem' }} icon={faCheck} />
</When>
{languages.find((language) => language.code === value)?.name}
<Unless condition={completed}>
<StatusCircle required={true} />
</Unless>
</span>
);
}}
>
{languages.map((language) => {
const completed = false; // todo: Replace with the real "completed" boolean values once they are available.
return (
<MenuItem value={language.code} key={language.code}>
<When condition={completed}>
<FontAwesomeIcon style={{ marginRight: '0.3rem' }} icon={faCheck} />
</When>
{language.name}
<Unless condition={completed}>
<StatusCircle required={true} />
</Unless>
</MenuItem>
);
})}
</Select>
);
};

export default AuthoringBottomNav;
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React from 'react';
import dayjs, { Dayjs } from 'dayjs';
import { FormProvider, useForm } from 'react-hook-form';
import { createSearchParams, useFetcher, Outlet } from 'react-router-dom';

export interface EngagementUpdateData {
id: number;
status_id: number;
taxon_id: number;
content_id: number;
name: string;
start_date: Dayjs;
end_date: Dayjs;
description: string;
rich_description: string;
banner_filename: string;
status_block: string[];
title: string;
icon_name: string;
metadata_value: string;
send_report: boolean;
slug: string;
request_type: string;
}

export const AuthoringContext = () => {
const fetcher = useFetcher();
const locationArray = window.location.href.split('/');
const slug = locationArray[locationArray.length - 1];
const defaultDateValue = dayjs(new Date(1970, 0, 1));
const engagementUpdateForm = useForm<EngagementUpdateData>({
defaultValues: {
id: 0,
status_id: 0,
taxon_id: 0,
content_id: 0,
name: '',
start_date: defaultDateValue,
end_date: defaultDateValue,
description: '',
rich_description: '',
banner_filename: '',
status_block: [],
title: '',
icon_name: '',
metadata_value: '',
send_report: undefined,
slug: '',
request_type: '',
},
mode: 'onSubmit',
reValidateMode: 'onChange',
});
const onSubmit = async (data: EngagementUpdateData) => {
fetcher.submit(
createSearchParams({
id: 0 === data.id ? '' : data.id.toString(),
status_id: 0 === data.status_id ? '' : data.status_id.toString(),
taxon_id: 0 === data.taxon_id ? '' : data.taxon_id.toString(),
content_id: 0 === data.content_id ? '' : data.content_id.toString(),
name: data.name,
start_date:
'1970-01-01' === data.start_date.format('YYYY-MM-DD') ? '' : data.start_date.format('YYYY-MM-DD'),
end_date:
'1970-01-01' === data.start_date.format('YYYY-MM-DD') ? '' : data.end_date.format('YYYY-MM-DD'),
description: data.description,
rich_description: data.rich_description,
banner_filename: data.banner_filename,
status_block: data.status_block,
title: data.title,
icon_name: data.icon_name,
metadata_value: data.metadata_value,
send_report: getSendReportValue(data.send_report),
slug: data.slug,
request_type: data.request_type,
}),
{
method: 'post',
action: `/engagements/${data.id}/details/authoring/${slug}`,
},
);
};

const getSendReportValue = (valueToInterpret: boolean) => {
if (undefined === valueToInterpret) {
return '';
}
return valueToInterpret ? 'true' : 'false';
};

return (
<FormProvider {...engagementUpdateForm}>
<Outlet context={{ onSubmit }} />
</FormProvider>
);
};
Loading

0 comments on commit 9e5a4f8

Please sign in to comment.