Skip to content

Commit 0f61db3

Browse files
release: v0.40.0 (#242)
* feat: Custom error component (#237) * feat: Delete rubric (#240) * feat: Delete rubric * test: Add test to ensure teachers can delete rubrics * feat: Toggle enrolled students status (#241) * feat: Toggle enrolled students status * test: Add tests to ensure teachers can activate and deactivate enrolled students
1 parent eaf0d26 commit 0f61db3

28 files changed

Lines changed: 719 additions & 124 deletions

CHANGELOG.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,45 @@
1-
# [0.37.0](https://github.com/upb-code-labs/react-client/compare/v0.36.0...v0.37.0) (2024-01-12)
1+
# [0.40.0](https://github.com/upb-code-labs/react-client/compare/v0.39.0...v0.40.0) (2024-01-13)
22

33

44
### Features
55

6-
* Laboratory progress and statistics ([#232](https://github.com/upb-code-labs/react-client/issues/232)) ([52406a5](https://github.com/upb-code-labs/react-client/commit/52406a57c8474e8d9d2f5de10bb46ed3048fe8ab))
6+
* Toggle enrolled students status ([#241](https://github.com/upb-code-labs/react-client/issues/241)) ([cd5c754](https://github.com/upb-code-labs/react-client/commit/cd5c7541c254597852aadfe3f96a1a13c156f871))
77

88

99

10-
# [0.36.0](https://github.com/upb-code-labs/react-client/compare/v0.35.0...v0.36.0) (2024-01-09)
10+
# [0.39.0](https://github.com/upb-code-labs/react-client/compare/v0.38.0...v0.39.0) (2024-01-13)
1111

1212

1313
### Features
1414

15-
* Get submission status ([#227](https://github.com/upb-code-labs/react-client/issues/227)) ([feff79c](https://github.com/upb-code-labs/react-client/commit/feff79c31521fdeb2bb430fadc9671a10f048256))
15+
* Delete rubric ([#240](https://github.com/upb-code-labs/react-client/issues/240)) ([53a0984](https://github.com/upb-code-labs/react-client/commit/53a0984a8182ce747f956f270c88a7a5e7f7ff00))
1616

1717

1818

19-
# [0.35.0](https://github.com/upb-code-labs/react-client/compare/v0.34.0...v0.35.0) (2024-01-08)
19+
# [0.38.0](https://github.com/upb-code-labs/react-client/compare/v0.37.0...v0.38.0) (2024-01-13)
2020

2121

2222
### Features
2323

24-
* Submit to test block ([#221](https://github.com/upb-code-labs/react-client/issues/221)) ([4ce7d19](https://github.com/upb-code-labs/react-client/commit/4ce7d196ae097374efe71c5b4751f1a8aac3543e))
24+
* Custom error component ([#237](https://github.com/upb-code-labs/react-client/issues/237)) ([56d5733](https://github.com/upb-code-labs/react-client/commit/56d57331c02e0798b06cce2912203065d4d8b589))
2525

2626

2727

28-
# [0.34.0](https://github.com/upb-code-labs/react-client/compare/v0.33.0...v0.34.0) (2024-01-08)
28+
# [0.37.0](https://github.com/upb-code-labs/react-client/compare/v0.36.0...v0.37.0) (2024-01-12)
2929

3030

3131
### Features
3232

33-
* Update test blocks ([#220](https://github.com/upb-code-labs/react-client/issues/220)) ([147f3db](https://github.com/upb-code-labs/react-client/commit/147f3db5efaa1ca325e8b9ac7dd8e66981c79ec8))
33+
* Laboratory progress and statistics ([#232](https://github.com/upb-code-labs/react-client/issues/232)) ([52406a5](https://github.com/upb-code-labs/react-client/commit/52406a57c8474e8d9d2f5de10bb46ed3048fe8ab))
3434

3535

3636

37-
# [0.33.0](https://github.com/upb-code-labs/react-client/compare/v0.32.0...v0.33.0) (2024-01-01)
37+
# [0.36.0](https://github.com/upb-code-labs/react-client/compare/v0.35.0...v0.36.0) (2024-01-09)
3838

3939

4040
### Features
4141

42-
* Show laboratory content to students ([#211](https://github.com/upb-code-labs/react-client/issues/211)) ([6d7c2e4](https://github.com/upb-code-labs/react-client/commit/6d7c2e45527ca3d0f3122d5a6ffbd3d43f4495af))
42+
* Get submission status ([#227](https://github.com/upb-code-labs/react-client/issues/227)) ([feff79c](https://github.com/upb-code-labs/react-client/commit/feff79c31521fdeb2bb430fadc9671a10f048256))
4343

4444

4545

e2e/courses/EnrollStudent.spec.ts

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,83 @@ test.describe.serial("Enroll student workflow", () => {
100100

101101
// Assert the student is listed
102102
const studentRow = page.getByRole("row", {
103-
name: new RegExp(studentFullName)
103+
name: new RegExp(`^\\s*${studentFullName}`),
104+
exact: true
104105
});
106+
105107
await expect(studentRow).toBeVisible();
106108
await expect(
107-
studentRow.getByRole("cell", { name: studentFullName })
109+
studentRow.getByRole("cell", { name: studentFullName, exact: true })
108110
).toBeVisible();
109111
await expect(
110112
studentRow.getByRole("cell", { name: studentInstitutionalID })
111113
).toBeVisible();
112114
await expect(
113-
studentRow.getByRole("button", { name: "Deactivate", exact: true })
115+
studentRow.getByLabel(`Deactivate ${studentFullName}`, { exact: true })
116+
).toBeVisible();
117+
});
118+
119+
test("Deactivate student in test course", async ({ page }) => {
120+
// Login as a teacher
121+
await page.goto("/login");
122+
await page.getByLabel("Email").fill(teacherEmail);
123+
await page.getByLabel("Password").fill(teacherPassword);
124+
await page.getByRole("button", { name: "Submit" }).click();
125+
126+
// Go to the participants view
127+
await page.getByRole("link", { name: courseName }).click();
128+
await page
129+
.getByRole("link", { name: "Manage participants", exact: true })
130+
.click();
131+
132+
// Deactivate the student
133+
const studentRow = page.getByRole("row", {
134+
name: new RegExp(`^\\s*${studentFullName}`),
135+
exact: true
136+
});
137+
const deactivateStudentButton = studentRow.getByLabel(
138+
`Deactivate ${studentFullName}`,
139+
{ exact: true }
140+
);
141+
142+
await expect(deactivateStudentButton).toBeVisible();
143+
await deactivateStudentButton.click();
144+
145+
// Assert an alert is shown
146+
await expect(
147+
page.getByText("Student deactivated successfully")
148+
).toBeVisible();
149+
});
150+
151+
test("Activate student in test course", async ({ page }) => {
152+
// Login as a teacher
153+
await page.goto("/login");
154+
await page.getByLabel("Email").fill(teacherEmail);
155+
await page.getByLabel("Password").fill(teacherPassword);
156+
await page.getByRole("button", { name: "Submit" }).click();
157+
158+
// Go to the participants view
159+
await page.getByRole("link", { name: courseName }).click();
160+
await page
161+
.getByRole("link", { name: "Manage participants", exact: true })
162+
.click();
163+
164+
// Activate the student
165+
const studentRow = page.getByRole("row", {
166+
name: new RegExp(`^\\s*${studentFullName}`),
167+
exact: true
168+
});
169+
const activateStudentButton = studentRow.getByLabel(
170+
`Activate ${studentFullName}`,
171+
{ exact: true }
172+
);
173+
174+
await expect(activateStudentButton).toBeVisible();
175+
await activateStudentButton.click();
176+
177+
// Assert an alert is shown
178+
await expect(
179+
page.getByText("Student activated successfully")
114180
).toBeVisible();
115181
});
116182
});

e2e/courses/JoinCourse.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,18 @@ test.describe.serial("Join courses workflow", () => {
203203

204204
// Assert the student is listed
205205
const studentRow = page.getByRole("row", {
206-
name: new RegExp(studentFullName)
206+
name: new RegExp(`^\\s*${studentFullName}`),
207+
exact: true
207208
});
208209
await expect(studentRow).toBeVisible();
209210
await expect(
210-
studentRow.getByRole("cell", { name: studentFullName })
211+
studentRow.getByRole("cell", { name: studentFullName, exact: true })
211212
).toBeVisible();
212213
await expect(
213214
studentRow.getByRole("cell", { name: studentInstitutionalID })
214215
).toBeVisible();
215216
await expect(
216-
studentRow.getByRole("button", { name: "Deactivate", exact: true })
217+
studentRow.getByLabel(`Deactivate ${studentFullName}`, { exact: true })
217218
).toBeVisible();
218219
});
219220
});

e2e/rubrics/CreateRubric.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,50 @@ test.describe.serial("Rubrics creation workflow", () => {
9090
await expect(
9191
rubricRow.getByRole("link", { name: `Edit ${rubricName}`, exact: true })
9292
).toBeVisible();
93+
await expect(
94+
rubricRow.getByRole("button", {
95+
name: `Delete ${rubricName}`,
96+
exact: true
97+
})
98+
).toBeVisible();
99+
});
100+
101+
test("Teachers can delete rubrics", async ({ page }) => {
102+
// Login as the teacher
103+
await page.goto("/login");
104+
await page.getByLabel("Email").fill(teacherEmail);
105+
await page.getByLabel("Password").fill(teacherPassword);
106+
await page.getByRole("button", { name: "Submit" }).click();
107+
108+
// Go to the rubrics page
109+
await page.getByRole("link", { name: "Rubrics", exact: true }).click();
110+
await page.waitForURL(/\/rubrics$/);
111+
112+
// Delete the rubric
113+
await page
114+
.getByRole("button", { name: `Delete ${rubricName}`, exact: true })
115+
.click();
116+
117+
// Assert the confirmation dialog is open
118+
const confirmationModalText =
119+
"Are you sure you want to delete this rubric?";
120+
await expect(
121+
page.getByText(confirmationModalText, { exact: true })
122+
).toBeVisible();
123+
124+
// Confirm the deletion
125+
await page.getByRole("button", { name: "Proceed", exact: true }).click();
126+
127+
// Assert the alert is shown
128+
await expect(page.getByText("The rubric has been deleted")).toBeVisible();
129+
130+
// Assert the rubric is not shown in the table
131+
const rubricRow = page.getByRole("row", {
132+
name: /Data structures rubric/i
133+
});
134+
await expect(rubricRow).not.toBeVisible();
135+
136+
// Assert the empty state is shown
137+
await expect(page.getByText("No results.")).toBeVisible();
93138
});
94139
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "react-client",
33
"private": true,
4-
"version": "0.37.0",
4+
"version": "0.40.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

public/images/error-image.svg

Lines changed: 1 addition & 0 deletions
Loading

src/components/CustomError.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { buttonVariants } from "@/components/ui/button";
2+
import { Card, CardContent, CardFooter } from "@/components/ui/card";
3+
import { ExternalLinkIcon } from "lucide-react";
4+
import { Link } from "react-router-dom";
5+
6+
interface CustomErrorProps {
7+
className?: string;
8+
title?: string;
9+
message?: string;
10+
showFooter?: boolean;
11+
redirectText?: string;
12+
redirectTo?: string;
13+
}
14+
15+
export const CustomError = ({
16+
className,
17+
title = "Oops! We had an error",
18+
message = "Sorry for the inconvenience.",
19+
showFooter = true,
20+
redirectText = "Go back to home",
21+
redirectTo = "/"
22+
}: CustomErrorProps) => {
23+
message = message.endsWith(".") ? message : `${message}.`;
24+
25+
return (
26+
<Card className={`mx-auto w-full max-w-sm ${className}`}>
27+
<CardContent className="space-y-4 py-4 text-center">
28+
<img
29+
src="/images/error-image.svg"
30+
alt="Error image"
31+
width={382}
32+
height={238}
33+
/>
34+
<h2 className="text-2xl font-bold">{title}</h2>
35+
<p>
36+
{message} Try again or report this error in the{" "}
37+
<a
38+
href="https://github.com/upb-code-labs/react-client/issues"
39+
target="_blank"
40+
rel="noopener noreferrer"
41+
className="text-red-upb"
42+
>
43+
GitHub repository{" "}
44+
<ExternalLinkIcon className="inline-block" size={16} />.
45+
</a>
46+
</p>
47+
</CardContent>
48+
{showFooter && (
49+
<CardFooter>
50+
<Link
51+
to={redirectTo}
52+
className={buttonVariants({
53+
variant: "destructive",
54+
className: "w-full bg-red-upb",
55+
size: "lg"
56+
})}
57+
>
58+
{redirectText}
59+
</Link>
60+
</CardFooter>
61+
)}
62+
</Card>
63+
);
64+
};

src/components/ui/card.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { cn } from "@/lib/utils";
2+
import * as React from "react";
3+
4+
const Card = React.forwardRef<
5+
HTMLDivElement,
6+
React.HTMLAttributes<HTMLDivElement>
7+
>(({ className, ...props }, ref) => (
8+
<div
9+
ref={ref}
10+
className={cn(
11+
"rounded-lg border bg-card text-card-foreground shadow-sm",
12+
className
13+
)}
14+
{...props}
15+
/>
16+
));
17+
Card.displayName = "Card";
18+
19+
const CardHeader = React.forwardRef<
20+
HTMLDivElement,
21+
React.HTMLAttributes<HTMLDivElement>
22+
>(({ className, ...props }, ref) => (
23+
<div
24+
ref={ref}
25+
className={cn("flex flex-col space-y-1.5 p-6", className)}
26+
{...props}
27+
/>
28+
));
29+
CardHeader.displayName = "CardHeader";
30+
31+
const CardTitle = React.forwardRef<
32+
HTMLParagraphElement,
33+
React.HTMLAttributes<HTMLHeadingElement>
34+
>(({ className, ...props }, ref) => (
35+
<h3
36+
ref={ref}
37+
className={cn(
38+
"text-2xl font-semibold leading-none tracking-tight",
39+
className
40+
)}
41+
{...props}
42+
/>
43+
));
44+
CardTitle.displayName = "CardTitle";
45+
46+
const CardDescription = React.forwardRef<
47+
HTMLParagraphElement,
48+
React.HTMLAttributes<HTMLParagraphElement>
49+
>(({ className, ...props }, ref) => (
50+
<p
51+
ref={ref}
52+
className={cn("text-sm text-muted-foreground", className)}
53+
{...props}
54+
/>
55+
));
56+
CardDescription.displayName = "CardDescription";
57+
58+
const CardContent = React.forwardRef<
59+
HTMLDivElement,
60+
React.HTMLAttributes<HTMLDivElement>
61+
>(({ className, ...props }, ref) => (
62+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
63+
));
64+
CardContent.displayName = "CardContent";
65+
66+
const CardFooter = React.forwardRef<
67+
HTMLDivElement,
68+
React.HTMLAttributes<HTMLDivElement>
69+
>(({ className, ...props }, ref) => (
70+
<div
71+
ref={ref}
72+
className={cn("flex items-center p-6 pt-0", className)}
73+
{...props}
74+
/>
75+
));
76+
CardFooter.displayName = "CardFooter";
77+
78+
export {
79+
Card,
80+
CardHeader,
81+
CardFooter,
82+
CardTitle,
83+
CardDescription,
84+
CardContent
85+
};

0 commit comments

Comments
 (0)