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

[To Main] Feature - DESENG-667: Add engagement configuration summary #2577

Merged
merged 9 commits into from
Aug 19, 2024
10 changes: 10 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
## August 15, 2024

- **Feature** Add engagement configuration summary [🎟️ DESENG-667](https://citz-gdx.atlassian.net/browse/DESENG-667)

- Added a tabbed layout to the new engagement view page
- Added a new "Configuration" tab to display the engagement's configuration details
- The other tabs are blank for now, but will be filled in future tickets
- The configuration tab allows navigating back to the engagement configuration page
- Improvements to how engagements and related resources are fetched and saved

- **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)

## August 8, 2024
Expand Down
10 changes: 5 additions & 5 deletions met-web/src/components/common/Communication/StatusIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,32 @@ type IconWeight = 'solid' | 'regular' | 'light';
export const StatusIcon = ({
status,
color,
weight = 'solid',
weight = 'regular',
...props
}: {
status: 'success' | 'warning' | 'error' | 'info';
status: 'success' | 'warning' | 'danger' | 'info';
color?: string;
weight?: IconWeight;
} & Partial<FontAwesomeIconProps>) => {
let iconMap = {
success: faCheckCircle,
warning: faExclamationTriangle,
error: faExclamationCircle,
danger: faExclamationCircle,
info: faInfoCircle,
};
if (weight === 'regular') {
iconMap = {
success: faCheckCircleRegular,
warning: faExclamationTriangleRegular,
error: faExclamationCircleRegular,
danger: faExclamationCircleRegular,
info: faInfoCircleRegular,
};
}
if (weight === 'light') {
iconMap = {
success: faCheckCircleLight,
warning: faExclamationTriangleLight,
error: faExclamationCircleLight,
danger: faExclamationCircleLight,
info: faInfoCircleLight,
};
}
Expand Down
10 changes: 8 additions & 2 deletions met-web/src/components/common/Indicators/StatusChip.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Chip as MuiChip, Skeleton, useTheme } from '@mui/material';
import { ChipProps as MuiChipProps, Chip as MuiChip, Skeleton, useTheme } from '@mui/material';
import { colors } from '..';
import { SubmissionStatus } from 'constants/engagementStatus';

Expand All @@ -23,12 +23,17 @@ export const getStatusFromStatusId = (statusId: SubmissionStatus): StatusText =>
}
};

export const EngagementStatusChip: React.FC<ChipProps> = ({ label: customLabel, statusId: status }) => {
export const EngagementStatusChip: React.FC<ChipProps & Partial<MuiChipProps>> = ({
label: customLabel,
statusId: status,
...props
}) => {
const statusText = getStatusFromStatusId(status);
const theme = useTheme();
const invert = theme.palette.mode === 'dark';
return (
<MuiChip
{...props}
label={customLabel || statusText}
className={`status-chip status-chip-${statusText.toLowerCase()} ${invert ? 'status-chip-invert' : ''}`}
sx={{
Expand Down Expand Up @@ -74,6 +79,7 @@ export const EngagementStatusChip: React.FC<ChipProps> = ({ label: customLabel,
borderColor: colors.surface.gray[100],
color: colors.surface.gray[40],
},
...props.sx,
}}
/>
);
Expand Down
4 changes: 2 additions & 2 deletions met-web/src/components/common/Layout/SystemMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const SystemMessage = ({
children,
...props
}: {
status: 'success' | 'warning' | 'error' | 'info';
status: 'success' | 'warning' | 'danger' | 'info';
onDismiss?: () => void;
color?: string;
coloredBackground?: boolean;
Expand All @@ -30,7 +30,7 @@ export const SystemMessage = ({
maxWidth: { xs: '100%', md: '700px' },
borderRadius: '8px',
backgroundColor: coloredBackground ? colors.notification[status].tint : 'transparent',
color: 'type.primary',
color: 'text.primary',
padding: '0.8rem 1rem',
paddingLeft: { xs: '0.5rem', md: '1rem' },
border: `1px solid ${colors.notification[status].shade}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,16 @@ export const DateRangePickerWithCalculation = () => {
if (name === 'end_date') {
trigger('end_date');
}
if (!value?.end_date) return;
setNumberOfDays(value.end_date.clone().add(1, 'second').diff(value.start_date, 'days'));
});
return () => subscription.unsubscribe();
}, [watch]);

useEffect(() => {
if (startDate && endDate) {
setNumberOfDays(endDate.clone().add(1, 'second').diff(startDate, 'days'));
}
}, [startDate, endDate]);

const getDayStyle = (props: PickersDayProps<Dayjs | null>) => {
const standardStyle = {
margin: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,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}/form`);
return redirect(`/engagements/${engagement.id}/view`);
};

export default engagementCreateAction;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ENGAGEMENT_MEMBERSHIP_STATUS } from 'models/engagementTeamMember';
import { ActionFunction, redirect } from 'react-router-dom';
import { patchEngagement } from 'services/engagementService';
import { patchEngagementSlug } from 'services/engagementSlugService';
import {
addTeamMemberToEngagement,
revokeMembership,
reinstateMembership,
getTeamMembers,
} from 'services/membershipService';

export const engagementUpdateAction: ActionFunction = async ({ request, params }) => {
NatSquared marked this conversation as resolved.
Show resolved Hide resolved
const formData = (await request.formData()) as FormData;
const engagementId = Number(params.engagementId);
await patchEngagement({
id: engagementId,
name: formData.get('name') as string,
start_date: formData.get('start_date') as string,
end_date: formData.get('end_date') as string,
is_internal: formData.get('is_internal') === 'true',
});
try {
await patchEngagementSlug({
engagement_id: engagementId,
slug: formData.get('slug') as string,
});
} catch (e) {
console.error('Error updating engagement slug', e);
}

const currentTeamMembers = await getTeamMembers({ engagement_id: engagementId });
const users = formData.getAll('users') as string[];
const usersSet = new Set(users);

try {
// Process deactivated users for reinstatement (and active users for revocation)
// Caution - headaches ahead! There is a big difference between user_id and user.external_id
NatSquared marked this conversation as resolved.
Show resolved Hide resolved
for (const member of currentTeamMembers) {
const isUserInForm = usersSet.has(String(member.user.external_id));
if (member.status !== ENGAGEMENT_MEMBERSHIP_STATUS.Active) {
if (isUserInForm) {
// If the user was previously deactivated, reinstate them
reinstateMembership(engagementId, member.user_id);
}
} else if (!isUserInForm) {
// If the user was previously active but is not in the form, revoke their membership
revokeMembership(engagementId, member.user_id);
}

// Remove all known users from the set so we can add new members in the next step
usersSet.delete(String(member.user.external_id));
}
// Add new members that weren't in the current team members list
for (const user of usersSet) {
addTeamMemberToEngagement({ user_id: user, engagement_id: engagementId });
}
} catch (e) {
console.error('Error updating team members', e);
}

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

export default engagementUpdateAction;
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ const EngagementVisibilityControl = () => {
const isInternal = watch('is_internal');
const formSlug = watch('slug');
const isConfirmed = watch('_visibilityConfirmed');
const setIsConfirmed = (value: boolean) => setValue('_visibilityConfirmed', value);
const setIsConfirmed = (value: boolean) => setValue('_visibilityConfirmed', value, { shouldDirty: true });

const [isEditing, setIsEditing] = React.useState(false);
const [currentSlug, setCurrentSlug] = React.useState(formSlug);
const [hasBeenEdited, setHasBeenEdited] = React.useState(false);
const [hasBeenEdited, setHasBeenEdited] = React.useState(isConfirmed);

useEffect(() => {
const subscription = watch((value, { name, type }) => {
Expand All @@ -41,7 +41,7 @@ const EngagementVisibilityControl = () => {
})
.join('')
.toLowerCase();
setValue('slug', newSlug);
setValue('slug', newSlug, { shouldDirty: true });
setCurrentSlug(newSlug);
}
});
Expand Down Expand Up @@ -125,7 +125,7 @@ const EngagementVisibilityControl = () => {
onClick={() => {
setIsConfirmed(true);
setHasBeenEdited(true);
setValue('slug', currentSlug);
setValue('slug', currentSlug, { shouldDirty: true });
}}
>
Confirm
Expand Down Expand Up @@ -178,7 +178,7 @@ const EngagementVisibilityControl = () => {
disabled={!currentSlug}
variant="primary"
onClick={() => {
setValue('slug', currentSlug);
setValue('slug', currentSlug, { shouldDirty: true });
setHasBeenEdited(true);
setIsConfirmed(true);
setIsEditing(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { Grid, Checkbox, FormControlLabel } from '@mui/material';
import { SystemMessage } from 'components/common/Layout/SystemMessage';
import { EngagementConfigurationData } from './form';
import { EngagementConfigurationData } from './wizard';
import { useFormContext } from 'react-hook-form';

export const FeedbackMethodSelector = () => {
Expand All @@ -26,6 +26,7 @@ export const FeedbackMethodSelector = () => {
checked
? [...watch('feedback_methods'), 'survey']
: watch('feedback_methods').filter((m) => m !== 'survey'),
{ shouldDirty: true },
);
}}
/>
Expand All @@ -40,6 +41,7 @@ export const FeedbackMethodSelector = () => {
checked
? [...watch('feedback_methods'), '3rd_party']
: watch('feedback_methods').filter((m) => m !== '3rd_party'),
{ shouldDirty: true },
);
}}
/>
Expand Down
Loading
Loading