Skip to content

Commit 78e212c

Browse files
feat: update experiences on form changes (#5062)
1 parent 2581fde commit 78e212c

6 files changed

Lines changed: 62 additions & 16 deletions

File tree

packages/shared/src/features/profile/components/experience/DeleteExperienceButton.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ const DeleteExperienceButton = ({
3434
experienceId,
3535
experienceType,
3636
}: DeleteExperienceButtonProps): React.ReactElement => {
37-
const { removeExperience, isPending } = useRemoveExperience();
37+
const { removeExperience, isPending } = useRemoveExperience({
38+
type: experienceType,
39+
});
3840
const { showPrompt } = usePrompt();
3941

4042
const buttonCopy = getCopy(experienceType);

packages/shared/src/hooks/useRemoveExperience.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
1-
import { useMutation } from '@tanstack/react-query';
1+
import { useMutation, useQueryClient } from '@tanstack/react-query';
22
import { useRouter } from 'next/router';
3+
import type { UserExperienceType } from '../graphql/user/profile';
34
import { removeUserExperience } from '../graphql/user/profile';
45
import { useToastNotification } from './useToastNotification';
56
import { labels } from '../lib/labels';
67
import { webappUrl } from '../lib/constants';
8+
import { useUserExperiencesByType } from '../features/profile/hooks/useUserExperiencesByType';
9+
import { useAuthContext } from '../contexts/AuthContext';
710

8-
const useRemoveExperience = () => {
11+
const useRemoveExperience = ({ type }: { type: UserExperienceType }) => {
12+
const { user } = useAuthContext();
913
const router = useRouter();
1014
const { displayToast } = useToastNotification();
11-
15+
const { queryKey: experienceQueryKey } = useUserExperiencesByType(
16+
type,
17+
user?.id,
18+
);
19+
const qc = useQueryClient();
1220
const { mutate, isPending } = useMutation({
1321
mutationFn: (id: string) => removeUserExperience(id),
1422
onSuccess: () => {
15-
const searchParams = new URLSearchParams(window.location.search);
16-
const type = searchParams?.get('type');
1723
router.push(`${webappUrl}/settings/profile/experience/${type}`);
24+
qc.invalidateQueries({ queryKey: experienceQueryKey });
1825
},
1926
onError: () => {
2027
displayToast(labels.error.generic || 'Failed to delete experience');

packages/shared/src/hooks/useUserExperienceForm.spec.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ jest.mock('../graphql/user/profile', () => ({
2929
upsertUserGeneralExperience: jest.fn(),
3030
}));
3131

32+
// Mock AuthContext
33+
jest.mock('../contexts/AuthContext', () => ({
34+
useAuthContext: jest.fn(() => ({
35+
user: { id: 'test-user-id' },
36+
})),
37+
}));
38+
39+
// Mock useUserExperiencesByType hook
40+
jest.mock('../features/profile/hooks/useUserExperiencesByType', () => ({
41+
useUserExperiencesByType: jest.fn(() => ({
42+
queryKey: ['user-experiences', 'test-type', 'test-user-id'],
43+
})),
44+
}));
45+
3246
const mockRouter = {
3347
back: jest.fn(),
3448
pathname: '/profile/experience',
@@ -59,6 +73,8 @@ type BaseUserExperience = {
5973
subtitle?: string | null;
6074
current?: boolean;
6175
companyId?: string | null;
76+
customCompanyName?: string | null;
77+
url?: string | null;
6278
};
6379

6480
describe('useUserExperienceForm', () => {

packages/shared/src/hooks/useUserExperienceForm.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMutation } from '@tanstack/react-query';
1+
import { useMutation, useQueryClient } from '@tanstack/react-query';
22
import { useForm } from 'react-hook-form';
33
import { z } from 'zod';
44
import { zodResolver } from '@hookform/resolvers/zod';
@@ -20,6 +20,8 @@ import { labels } from '../lib/labels';
2020
import { applyZodErrorsToForm } from '../lib/form';
2121
import { useToastNotification } from './useToastNotification';
2222
import { webappUrl } from '../lib/constants';
23+
import { useUserExperiencesByType } from '../features/profile/hooks/useUserExperiencesByType';
24+
import { useAuthContext } from '../contexts/AuthContext';
2325

2426
export const userExperienceInputBaseSchema = z
2527
.object({
@@ -28,7 +30,7 @@ export const userExperienceInputBaseSchema = z
2830
description: z.string().max(5000).optional(),
2931
subtitle: z.string().max(1000).optional(),
3032
startedAt: z.date({ message: 'Start date is required.' }),
31-
endedAt: z.date().optional(),
33+
endedAt: z.date().optional().nullable(),
3234
current: z.boolean().default(false),
3335
companyId: z.string().nullable().optional().default(null),
3436
customCompanyName: z
@@ -75,6 +77,12 @@ const useUserExperienceForm = ({
7577
}: {
7678
defaultValues: BaseUserExperience;
7779
}) => {
80+
const qc = useQueryClient();
81+
const { user } = useAuthContext();
82+
const { queryKey: experienceQueryKey } = useUserExperiencesByType(
83+
defaultValues.type,
84+
user?.id,
85+
);
7886
const dirtyFormRef = useRef<ReturnType<typeof useDirtyForm> | null>(null);
7987
const router = useRouter();
8088
const { displayToast } = useToastNotification();
@@ -88,10 +96,11 @@ const useUserExperienceForm = ({
8896
type === UserExperienceType.Work
8997
? upsertUserWorkExperience(data as UserExperienceWork, id)
9098
: upsertUserGeneralExperience(data, id),
91-
onSuccess: () => {
99+
onSuccess: (_, vars) => {
100+
methods.reset(vars);
92101
dirtyFormRef.current?.allowNavigation();
102+
qc.invalidateQueries({ queryKey: experienceQueryKey });
93103
router.push(`${webappUrl}settings/profile/experience/${type}`);
94-
methods.reset();
95104
},
96105
onError: (error: ApiErrorResult) => {
97106
if (

packages/shared/src/hooks/useUserInfoForm.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useContext, useRef } from 'react';
22
import { useForm } from 'react-hook-form';
33
import type { UseFormReturn } from 'react-hook-form';
4-
import { useMutation } from '@tanstack/react-query';
4+
import { useMutation, useQueryClient } from '@tanstack/react-query';
55
import { useRouter } from 'next/router';
66
import AuthContext from '../contexts/AuthContext';
77
import { UPDATE_USER_PROFILE_MUTATION } from '../graphql/users';
8-
import type { LoggedUser, UserProfile } from '../lib/user';
8+
import type { LoggedUser, PublicProfile, UserProfile } from '../lib/user';
99
import { useToastNotification } from './useToastNotification';
1010
import type { ResponseError } from '../graphql/common';
1111
import { errorMessage, gqlClient } from '../graphql/common';
@@ -15,6 +15,7 @@ import { useActions } from './useActions';
1515
import { getCompletionItems } from '../features/profile/components/ProfileWidgets/ProfileCompletion';
1616
import { useLogContext } from '../contexts/LogContext';
1717
import { LogEvent } from '../lib/log';
18+
import { useProfile } from './profile/useProfile';
1819

1920
export interface ProfileFormHint {
2021
portfolio?: string;
@@ -59,8 +60,10 @@ const socials = [
5960
];
6061

6162
const useUserInfoForm = (): UseUserInfoForm => {
62-
const { logEvent } = useLogContext();
63+
const qc = useQueryClient();
6364
const { user, updateUser } = useContext(AuthContext);
65+
const { userQueryKey } = useProfile(user as PublicProfile);
66+
const { logEvent } = useLogContext();
6467
const { displayToast } = useToastNotification();
6568
const { completeAction, checkHasCompleted, isActionsFetched } = useActions();
6669
const router = useRouter();
@@ -105,6 +108,11 @@ const useUserInfoForm = (): UseUserInfoForm => {
105108
}),
106109

107110
onSuccess: async (_, vars) => {
111+
const oldProfileData = qc.getQueryData<PublicProfile>(userQueryKey);
112+
qc.setQueryData(userQueryKey, {
113+
...oldProfileData,
114+
...vars,
115+
});
108116
await updateUser({ ...user, ...vars });
109117

110118
dirtyFormRef.current?.allowNavigation();

packages/webapp/pages/settings/profile/experience/edit.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async ({
113113
startedAtYear,
114114
endedAtMonth,
115115
endedAtYear,
116+
current: !result?.endedAt,
116117
skills: result?.skills.map((skill) => skill.value),
117118
location: result?.location,
118119
locationId: result?.location?.id || '',
@@ -121,7 +122,10 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async ({
121122
};
122123
};
123124

124-
const renderExperienceForm = (type?: UserExperienceType) => {
125+
const renderExperienceForm = (
126+
type?: UserExperienceType,
127+
experience?: DefaultValues,
128+
) => {
125129
switch (type) {
126130
case UserExperienceType.Education:
127131
return <UserEducationForm />;
@@ -133,7 +137,7 @@ const renderExperienceForm = (type?: UserExperienceType) => {
133137
case UserExperienceType.OpenSource:
134138
return <UserProjectExperienceForm />;
135139
default:
136-
return <UserWorkExperienceForm />;
140+
return <UserWorkExperienceForm location={experience?.location} />;
137141
}
138142
};
139143

@@ -163,7 +167,7 @@ const Page = ({ experience }: PageProps): ReactElement => {
163167
</Button>
164168
}
165169
>
166-
{renderExperienceForm(experience?.type)}
170+
{renderExperienceForm(experience?.type, experience)}
167171
{experience?.id && (
168172
<DeleteExperienceButton
169173
experienceId={experience.id}

0 commit comments

Comments
 (0)