diff --git a/CHANGELOG.MD b/CHANGELOG.MD
index f1003ffb5..fedf3eeac 100644
--- a/CHANGELOG.MD
+++ b/CHANGELOG.MD
@@ -1,3 +1,6 @@
+## August 15, 2024
+- **Feature** New engagement authoring view tab [🎟️ DESENG-674](https://citz-gdx.atlassian.net/browse/DESENG-674)
+
## August 13, 2024
- **Task** Fix dagster etl error in "dev" environment [🎟️ DESENG-677](https://citz-gdx.atlassian.net/browse/DESENG-677)
diff --git a/met-web/src/components/engagement/admin/view/AuthoringTab.tsx b/met-web/src/components/engagement/admin/view/AuthoringTab.tsx
new file mode 100644
index 000000000..8f81689fa
--- /dev/null
+++ b/met-web/src/components/engagement/admin/view/AuthoringTab.tsx
@@ -0,0 +1,208 @@
+import React, { useState, useEffect } from 'react';
+import { AuthoringValue, AuthoringButtonProps, StatusCircleProps } from './types';
+import { Header2 } from 'components/common/Typography';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faArrowRightLong } from '@fortawesome/pro-light-svg-icons';
+import { faCheck } from '@fortawesome/pro-solid-svg-icons';
+import { MetLabel, MetHeader3 } from 'components/common';
+import { SystemMessage } from 'components/common/Layout/SystemMessage';
+import { When } from 'react-if';
+import { Grid, Link } from '@mui/material';
+import { colors } from 'styles/Theme';
+
+const StatusCircle = (props: StatusCircleProps) => {
+ const statusCircleStyles = {
+ width: '6px',
+ height: '6px',
+ borderRadius: '50%',
+ backgroundColor: props.required ? colors.notification.danger.icon : colors.surface.gray[70],
+ marginLeft: '0.3rem',
+ marginTop: '-1rem',
+ };
+ return ;
+};
+
+const AuthoringButton = (props: AuthoringButtonProps) => {
+ const buttonStyles = {
+ display: 'flex',
+ width: '100%',
+ height: '3rem',
+ backgroundColor: props.item.required ? colors.surface.blue[10] : colors.surface.gray[10],
+ borderRadius: '8px',
+ border: 'none',
+ padding: '0 1rem 0 2.5rem',
+ margin: '0 0 0.5rem',
+ alignItems: 'center',
+ justifyContent: 'flex-start',
+ cursor: 'pointer',
+ };
+ const textStyles = {
+ fontSize: '1rem',
+ color: colors.type.regular.primary,
+ };
+ const arrowStyles = {
+ color: colors.surface.blue[90],
+ fontSize: '1.3rem',
+ marginLeft: 'auto',
+ };
+ const checkStyles = {
+ color: colors.type.regular.primary,
+ fontSize: '1rem',
+ fontWeight: 'bold',
+ paddingRight: '0.4rem',
+ };
+ return (
+
+
+
+ );
+};
+
+export const AuthoringTab = () => {
+ // Set default values
+ const mandatorySectionTitles = ['Hero Banner', 'Summary', 'Details', 'Provide Feedback'];
+ const optionalSectionTitles = ['View Results', 'Subscribe', 'More Engagements'];
+ const feedbackTitles = ['Survey', '3rd Party Feedback Method Link'];
+ const defaultAuthoringValue: AuthoringValue = {
+ id: 0,
+ title: '',
+ link: '#',
+ required: false,
+ completed: false,
+ };
+ const getAuthoringValues = (
+ defaultValues: AuthoringValue,
+ titles: string[],
+ required: boolean,
+ idOffset = 0,
+ ): AuthoringValue[] => {
+ return titles.map((title, index) => ({
+ ...defaultValues,
+ title: title,
+ required: required,
+ id: index + idOffset,
+ }));
+ };
+ const mandatorySectionValues = getAuthoringValues(defaultAuthoringValue, mandatorySectionTitles, true);
+ const optionalSectionValues = getAuthoringValues(defaultAuthoringValue, optionalSectionTitles, false, 100);
+ const defaultSectionValues = [...mandatorySectionValues, ...optionalSectionValues];
+ const defaultFeedbackMethods = getAuthoringValues(defaultAuthoringValue, feedbackTitles, true, 1000);
+
+ // Set useStates. When data is imported, it will be set with setSectionValues and setFeedbackMethods.
+ const [sectionValues] = useState(defaultSectionValues);
+ const [feedbackMethods] = useState(defaultFeedbackMethods);
+ const [requiredSectionsCompleted, setRequiredSectionsCompleted] = useState(false);
+ const [feedbackCompleted, setFeedbackCompleted] = useState(false);
+
+ // Define styles
+ const systemMessageStyles = {
+ marginBottom: '1.5rem',
+ };
+ const metHeaderStyles = {
+ marginBottom: '1.5rem',
+ fontSize: '1.2rem',
+ };
+ const metLabelStyles = {
+ textTransform: 'uppercase',
+ marginBottom: '1.1rem',
+ fontSize: '0.9rem',
+ };
+ const anchorContainerStyles = {
+ margin: '0 0 2.5rem 0',
+ padding: '0',
+ };
+
+ // Listen to the sectionValues useState and update the boolean value that controls the presence of the "sections" incomplete message.
+ useEffect(() => {
+ if (true !== requiredSectionsCompleted && allRequiredItemsComplete(sectionValues)) {
+ setRequiredSectionsCompleted(true);
+ } else if (false !== requiredSectionsCompleted) {
+ setRequiredSectionsCompleted(false);
+ }
+ }, [sectionValues]);
+
+ // Listen to the feedbackMethods useState and update the boolean value that controls the presence of the "feedback" incomplete message.
+ useEffect(() => {
+ if (true !== feedbackCompleted && allRequiredItemsComplete(feedbackMethods)) {
+ setFeedbackCompleted(true);
+ } else if (false !== feedbackCompleted) {
+ setRequiredSectionsCompleted(false);
+ }
+ }, [feedbackMethods]);
+
+ // Check if all required items are completed.
+ const allRequiredItemsComplete = (values: AuthoringValue[]) => {
+ const itemChecklist = values.map((value) => {
+ if (undefined === value.required || undefined === value.completed) {
+ return false;
+ }
+ if (true === value.required) {
+ return true === value.completed;
+ } else {
+ return true;
+ }
+ });
+ return !itemChecklist.includes(false);
+ };
+
+ return (
+
+ Authoring
+ Page Section Authoring
+
+
+ There are incomplete or missing sections of required content in your engagement. Please complete all
+ required content in all of the languages included in your engagement.
+
+
+
+
+ Required Sections
+ {sectionValues.map(
+ (section) => section.required && ,
+ )}
+
+
+ Optional Sections
+ {sectionValues.map(
+ (section) => !section.required && ,
+ )}
+
+
+
+ Feedback Configuration
+
+
+ There are feedback methods included in your engagement that are incomplete. Please complete
+ configuration for all of the feedback methods included in your engagement.
+
+
+ Feedback Methods
+
+ {feedbackMethods.map((method) => (
+
+ ))}
+
+
+
+ );
+};
diff --git a/met-web/src/components/engagement/admin/view/index.tsx b/met-web/src/components/engagement/admin/view/index.tsx
index c8cd3aac7..2ff3924e2 100644
--- a/met-web/src/components/engagement/admin/view/index.tsx
+++ b/met-web/src/components/engagement/admin/view/index.tsx
@@ -1,16 +1,26 @@
-import React, { Suspense } from 'react';
+import React, { Suspense, useState } from 'react';
import { useLoaderData, Await } from 'react-router-dom';
import { Engagement } from 'models/engagement';
import { AutoBreadcrumbs } from 'components/common/Navigation/Breadcrumb';
import { EngagementStatus } from 'constants/engagementStatus';
-import { Theme, useMediaQuery } from '@mui/material';
+import { Tab } from '@mui/material';
+import { ResponsiveContainer } from 'components/common/Layout';
+import { AuthoringTab } from './AuthoringTab';
+import { TabContext, TabList, TabPanel } from '@mui/lab';
export const AdminEngagementView = () => {
const { engagement } = useLoaderData() as { engagement: Promise };
- const isMediumScreenOrLarger: boolean = useMediaQuery((theme: Theme) => theme.breakpoints.up('md'));
+ const EngagementViewTabs = {
+ config: 'Configuration',
+ authoring: 'Authoring',
+ activity: 'Activity',
+ results: 'Results',
+ publishing: 'Publishing',
+ };
+ const [currentTab, setCurrentTab] = useState(EngagementViewTabs.config);
return (
-
+
@@ -32,6 +42,53 @@ export const AdminEngagementView = () => {
)}
-
+
+ setCurrentTab(newValue)}
+ aria-label="Admin Engagement View Tabs"
+ TabIndicatorProps={{ sx: { display: 'none' } }}
+ sx={{
+ '& .MuiTabs-flexContainer': {
+ justifyContent: 'flex-start',
+ width: 'max-content',
+ },
+ }}
+ >
+ {Object.entries(EngagementViewTabs).map(([key, value]) => (
+
+ ))}
+
+
+
+
+
+
+
+
);
};
diff --git a/met-web/src/components/engagement/admin/view/types.tsx b/met-web/src/components/engagement/admin/view/types.tsx
new file mode 100644
index 000000000..b779e373c
--- /dev/null
+++ b/met-web/src/components/engagement/admin/view/types.tsx
@@ -0,0 +1,15 @@
+export interface AuthoringValue {
+ id: number;
+ title: string;
+ link: string;
+ required: boolean;
+ completed: boolean;
+}
+
+export interface StatusCircleProps {
+ required: boolean;
+}
+
+export interface AuthoringButtonProps {
+ item: AuthoringValue;
+}
diff --git a/met-web/src/components/layout/Footer/index.tsx b/met-web/src/components/layout/Footer/index.tsx
index 9227b46b9..bc3b9f772 100644
--- a/met-web/src/components/layout/Footer/index.tsx
+++ b/met-web/src/components/layout/Footer/index.tsx
@@ -177,7 +177,7 @@ const Footer = () => {
{translate('footer.serviceCentres')}
-
+
diff --git a/met-web/src/components/layout/SideNav/SideNav.tsx b/met-web/src/components/layout/SideNav/SideNav.tsx
index 4078b2580..95eed2faf 100644
--- a/met-web/src/components/layout/SideNav/SideNav.tsx
+++ b/met-web/src/components/layout/SideNav/SideNav.tsx
@@ -60,14 +60,14 @@ const DrawerBox = ({ isMediumScreenOrLarger, setOpen }: DrawerBoxProps) => {
return !route.authenticated || route.allowedRoles.some((role) => permissions.includes(role));
});
- const renderListItem = (route: Route, itemType: string) => {
+ const renderListItem = (route: Route, itemType: string, key: number) => {
return (
<>
{
}}
>
- {allowedRoutes.map((route) =>
- renderListItem(route, currentBaseRoute === route.base ? 'selected' : 'other'),
+ {allowedRoutes.map((route, index) =>
+ renderListItem(route, currentBaseRoute === route.base ? 'selected' : 'other', index),
)}