Skip to content

Commit 64c60be

Browse files
committed
Hotfix bug in engagement config update
1 parent e143fd3 commit 64c60be

9 files changed

Lines changed: 68 additions & 28 deletions

File tree

met-web/src/components/common/Layout/SystemMessage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export const SystemMessage = ({
5959
container
6060
direction="row"
6161
justifyContent="flex-start"
62-
alignItems="flex-start"
62+
alignItems="center"
6363
sx={{
6464
maxWidth: { xs: '100%', md: '700px' },
6565
borderRadius: '8px',
@@ -72,7 +72,7 @@ export const SystemMessage = ({
7272
...props.sx,
7373
}}
7474
>
75-
<Grid sx={{ pr: 1, mt: -0.5, fontSize: '18px' }}>
75+
<Grid sx={{ pr: 1, fontSize: '18px' }}>
7676
<StatusIcon status={status} color={color} />
7777
</Grid>
7878
<Grid sx={{ width: '100%', maxWidth: { xs: '100%', md: '600px', fontSize: '14px' } }}>{children}</Grid>

met-web/src/components/engagement/admin/config/EngagementUpdateAction.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
1+
import axios from 'axios';
12
import { ENGAGEMENT_MEMBERSHIP_STATUS } from 'models/engagementTeamMember';
23
import { ActionFunction, redirect } from 'react-router';
3-
import { patchEngagement } from 'services/engagementService';
4+
import { ApiErrorBody, patchEngagement } from 'services/engagementService';
45
import { patchEngagementSlug } from 'services/engagementSlugService';
6+
import { openNotification } from 'services/notificationService/notificationSlice';
57
import {
68
addTeamMemberToEngagement,
79
revokeMembership,
810
reinstateMembership,
911
getTeamMembers,
1012
} from 'services/membershipService';
13+
import { store } from 'store';
1114

1215
export const engagementUpdateAction: ActionFunction = async ({ request, params }) => {
1316
const formData = (await request.formData()) as FormData;
1417
const engagementId = Number(params.engagementId);
15-
await patchEngagement({
16-
id: engagementId,
17-
name: formData.get('name') as string,
18-
start_date: formData.get('start_date') as string,
19-
end_date: formData.get('end_date') as string,
20-
is_internal: formData.get('is_internal') === 'true',
21-
});
18+
try {
19+
await patchEngagement({
20+
id: engagementId,
21+
name: formData.get('name') as string,
22+
start_date: formData.get('start_date') as string,
23+
end_date: formData.get('end_date') as string,
24+
is_internal: formData.get('is_internal') === 'true',
25+
});
26+
} catch (e) {
27+
const message = axios.isAxiosError<ApiErrorBody>(e)
28+
? typeof e.response?.data === 'string'
29+
? e.response.data
30+
: (e.response?.data?.message ?? 'Failed to update engagement')
31+
: 'Failed to update engagement';
32+
console.error('Error updating engagement:', e);
33+
store.dispatch(openNotification({ severity: 'error', text: message }));
34+
return { status: 'failure' };
35+
}
2236
try {
2337
await patchEngagementSlug({
2438
engagement_id: engagementId,

met-web/src/components/engagement/admin/config/LanguageManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const LanguageManager = () => {
2525
const engagementForm = useFormContext();
2626
const { setValue, watch } = engagementForm;
2727
const selectedLanguages = watch('languages') as Language[];
28-
const fetcher = useFetcher();
28+
const fetcher = useFetcher({ key: 'language-loader' });
2929
const fetcherData = fetcher.data as Awaited<LanguageLoaderData>;
3030
const { languages: availableLanguages } = fetcherData ?? { languages: [] };
3131

met-web/src/components/engagement/admin/config/UserManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import MultiSelect from './MultiSelect';
1313

1414
export const UserManager = () => {
1515
const currentUser = useAppSelector((state) => state.user);
16-
const fetcher = useFetcher();
16+
const fetcher = useFetcher({ key: 'user-search' });
1717

1818
const engagementForm = useFormContext();
1919
const { setValue, watch } = engagementForm;

met-web/src/components/engagement/admin/config/wizard/ConfigWizard.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React, { Suspense } from 'react';
1+
import React, { Suspense, useEffect } from 'react';
22
import { ResponsiveContainer } from 'components/common/Layout';
3-
import { useFetcher, createSearchParams, useRouteLoaderData, Await, useAsyncValue, useNavigation } from 'react-router';
3+
import { useFetcher, createSearchParams, useRouteLoaderData, Await, useAsyncValue } from 'react-router';
44
import { FormProvider, useForm } from 'react-hook-form';
55
import { AutoBreadcrumbs } from 'components/common/Navigation/Breadcrumb';
66
import EngagementForm, { EngagementConfigurationData } from '.';
@@ -13,6 +13,8 @@ import { Language } from 'models/language';
1313
import { CircularProgress, Grid2 as Grid, Modal, Skeleton } from '@mui/material';
1414
import { modalStyle } from 'components/common';
1515

16+
type ConfigUpdateFetcherData = { status?: 'failure' };
17+
1618
const EngagementConfigurationWizard = () => {
1719
const { engagement, teamMembers, slug } = useRouteLoaderData('single-engagement') as EngagementLoaderAdminData;
1820
return (
@@ -43,9 +45,8 @@ const EngagementConfigurationWizard = () => {
4345
};
4446

4547
const ConfigForm = () => {
48+
const fetcher = useFetcher<ConfigUpdateFetcherData>({ key: 'config-update' });
4649
const [engagement, teamMembers, slug] = useAsyncValue() as [Engagement, EngagementTeamMember[], string];
47-
const fetcher = useFetcher();
48-
const navigation = useNavigation();
4950

5051
const engagementConfigForm = useForm<EngagementConfigurationData>({
5152
defaultValues: {
@@ -65,7 +66,7 @@ const ConfigForm = () => {
6566
});
6667

6768
const onSubmit = async (data: EngagementConfigurationData) => {
68-
fetcher.submit(
69+
await fetcher.submit(
6970
createSearchParams({
7071
name: data.name,
7172
feedback_methods: data.feedback_methods,
@@ -84,17 +85,22 @@ const ConfigForm = () => {
8485
};
8586

8687
const {
87-
formState: { isSubmitting, isSubmitted },
88+
getValues,
89+
reset,
90+
formState: { isSubmitting, isSubmitted, defaultValues },
8891
} = engagementConfigForm;
8992

93+
useEffect(() => {
94+
if (fetcher.state === 'idle' && fetcher.data?.status === 'failure') {
95+
// Keep entered field values but clear submit state so the modal can close.
96+
reset(defaultValues, { keepValues: true, keepDirty: false, keepSubmitCount: false });
97+
}
98+
}, [fetcher.state, fetcher.data, getValues, reset]);
99+
90100
return (
91101
<FormProvider {...engagementConfigForm}>
92102
<EngagementForm onSubmit={onSubmit} />
93-
<Modal
94-
open={
95-
isSubmitting || isSubmitted || fetcher.state === 'submitting' || navigation.state === 'submitting'
96-
}
97-
>
103+
<Modal open={isSubmitting || isSubmitted}>
98104
<Grid
99105
container
100106
direction="row"

met-web/src/components/engagement/admin/config/wizard/CreationWizard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { SystemMessage } from 'components/common/Layout/SystemMessage';
99
import Grid from '@mui/material/Grid2';
1010

1111
const EngagementCreationWizard = () => {
12-
const fetcher = useFetcher();
12+
const fetcher = useFetcher({ key: 'config-update' });
1313

1414
const engagementCreationForm = useForm<EngagementConfigurationData>({
1515
defaultValues: {

met-web/src/components/engagement/admin/config/wizard/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const EngagementForm = ({
4747
control,
4848
handleSubmit,
4949
watch,
50-
formState: { errors, isDirty, isValid, isSubmitting, touchedFields },
50+
formState: { errors, isDirty, isValid, isSubmitting, isSubmitted, touchedFields },
5151
} = engagementForm;
5252

5353
const [nameHasBeenEdited, setNameHasBeenEdited] = useState(false);
@@ -144,6 +144,7 @@ const EngagementForm = ({
144144
!isValid ||
145145
!isDirty ||
146146
isSubmitting ||
147+
isSubmitted ||
147148
!watch('_dateConfirmed') ||
148149
!watch('_visibilityConfirmed')
149150
}

met-web/src/components/engagement/widgets/Subscribe/SubscribeWidget.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import EmailListSection from './EmailListSection';
1111
import EmailListModal from './EmailListModal';
1212
import FormSignUpSection from './FormSignUpSection';
1313
import { Header2 } from 'components/common/Typography';
14-
import { useRouteLoaderData } from 'react-router';
14+
import { useLoaderData } from 'react-router';
1515
import { EngagementLoaderPublicData } from 'engagements/public/view';
1616
import { BaseTheme } from 'styles/Theme';
1717

1818
const SubscribeWidget = ({ widget }: { widget: Widget }) => {
1919
const dispatch = useAppDispatch();
20-
const { widgets } = useRouteLoaderData('single-engagement') as EngagementLoaderPublicData;
20+
const { widgets } = useLoaderData() as EngagementLoaderPublicData;
2121
const [open, setOpen] = useState(false);
2222
const [subscribeItems, setSubscribeItems] = useState<SubscribeForm[]>([]);
2323
const [isLoadingSubscribeItems, setIsLoadingSubscribeItems] = useState(true);

met-web/src/styles/Theme.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ export const BaseTheme = createTheme({
261261
styleOverrides: {
262262
root: {
263263
backgroundColor: Palette.background.paper,
264-
borderRadius: '1rem',
264+
borderRadius: '8px',
265265
backgroundImage: 'none',
266266
},
267267
},
@@ -280,10 +280,29 @@ export const BaseTheme = createTheme({
280280
style: {
281281
backgroundColor: 'white',
282282
borderRadius: '8px',
283+
transition: 'box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
284+
'&:hover, &:has(:focus), &:has(:focus-visible)': {
285+
backgroundColor: 'color-mix(in srgb, white, black 20%)',
286+
boxShadow: elevations.hover,
287+
},
288+
'&:has(:focus-visible)': {
289+
backgroundColor: 'color-mix(in srgb, white, black 20%)',
290+
outline: `2px solid ${Palette.focus.outer}`,
291+
boxShadow: `0 0 0 2px ${Palette.focus.inner} inset,${elevations.hover}`,
292+
},
293+
'&:has(:active)': {
294+
backgroundColor: 'color-mix(in srgb, white, black 20%)',
295+
boxShadow: elevations.pressed,
296+
},
297+
'&.Mui-disabled, &:has(:disabled)': {
298+
backgroundColor: colors.surface.gray[10],
299+
color: colors.type.regular.disabled,
300+
},
283301
},
284302
},
285303
],
286304
defaultProps: {
305+
disableUnderline: true,
287306
IconComponent: CustomSelectIcon,
288307
MenuProps: {
289308
sx: {

0 commit comments

Comments
 (0)