Skip to content

Commit 25c626a

Browse files
committed
feat: Add the un/archive buttons.
Hook up archive and unarchive buttons to the UI and link them to the relevant backend APIs.
1 parent ea1675c commit 25c626a

File tree

1 file changed

+151
-23
lines changed

1 file changed

+151
-23
lines changed

frontend/src/plugin.jsx

Lines changed: 151 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
import React, { useState, useEffect } from "react";
22
import { getConfig } from "@edx/frontend-platform";
33
import { getAuthenticatedHttpClient } from "@edx/frontend-platform/auth";
4-
import { Card, Container, Row, Col, Badge, Collapsible } from "@openedx/paragon";
4+
import {
5+
Card,
6+
Container,
7+
Row,
8+
Col,
9+
Badge,
10+
Collapsible,
11+
Button,
12+
Spinner,
13+
} from "@openedx/paragon";
14+
import { Archive, Unarchive } from "@openedx/paragon/icons";
515

616
const CourseList = ({ courseListData }) => {
717
const [archivedCourses, setArchivedCourses] = useState(new Set());
18+
const [loadingStates, setLoadingStates] = useState(new Map());
819

920
// Safety check for courseListData
1021
if (!courseListData || !courseListData.visibleList) {
@@ -89,28 +100,145 @@ const CourseList = ({ courseListData }) => {
89100
console.log("DEBUG: Active courses count:", activeCourses.length);
90101
console.log("DEBUG: Archived courses count:", archivedCoursesList.length);
91102

92-
const renderCourse = (courseData, isArchived = false) => (
93-
<Col key={courseData.cardId} xs={12} sm={6} md={4} lg={3} className="mb-4">
94-
<Card>
95-
<Card.ImageCap
96-
src={getConfig().LMS_BASE_URL + courseData.course.bannerImgSrc}
97-
alt={courseData.course.courseName}
98-
/>
99-
<Card.Header
100-
title={courseData.course.courseName}
101-
subtitle={courseData.course.courseNumber}
102-
actions={isArchived && <Badge variant="secondary">Archived</Badge>}
103-
/>
104-
<Card.Section>
105-
{courseData.course.shortDescription && (
106-
<p className="text-muted small">
107-
{courseData.course.shortDescription}
108-
</p>
109-
)}
110-
</Card.Section>
111-
</Card>
112-
</Col>
113-
);
103+
const handleArchiveToggle = async (courseId, isCurrentlyArchived) => {
104+
console.log(
105+
`DEBUG: Toggling archive for course ${courseId}, currently archived: ${isCurrentlyArchived}`,
106+
);
107+
108+
setLoadingStates((prev) => new Map(prev).set(courseId, true));
109+
110+
try {
111+
const client = getAuthenticatedHttpClient();
112+
const lmsBaseUrl = getConfig().LMS_BASE_URL;
113+
const url = `${lmsBaseUrl}/sample-plugin/api/v1/course-archive-status/`;
114+
115+
if (isCurrentlyArchived) {
116+
// Unarchive: Find existing record and update it
117+
const listResponse = await client.get(url, {
118+
params: { course_id: courseId },
119+
});
120+
121+
if (listResponse.data.results.length > 0) {
122+
const existingRecord = listResponse.data.results[0];
123+
await client.patch(`${url}${existingRecord.id}/`, {
124+
is_archived: false,
125+
});
126+
}
127+
128+
// Update local state
129+
setArchivedCourses((prev) => {
130+
const newSet = new Set(prev);
131+
newSet.delete(courseId);
132+
return newSet;
133+
});
134+
} else {
135+
// Archive: Check if record exists first, then create or update
136+
const listResponse = await client.get(url, {
137+
params: { course_id: courseId },
138+
});
139+
140+
if (listResponse.data.results.length > 0) {
141+
// Update existing record
142+
const existingRecord = listResponse.data.results[0];
143+
await client.patch(`${url}${existingRecord.id}/`, {
144+
is_archived: true,
145+
});
146+
} else {
147+
// Create new record
148+
console.log(
149+
`DEBUG: Creating new archive record for course ${courseId}`,
150+
);
151+
const createResponse = await client.post(url, {
152+
course_id: courseId,
153+
is_archived: true,
154+
});
155+
console.log(`DEBUG: Create response:`, createResponse.data);
156+
}
157+
158+
// Update local state
159+
console.log(`DEBUG: Adding course ${courseId} to archived set`);
160+
setArchivedCourses((prev) => {
161+
const newSet = new Set(prev).add(courseId);
162+
console.log(`DEBUG: New archived courses set:`, Array.from(newSet));
163+
return newSet;
164+
});
165+
}
166+
167+
console.log(
168+
`DEBUG: Successfully ${isCurrentlyArchived ? "unarchived" : "archived"} course ${courseId}`,
169+
);
170+
} catch (error) {
171+
console.error(
172+
`Failed to ${isCurrentlyArchived ? "unarchive" : "archive"} course:`,
173+
error,
174+
);
175+
console.error("Error details:", {
176+
status: error.response?.status,
177+
statusText: error.response?.statusText,
178+
data: error.response?.data,
179+
message: error.message,
180+
});
181+
// Could add toast notification here
182+
} finally {
183+
setLoadingStates((prev) => {
184+
const newMap = new Map(prev);
185+
newMap.delete(courseId);
186+
return newMap;
187+
});
188+
}
189+
};
190+
191+
const renderCourse = (courseData, isArchived = false) => {
192+
const courseId = courseData.courseRun?.courseId;
193+
const isLoading = loadingStates.get(courseId);
194+
195+
return (
196+
<Col
197+
key={courseData.cardId}
198+
xs={12}
199+
sm={6}
200+
md={4}
201+
lg={3}
202+
className="mb-4"
203+
>
204+
<Card>
205+
<Card.ImageCap
206+
src={getConfig().LMS_BASE_URL + courseData.course.bannerImgSrc}
207+
alt={courseData.course.courseName}
208+
/>
209+
<Card.Header
210+
title={courseData.course.courseName}
211+
subtitle={courseData.course.courseNumber}
212+
actions={isArchived && <Badge variant="secondary">Archived</Badge>}
213+
/>
214+
<Card.Section>
215+
{courseData.course.shortDescription && (
216+
<p className="text-muted small">
217+
{courseData.course.shortDescription}
218+
</p>
219+
)}
220+
</Card.Section>
221+
<Card.Footer>
222+
<Button
223+
variant={isArchived ? "outline-primary" : "outline-secondary"}
224+
size="sm"
225+
disabled={isLoading}
226+
onClick={() => handleArchiveToggle(courseId, isArchived)}
227+
iconBefore={
228+
isLoading ? Spinner : isArchived ? Unarchive : Archive
229+
}
230+
>
231+
{isLoading
232+
? "Processing..."
233+
: isArchived
234+
? "Unarchive"
235+
: "Archive"}
236+
</Button>
237+
</Card.Footer>
238+
</Card>
239+
</Col>
240+
);
241+
};
114242

115243
return (
116244
<Container fluid>

0 commit comments

Comments
 (0)