Skip to content

Commit

Permalink
✨(frontend) refetch user profile after edition
Browse files Browse the repository at this point in the history
When a user go on OpenEdx profile form, it need's to see he's data up to
date when coming back.
The click happend now in the same tab.
  • Loading branch information
rlecellier committed Apr 25, 2024
1 parent b5db1b3 commit 8ff9776
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).

## Fixed

- User profile in the learner dashboard is now always synchornized with
openEdx profile informations.
- Ongoing product displayed on the syllabus with an active enrollment
now link to the order details page in the learner dashboard instead of
OpenEdx course.
Expand Down
26 changes: 13 additions & 13 deletions src/frontend/js/hooks/useOpenEdxProfile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,6 @@ const useOpenEdxProfile = (
const [error, setError] = useState<string>();
const queryClient = useQueryClient();

const invalidate = async () => {
// Invalidate all queries related to the resource
await queryClient.invalidateQueries({ queryKey: ['user', 'open-edx-profile'] });
await queryClient.invalidateQueries({ queryKey: ['user'], exact: true });
};

const onSuccess = async () => {
setError(undefined);
await invalidate();
onUpdateSuccess?.();
};

const queryFn: () => Promise<OpenEdxProfile> = useCallback(async () => {
try {
const openEdxApiProfile = await AuthenticationApi!.account!.get(username);
Expand All @@ -69,12 +57,24 @@ const useOpenEdxProfile = (
return Promise.reject();
}, [username, AuthenticationApi]);

const [readHandler] = useSessionQuery<OpenEdxProfile>(
const [readHandler, sessionQueryKey] = useSessionQuery<OpenEdxProfile>(
['open-edx-profile'],
queryFn,
queryOptions,
);

const invalidate = async () => {
// Invalidate all queries related to the resource
await queryClient.invalidateQueries({ queryKey: ['user'], exact: true });
await queryClient.invalidateQueries({ queryKey: sessionQueryKey });
};

const onSuccess = async () => {
setError(undefined);
await invalidate();
onUpdateSuccess?.();
};

const mutation = useSessionMutation;
const writeHandlers = {
update: mutation({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ describe('<DashboardCreateAddress/>', () => {
});

it('creates an address and redirect to preferences', async () => {
fetchMock.get('https://demo.endpoint/api/v1.0/user/me', richieUser);
fetchMock.get('https://joanie.endpoint/api/v1.0/addresses/', []);
fetchMock.post('https://joanie.endpoint/api/v1.0/addresses/', []);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
within,
} from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import userEvent from '@testing-library/user-event';
import {
UserFactory,
RichieContextFactory as mockRichieContextFactory,
Expand Down Expand Up @@ -54,6 +55,7 @@ describe('<DashboardEditAddress/>', () => {
fetchMock.put(updateUrl, HttpStatusCode.OK);

const richieUser = UserFactory().one();
fetchMock.get('https://demo.endpoint/api/v1.0/user/me', richieUser);
fetchMock.get(`https://demo.endpoint/api/user/v1/accounts/${richieUser.username}`, {});
fetchMock.get(`https://demo.endpoint/api/user/v1/preferences/${richieUser.username}`, {});

Expand Down Expand Up @@ -111,11 +113,10 @@ describe('<DashboardEditAddress/>', () => {

// Submit of the form calls the API edit route.
expect(fetchMock.called(updateUrl, { method: 'put' })).toBe(false);
await act(async () => {
// it is not necessary to update all fields as it is mocked above.
fireEvent.change($titleInput, { target: { value: addressUpdated.title } });
fireEvent.click($button);
});
const user = userEvent.setup();
await user.clear($titleInput);
await user.type($titleInput, addressUpdated.title);
await user.click($button);
expect(fetchMock.called(updateUrl, { method: 'put' })).toBe(true);

// The API is called with correct body.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ describe('<DashAddressesManagement/>', () => {
}).one();
const { 'pref-lang': prefLang, ...openEdxAccount } = openEdxProfile;

fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);
fetchMock.get(
`https://endpoint.test/api/user/v1/accounts/${richieUser.username}`,
openEdxAccount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ describe('<DahsboardEditCreditCard/>', () => {
});

it('updates a credit card name', async () => {
fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);
const creditCard = CreditCardFactory().one();
const creditCards = [...CreditCardFactory().many(5), creditCard];
const creditCardUpdated = CreditCardFactory().one();
Expand Down Expand Up @@ -195,6 +196,7 @@ describe('<DahsboardEditCreditCard/>', () => {
});

it('sets credit card as main', async () => {
fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);
const creditCard = CreditCardFactory().one();
const creditCards = [...CreditCardFactory().many(5), creditCard];
const creditCardUpdated = { ...creditCard };
Expand Down Expand Up @@ -282,6 +284,7 @@ describe('<DahsboardEditCreditCard/>', () => {
});

it('deletes a credit card', async () => {
fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);
const creditCard = CreditCardFactory().one();
let creditCards = [...CreditCardFactory().many(5), creditCard];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('<DashboardCreditCardsManagement/>', () => {
}).one();
const { 'pref-lang': prefLang, ...openEdxAccount } = openEdxProfile;

fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);
fetchMock.get(
`https://endpoint.test/api/user/v1/accounts/${richieUser.username}`,
openEdxAccount,
Expand Down
9 changes: 8 additions & 1 deletion src/frontend/js/pages/DashboardOpenEdxProfile/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ describe('pages.DashboardOpenEdxProfile', () => {
setupJoanieSession();

it('should render profile informations', async () => {
const richieUser = UserFactory().one();
fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);

const intl = createIntl({ locale: 'en' });
const languageNames = new Intl.DisplayNames([intl.locale], { type: 'language' });
const richieUser = UserFactory().one();
const openEdxProfile = OpenEdxApiProfileFactory({
username: richieUser.username,
email: richieUser.email,
Expand Down Expand Up @@ -83,6 +85,8 @@ describe('pages.DashboardOpenEdxProfile', () => {

it('should render empty profile informations', async () => {
const richieUser = UserFactory().one();
fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);

const openEdxProfile = OpenEdxApiProfileFactory({
name: '',
country: null,
Expand Down Expand Up @@ -122,6 +126,7 @@ describe('pages.DashboardOpenEdxProfile', () => {

it('should display get error when account request fail', async () => {
const richieUser = UserFactory().one();
fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);

fetchMock.get(
`https://endpoint.test/api/user/v1/accounts/${richieUser.username}`,
Expand All @@ -142,6 +147,8 @@ describe('pages.DashboardOpenEdxProfile', () => {

it('should display get error when preferences request fail', async () => {
const richieUser = UserFactory().one();
fetchMock.get('https://endpoint.test/api/v1.0/user/me', richieUser);

const openEdxProfile = OpenEdxApiProfileFactory({
username: richieUser.username,
email: richieUser.email,
Expand Down
31 changes: 22 additions & 9 deletions src/frontend/js/pages/DashboardOpenEdxProfile/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Input } from '@openfun/cunningham-react';
import { Button, Input } from '@openfun/cunningham-react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { useEffect } from 'react';
import { useSession } from 'contexts/SessionContext';
import useOpenEdxProfile from 'hooks/useOpenEdxProfile';
import { DashboardBox } from 'widgets/Dashboard/components/DashboardBox';
import { DashboardCard } from 'widgets/Dashboard/components/DashboardCard';
import { RouterButton } from 'widgets/Dashboard/components/RouterButton';
import Form from 'components/Form';
import context from 'utils/context';
import Banner, { BannerType } from 'components/Banner';
Expand Down Expand Up @@ -111,7 +111,24 @@ export const DEFAULT_DISPLAYED_FORM_VALUE = ' - ';
const DashboardOpenEdxProfile = () => {
const intl = useIntl();
const { user } = useSession();
const { data: openEdxProfileData, error } = useOpenEdxProfile({ username: user!.username });
const {
data: openEdxProfileData,
methods: { invalidate },
error,
} = useOpenEdxProfile({ username: user!.username });

const onClickModifyProfile = async () => {
window.location.replace(`${context.authentication.endpoint}/account/settings`);
};

// Because the user can leaver the page to update his profile in openEdx.
// if we invalidate before leaving the page, it's directly refetched.
// We need to invalidate openEdx's profile each time we reload this page.
// it's the only way to have user data synchronised between richie's frontend
// and openEdx's database.
useEffect(() => {
invalidate();
}, []);

if (error) {
return (
Expand Down Expand Up @@ -225,13 +242,9 @@ const DashboardOpenEdxProfile = () => {
</DashboardBox.List>

<Form.Footer>
<RouterButton
fullWidth
target="_blank"
href={`${context.authentication.endpoint}/account/settings`}
>
<Button fullWidth onClick={onClickModifyProfile}>
<FormattedMessage {...messages.editButtonLabel} />
</RouterButton>
</Button>
</Form.Footer>
</DashboardCard>
);
Expand Down

0 comments on commit 8ff9776

Please sign in to comment.