Skip to content

Commit ae55f7f

Browse files
committed
feat: use react-tanstack request handling & show enrollment errors
1 parent 04e6708 commit ae55f7f

3 files changed

Lines changed: 68 additions & 71 deletions

File tree

src/learningpath/CourseCard.jsx

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React, { useMemo, useState } from 'react';
22
import PropTypes from 'prop-types';
33
import {
44
Card, Button, Icon,
5+
Stack,
6+
Toast,
57
} from '@openedx/paragon';
68
import {
79
AccessTime,
@@ -140,27 +142,26 @@ export const CourseCard = ({
140142
</Card.Section>
141143

142144
<Card.Footer>
143-
<div className="d-flex flex-column flex-lg-row w-100">
145+
<Stack className="w-100" direction={isMedium ? 'vertical' : 'horizontal'} gap={2}>
144146
<Button
145147
variant="outline-primary"
146148
size="sm"
147-
className="flex-fill py-2 mr-2"
149+
className="flex-fill py-2"
148150
onClick={onClickViewButton}
149151
>
150152
{formatMessage(messages.moreDetails)}
151153
</Button>
152154

153-
{!disableStartButton && (
154155
<Button
155156
variant="primary"
156157
size="sm"
158+
disabled={disableStartButton}
157159
className="flex-fill py-2"
158160
onClick={onClick}
159161
>
160162
{ buttonText }
161163
</Button>
162-
)}
163-
</div>
164+
</Stack>
164165
</Card.Footer>
165166
</Card>
166167
);
@@ -204,6 +205,7 @@ export const CourseCardWithEnrollment = ({
204205

205206
const courseHomeUrl = buildCourseHomeUrl(course.id);
206207

208+
const [errorMessage, setErrorMessage] = useState('');
207209
const handleCourseAction = async () => {
208210
const { administrator } = getAuthenticatedUser();
209211

@@ -213,31 +215,36 @@ export const CourseCardWithEnrollment = ({
213215
}
214216

215217
setEnrolling(true);
216-
try {
217-
const result = await enrollCourseMutation.mutateAsync(course.id);
218-
if (result.success) {
218+
enrollCourseMutation.mutate(course.id, {
219+
onSuccess: () => {
219220
window.location.href = courseHomeUrl;
220-
} else {
221-
// eslint-disable-next-line no-console
222-
console.error('Failed to enroll in the course:', result.data?.error || 'Unknown error');
223-
}
224-
} catch (error) {
225-
// eslint-disable-next-line no-console
226-
console.error('Failed to enroll in the course:', error);
227-
} finally {
228-
setEnrolling(false);
229-
}
221+
setEnrolling(false);
222+
},
223+
onError: ({ response }) => {
224+
setErrorMessage(response.data.detail);
225+
setEnrolling(false);
226+
},
227+
});
230228
};
231229

232230
return (
233-
<CourseCard
234-
course={courseWithEnrollment}
235-
onClick={handleCourseAction}
236-
onClickViewButton={onClick}
237-
isEnrolledInLearningPath={isEnrolledInLearningPath}
238-
orientationOverride={orientationOverride}
239-
isEnrolledInCourse={courseWithEnrollment.isEnrolledInCourse}
240-
/>
231+
<>
232+
<Toast
233+
onClose={() => setErrorMessage('')}
234+
show={!!errorMessage}
235+
>
236+
{errorMessage}
237+
</Toast>
238+
239+
<CourseCard
240+
course={courseWithEnrollment}
241+
onClick={handleCourseAction}
242+
onClickViewButton={onClick}
243+
isEnrolledInLearningPath={isEnrolledInLearningPath}
244+
orientationOverride={orientationOverride}
245+
isEnrolledInCourse={courseWithEnrollment.isEnrolledInCourse}
246+
/>
247+
</>
241248
);
242249
};
243250

src/learningpath/LearningPathDetails.jsx

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useParams, Link, useSearchParams } from 'react-router-dom';
33
import {
44
Row, Spinner, Icon, ModalLayer, Button, Chip, Card, Collapsible, Col,
55
Stack,
6+
Toast,
67
} from '@openedx/paragon';
78
import {
89
Person,
@@ -49,7 +50,7 @@ const LearningPathDetailPage = () => {
4950

5051
const { data: learningPaths, isLoading: loadingLearningPaths } = useLearningPaths();
5152

52-
const key = learningPaths?.find((lp) => lp.slug === catalogId).id;
53+
const key = learningPaths?.find((lp) => lp.slug === catalogId)?.id;
5354

5455
const {
5556
data: detail,
@@ -70,20 +71,23 @@ const LearningPathDetailPage = () => {
7071
setIsModalOpen(false);
7172
};
7273

73-
const handleAllowAndContinue = async () => {
74+
const [errorMessage, setErrorMessage] = useState('');
75+
const handleAllowAndContinue = () => {
7476
if (detail && !detail.enrollmentDate) {
7577
setEnrolling(true);
7678
setIsModalOpen(false);
77-
try {
78-
await enrollMutation.mutateAsync(key);
79-
setLocalStatus('accepted');
80-
} catch (error) {
81-
// eslint-disable-next-line no-console
82-
console.error('Enrollment failed:', error);
83-
} finally {
84-
setActiveTab('courses');
85-
setEnrolling(false);
86-
}
79+
enrollMutation.mutate(key, {
80+
onSuccess: () => {
81+
setLocalStatus('accepted');
82+
setEnrolling(false);
83+
setActiveTab('courses');
84+
},
85+
onError: ({ response }) => {
86+
setErrorMessage(response.data.detail);
87+
setEnrolling(false);
88+
},
89+
90+
});
8791
}
8892
};
8993

@@ -172,10 +176,12 @@ const LearningPathDetailPage = () => {
172176
} else if (detailError || !detail) {
173177
content = (
174178
<div className="p-4">
175-
<p>{formatMessage(messages.failedToLoadDetail)}</p>
179+
<p className="text-center">{formatMessage(messages.failedToLoadDetail)}</p>
176180
<Link to="/">
177-
<Icon src={ChevronLeft} />
178-
<span>{formatMessage(messages.backToMyCatalogs)}</span>
181+
<Stack direction="horizontal">
182+
<Icon src={ChevronLeft} />
183+
<span>{formatMessage(messages.backToMyCatalogs)}</span>
184+
</Stack>
179185
</Link>
180186
</div>
181187
);
@@ -338,6 +344,7 @@ const LearningPathDetailPage = () => {
338344
learningPathSteps={detail?.steps}
339345
learningPathId={key}
340346
onCourseClick={handleCourseViewButton}
347+
isEnrolledInLearningPath={isEnrolledInLearningPath}
341348
/>
342349
)}
343350
</section>
@@ -378,6 +385,13 @@ const LearningPathDetailPage = () => {
378385

379386
return (
380387
<>
388+
<Toast
389+
onClose={() => setErrorMessage('')}
390+
show={!!errorMessage}
391+
>
392+
{errorMessage}
393+
</Toast>
394+
381395
{content}
382396

383397
{selectedCourseKey && (

src/learningpath/data/api.js

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -109,40 +109,16 @@ export async function fetchAllCourseCompletions() {
109109

110110
export async function enrollInLearningPath(learningPathId) {
111111
const client = getAuthenticatedHttpClient();
112-
try {
113-
const response = await client.post(
114-
`${getConfig().LMS_BASE_URL}/partner_catalog/api/v1/catalogs/${learningPathId}/enroll/`,
115-
);
116-
return {
117-
success: true,
118-
status: response.status,
119-
};
120-
} catch (error) {
121-
return {
122-
success: false,
123-
status: error.response?.status,
124-
error,
125-
};
126-
}
112+
return client.post(
113+
`${getConfig().LMS_BASE_URL}/partner_catalog/api/v1/catalogs/${learningPathId}/enroll/`,
114+
);
127115
}
128116

129117
export async function enrollInCourse(learningPathId, courseId) {
130118
const client = getAuthenticatedHttpClient();
131-
try {
132-
const response = await client.post(
133-
`${getConfig().LMS_BASE_URL}/partner_catalog/api/v1/catalogs/${learningPathId}/courses/${courseId}/enroll/`,
134-
);
135-
return {
136-
success: true,
137-
status: response.status,
138-
};
139-
} catch (error) {
140-
return {
141-
success: false,
142-
status: error.response?.status,
143-
error,
144-
};
145-
}
119+
return client.post(
120+
`${getConfig().LMS_BASE_URL}/partner_catalog/api/v1/catalogs/${learningPathId}/courses/${courseId}/enroll/`,
121+
);
146122
}
147123

148124
export async function fetchCourseEnrollmentStatus(courseId) {

0 commit comments

Comments
 (0)