Skip to content
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

DESENG-694: Update timeline widget design #2596

Merged
merged 2 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
## September 26, 2024

- **Feature** New Timeline Widget designs [🎟️ DESENG-694](https://citz-gdx.atlassian.net/browse/DESENG-694)
- Implemented Figma design
- Adjusted styling in Engagement views slightly to accommodate theme-aware widgets

## September 25, 2024

- **Feature** New Video Widget front end [🎟️ DESENG-692](https://citz-gdx.atlassian.net/browse/DESENG-692)
- Removed unneeded tables from db
- Updated all other met_api and met_web logic to accomodate this

- Removed unneeded tables from db
- Updated all other met_api and met_web logic to accomodate this

- **Feature** New Map Widget front end [🎟️ DESENG-693](https://citz-gdx.atlassian.net/browse/DESENG-693)
- Implemented Figma design
- Fixed accessibility issue with map labels (white text on white background)
- Implemented Figma design
- Fixed accessibility issue with map labels (white text on white background)

## September 23, 2024

Expand All @@ -19,6 +26,7 @@
## September 18, 2024

- **Feature** New Video Widget front end [🎟️ DESENG-692](https://citz-gdx.atlassian.net/browse/DESENG-692)

- Implemented Figma design
- Created custom layover bar for videos that shows video provider and has a custom logo
- Transcripts will not be implemented at this time
Expand Down
2 changes: 1 addition & 1 deletion met-web/src/components/common/Input/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const CommonSelect: React.FC<CommonSelectProps & Omit<MuiSelectProps, 'va
p: 1,
height: 48,
borderRadius: '2em',
background: bgColor,
backgroundColor: bgColor,
color: textColors.primary,
boxShadow: 3,
'&:hover': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
border: 1px solid #605E5C;
}

.rdw-editor-wrapper:has(.public-DraftEditor-content[contenteditable="false"]){
line-height: 1.75;
}

.rdw-editor-toolbar{
padding: 2px;
border: none;
Expand Down
2 changes: 1 addition & 1 deletion met-web/src/components/common/Typography/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { Header1, Header2 } from './Headers';
export { Header1, Header2, Header3 } from './Headers';
export { BodyText, EyebrowText } from './Body';
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const engagementCreateAction: ActionFunction = async ({ request }) => {
formData.getAll('users').forEach((user_id) => {
addTeamMemberToEngagement({ user_id: user_id.toString(), engagement_id: engagement.id });
});
return redirect(`/engagements/${engagement.id}/view`);
return redirect(`/engagements/${engagement.id}/details/config`);
};

export default engagementCreateAction;
Original file line number Diff line number Diff line change
@@ -1,190 +1,120 @@
import React, { useEffect, useState } from 'react';
import { MetPaper } from 'components/common';
import { Avatar, Grid, Skeleton, Divider } from '@mui/material';
import React, { Suspense } from 'react';
import { Grid, Skeleton, Paper, ThemeProvider, Box } from '@mui/material';
import { Widget } from 'models/widget';
import { useAppDispatch } from 'hooks';
import { openNotification } from 'services/notificationService/notificationSlice';
import { TimelineWidget, TimelineEvent } from 'models/timelineWidget';
import { TimelineWidget, TimelineEvent as TimelineEventType, EventStatus } from 'models/timelineWidget';
import { fetchTimelineWidgets } from 'services/widgetService/TimelineService';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCircleCheck } from '@fortawesome/pro-solid-svg-icons/faCircleCheck';
import { faCircleHalf } from '@fortawesome/pro-solid-svg-icons/faCircleHalf';
import { Palette } from 'styles/Theme';
import { EventStatus } from 'models/timelineWidget';
import { BodyText, Header2 } from 'components/common/Typography';

import { BaseTheme } from 'styles/Theme';
import { BodyText, Header3 } from 'components/common/Typography';
import { Await } from 'react-router-dom';
import { faCircle, faCircleHalf } from '@fortawesome/pro-solid-svg-icons';
interface TimelineWidgetProps {
widget: Widget;
}

const TimelineWidgetView = ({ widget }: TimelineWidgetProps) => {
const dispatch = useAppDispatch();
const [timelineWidget, setTimelineWidget] = useState<TimelineWidget>({
id: 0,
widget_id: 0,
engagement_id: 0,
title: '',
description: '',
events: [],
});
const [isLoading, setIsLoading] = useState(true);

const fetchTimeline = async () => {
try {
const timelines = await fetchTimelineWidgets(widget.id);
const timeline = timelines[timelines.length - 1];
timeline.events.sort((a, b) => a.position - b.position);
setTimelineWidget(timeline);
setIsLoading(false);
} catch (error) {
setIsLoading(false);
console.log(error);
dispatch(
openNotification({
severity: 'error',
text: 'Error occurred while fetching Engagement widgets information',
}),
);
}
};

useEffect(() => {
fetchTimeline();
}, [widget]);

const containerStylesObject = (index: number) => ({
minHeight: index + 1 === timelineWidget.events.length ? '60px' : '80px',
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-start',
marginLeft: index + 1 === timelineWidget.events.length ? '18px' : '16px',
borderLeft: index + 1 === timelineWidget.events.length ? 'none' : '2px solid grey',
});
const timeline = fetchTimelineWidgets(widget.id);

const commonAvatarStyles = {
height: 30,
width: 30,
marginLeft: '-16px',
backgroundColor: Palette.info.main,
};

const commonWhiteAvatarStyles = {
height: 23,
width: 23,
backgroundColor: 'var(--bcds-surface-background-white)',
color: 'transparent',
};

const renderIcon = (status: EventStatus) => {
const icons: { [key in EventStatus]: JSX.Element } = {
[EventStatus.Pending]: (
<Avatar sx={commonAvatarStyles}>
<Avatar sx={commonWhiteAvatarStyles} />
</Avatar>
),
[EventStatus.InProgress]: (
<Avatar sx={commonAvatarStyles}>
<Avatar sx={commonWhiteAvatarStyles}>
<FontAwesomeIcon
icon={faCircleHalf}
rotation={90}
style={{
fontSize: '20px',
color: Palette.action.active,
stroke: Palette.action.active,
strokeWidth: 3,
}}
/>
</Avatar>
</Avatar>
),
[EventStatus.Completed]: (
<Avatar sx={commonAvatarStyles}>
<Avatar sx={commonWhiteAvatarStyles}>
<FontAwesomeIcon
icon={faCircleCheck}
style={{
fontSize: '20px',
color: Palette.action.active,
stroke: Palette.action.active,
strokeWidth: 3,
}}
/>
</Avatar>
</Avatar>
),
};
return (
<Suspense fallback={<Skeleton variant="rectangular" height={200} />}>
<Await resolve={timeline}>
{(timelineWidgets: TimelineWidget[]) => {
const timelineWidget = timelineWidgets[0];
return (
<Grid container gap="1rem">
<Grid item xs={12} mt="4rem">
<Header3 sx={{ fontSize: '1.375rem' }} weight="thin">
{timelineWidget.title}
</Header3>
</Grid>
<Grid item xs={12}>
<BodyText>{timelineWidget.description}</BodyText>
</Grid>
<Grid
item
xs={12}
component={Paper}
sx={{
mt: '1.5rem',
bgcolor: 'white',
padding: '2em',
borderRadius: '16px',
border: '1px solid',
borderColor: 'blue.90',
height: 'fit-content',
}}
>
<ThemeProvider theme={BaseTheme}>
{timelineWidget.events.map((event, index) => (
<Grid container item xs={12} key={event.id} direction="row">
<TimelineEvent
event={event}
isLast={index === timelineWidget.events.length - 1}
/>
</Grid>
))}
</ThemeProvider>
</Grid>
</Grid>
);
}}
</Await>
</Suspense>
);
};

return icons[status] || null;
};
export default TimelineWidgetView;

const handleRenderTimelineEvent = (tEvent: TimelineEvent, index: number) => {
return (
<Grid container item xs={12} sx={containerStylesObject(index)} key={'event' + (index + 1)}>
<Grid item fontWeight="bold" xs={0.5}>
{renderIcon(tEvent.status)}
</Grid>
<Grid item xs={11} sx={{ paddingLeft: '10px' }}>
<BodyText size="large" thin>
{tEvent.description}
</BodyText>
<BodyText
style={{
paddingBottom: index + 1 === timelineWidget.events.length ? '0' : '20px',
const TimelineEvent = ({ event, isLast }: { event: TimelineEventType; isLast: boolean }) => {
return (
<Grid container direction="row" gap={2} alignItems="stretch" justifyContent="flex-start">
{/* Left side with icon and line */}
<Grid item container alignItems="stretch" direction="column" sx={{ width: '3rem' }}>
{/* Event Icon */}
<Grid item>
<Paper
sx={{
height: '1.5em',
width: '1.5em',
borderRadius: '50%',
border: '1px solid',
borderColor: 'blue.90',
fontSize: '32px',
padding: '8px',
alignItems: 'center',
justifyContent: 'center',
display: 'flex',
color: 'blue.90',
}}
>
{tEvent.time}
</BodyText>
{event.status === EventStatus.Completed && <FontAwesomeIcon icon={faCircle} />}
{event.status === EventStatus.InProgress && (
<FontAwesomeIcon rotation={270} icon={faCircleHalf} />
)}
</Paper>
</Grid>
</Grid>
);
};

if (isLoading) {
return (
<MetPaper elevation={1} sx={{ padding: '1em' }}>
<Grid container justifyContent="flex-start" spacing={3}>
<Grid item xs={12}>
<Header2>
<Skeleton variant="rectangular" />
</Header2>
</Grid>
<Grid item xs={12}>
<Skeleton variant="rectangular" height="20em" />
{/* Dividing line */}
{!isLast && (
<Grid item xs alignItems="center" direction="column">
<Box
sx={{
height: 'calc(100% + 2em)',
width: 'calc(50% + 2px)',
borderRight: '4px solid',
borderColor: 'blue.90',
}}
/>
</Grid>
</Grid>
</MetPaper>
);
}

if (!timelineWidget) {
return null;
}
)}
</Grid>

return (
<MetPaper elevation={1} sx={{ paddingTop: '0.5em', padding: '1em', width: '100%' }}>
<Grid container justifyContent={{ xs: 'center' }} alignItems="center" rowSpacing={2}>
<Grid
item
container
justifyContent={{ xs: 'center', md: 'flex-start' }}
flexDirection={'column'}
xs={12}
paddingBottom={0}
>
<Header2>{timelineWidget.title}</Header2>
<Divider sx={{ borderWidth: 1 }} />
</Grid>
<Grid item xs={12}>
<BodyText>{timelineWidget.description}</BodyText>
</Grid>
<Grid item xs={12}>
{timelineWidget &&
timelineWidget.events.map((tEvent, index) => handleRenderTimelineEvent(tEvent, index))}
</Grid>
{/* Right side with event content */}
<Grid item xs pb={isLast ? '0' : '2em'} minHeight={isLast ? '0' : '6em'}>
<BodyText size="large" bold>
{event.description} ({['Pending', 'In Progress', 'Completed'][event.status - 1]})
</BodyText>
<BodyText>{event.time}</BodyText>
</Grid>
</MetPaper>
</Grid>
);
};

export default TimelineWidgetView;
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const EngagementDescription = () => {
<Await resolve={engagement}>
{(engagement: Engagement) => (
<>
<Header2 decorated id="description-header">
<Header2 decorated id="description-header" sx={{ mb: 1 }}>
{engagement.description_title}
</Header2>
<BodyText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,15 @@ export const EngagementSurveyBlock = () => {
// No point in rendering if there is no status block or 2nd widget
if (!statusBlock?.block_text && !widget) return null;
return (
<Grid container justifyContent="space-between" sx={gridContainerStyles}>
<Grid
component="section"
id={EngagementViewSections.PROVIDE_FEEDBACK}
container
justifyContent="space-between"
sx={gridContainerStyles}
aria-label="Survey Section"
>
<Grid
component={'section'}
id={EngagementViewSections.PROVIDE_FEEDBACK}
aria-label="Survey Section"
item
sx={{
width: { xs: '100%', md: '47.5%' },
Expand Down Expand Up @@ -166,9 +170,7 @@ export const EngagementSurveyBlock = () => {
marginBottom: '48px',
}}
>
<ThemeProvider theme={BaseTheme}>
{widget && <WidgetSwitch widget={widget} />}
</ThemeProvider>
{widget && <WidgetSwitch widget={widget} />}
</Grid>
</Grid>
);
Expand Down
Loading
Loading