Skip to content

Commit

Permalink
release: v0.40.0 (#242)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
PedroChaparro authored Jan 13, 2024
1 parent eaf0d26 commit 0f61db3
Show file tree
Hide file tree
Showing 28 changed files with 719 additions and 124 deletions.
20 changes: 10 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
# [0.37.0](https://github.com/upb-code-labs/react-client/compare/v0.36.0...v0.37.0) (2024-01-12)
# [0.40.0](https://github.com/upb-code-labs/react-client/compare/v0.39.0...v0.40.0) (2024-01-13)


### Features

* 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))
* 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))



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


### Features

* 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))
* Delete rubric ([#240](https://github.com/upb-code-labs/react-client/issues/240)) ([53a0984](https://github.com/upb-code-labs/react-client/commit/53a0984a8182ce747f956f270c88a7a5e7f7ff00))



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


### Features

* 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))
* 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))



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


### Features

* 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))
* 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))



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


### Features

* 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))
* 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))



72 changes: 69 additions & 3 deletions e2e/courses/EnrollStudent.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,83 @@ test.describe.serial("Enroll student workflow", () => {

// Assert the student is listed
const studentRow = page.getByRole("row", {
name: new RegExp(studentFullName)
name: new RegExp(`^\\s*${studentFullName}`),
exact: true
});

await expect(studentRow).toBeVisible();
await expect(
studentRow.getByRole("cell", { name: studentFullName })
studentRow.getByRole("cell", { name: studentFullName, exact: true })
).toBeVisible();
await expect(
studentRow.getByRole("cell", { name: studentInstitutionalID })
).toBeVisible();
await expect(
studentRow.getByRole("button", { name: "Deactivate", exact: true })
studentRow.getByLabel(`Deactivate ${studentFullName}`, { exact: true })
).toBeVisible();
});

test("Deactivate student in test course", async ({ page }) => {
// Login as a teacher
await page.goto("/login");
await page.getByLabel("Email").fill(teacherEmail);
await page.getByLabel("Password").fill(teacherPassword);
await page.getByRole("button", { name: "Submit" }).click();

// Go to the participants view
await page.getByRole("link", { name: courseName }).click();
await page
.getByRole("link", { name: "Manage participants", exact: true })
.click();

// Deactivate the student
const studentRow = page.getByRole("row", {
name: new RegExp(`^\\s*${studentFullName}`),
exact: true
});
const deactivateStudentButton = studentRow.getByLabel(
`Deactivate ${studentFullName}`,
{ exact: true }
);

await expect(deactivateStudentButton).toBeVisible();
await deactivateStudentButton.click();

// Assert an alert is shown
await expect(
page.getByText("Student deactivated successfully")
).toBeVisible();
});

test("Activate student in test course", async ({ page }) => {
// Login as a teacher
await page.goto("/login");
await page.getByLabel("Email").fill(teacherEmail);
await page.getByLabel("Password").fill(teacherPassword);
await page.getByRole("button", { name: "Submit" }).click();

// Go to the participants view
await page.getByRole("link", { name: courseName }).click();
await page
.getByRole("link", { name: "Manage participants", exact: true })
.click();

// Activate the student
const studentRow = page.getByRole("row", {
name: new RegExp(`^\\s*${studentFullName}`),
exact: true
});
const activateStudentButton = studentRow.getByLabel(
`Activate ${studentFullName}`,
{ exact: true }
);

await expect(activateStudentButton).toBeVisible();
await activateStudentButton.click();

// Assert an alert is shown
await expect(
page.getByText("Student activated successfully")
).toBeVisible();
});
});
7 changes: 4 additions & 3 deletions e2e/courses/JoinCourse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,17 +203,18 @@ test.describe.serial("Join courses workflow", () => {

// Assert the student is listed
const studentRow = page.getByRole("row", {
name: new RegExp(studentFullName)
name: new RegExp(`^\\s*${studentFullName}`),
exact: true
});
await expect(studentRow).toBeVisible();
await expect(
studentRow.getByRole("cell", { name: studentFullName })
studentRow.getByRole("cell", { name: studentFullName, exact: true })
).toBeVisible();
await expect(
studentRow.getByRole("cell", { name: studentInstitutionalID })
).toBeVisible();
await expect(
studentRow.getByRole("button", { name: "Deactivate", exact: true })
studentRow.getByLabel(`Deactivate ${studentFullName}`, { exact: true })
).toBeVisible();
});
});
45 changes: 45 additions & 0 deletions e2e/rubrics/CreateRubric.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,50 @@ test.describe.serial("Rubrics creation workflow", () => {
await expect(
rubricRow.getByRole("link", { name: `Edit ${rubricName}`, exact: true })
).toBeVisible();
await expect(
rubricRow.getByRole("button", {
name: `Delete ${rubricName}`,
exact: true
})
).toBeVisible();
});

test("Teachers can delete rubrics", async ({ page }) => {
// Login as the teacher
await page.goto("/login");
await page.getByLabel("Email").fill(teacherEmail);
await page.getByLabel("Password").fill(teacherPassword);
await page.getByRole("button", { name: "Submit" }).click();

// Go to the rubrics page
await page.getByRole("link", { name: "Rubrics", exact: true }).click();
await page.waitForURL(/\/rubrics$/);

// Delete the rubric
await page
.getByRole("button", { name: `Delete ${rubricName}`, exact: true })
.click();

// Assert the confirmation dialog is open
const confirmationModalText =
"Are you sure you want to delete this rubric?";
await expect(
page.getByText(confirmationModalText, { exact: true })
).toBeVisible();

// Confirm the deletion
await page.getByRole("button", { name: "Proceed", exact: true }).click();

// Assert the alert is shown
await expect(page.getByText("The rubric has been deleted")).toBeVisible();

// Assert the rubric is not shown in the table
const rubricRow = page.getByRole("row", {
name: /Data structures rubric/i
});
await expect(rubricRow).not.toBeVisible();

// Assert the empty state is shown
await expect(page.getByText("No results.")).toBeVisible();
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-client",
"private": true,
"version": "0.37.0",
"version": "0.40.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
1 change: 1 addition & 0 deletions public/images/error-image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
64 changes: 64 additions & 0 deletions src/components/CustomError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { buttonVariants } from "@/components/ui/button";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
import { ExternalLinkIcon } from "lucide-react";
import { Link } from "react-router-dom";

interface CustomErrorProps {
className?: string;
title?: string;
message?: string;
showFooter?: boolean;
redirectText?: string;
redirectTo?: string;
}

export const CustomError = ({
className,
title = "Oops! We had an error",
message = "Sorry for the inconvenience.",
showFooter = true,
redirectText = "Go back to home",
redirectTo = "/"
}: CustomErrorProps) => {
message = message.endsWith(".") ? message : `${message}.`;

return (
<Card className={`mx-auto w-full max-w-sm ${className}`}>
<CardContent className="space-y-4 py-4 text-center">
<img
src="/images/error-image.svg"
alt="Error image"
width={382}
height={238}
/>
<h2 className="text-2xl font-bold">{title}</h2>
<p>
{message} Try again or report this error in the{" "}
<a
href="https://github.com/upb-code-labs/react-client/issues"
target="_blank"
rel="noopener noreferrer"
className="text-red-upb"
>
GitHub repository{" "}
<ExternalLinkIcon className="inline-block" size={16} />.
</a>
</p>
</CardContent>
{showFooter && (
<CardFooter>
<Link
to={redirectTo}
className={buttonVariants({
variant: "destructive",
className: "w-full bg-red-upb",
size: "lg"
})}
>
{redirectText}
</Link>
</CardFooter>
)}
</Card>
);
};
File renamed without changes.
85 changes: 85 additions & 0 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { cn } from "@/lib/utils";
import * as React from "react";

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
));
Card.displayName = "Card";

const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
));
CardHeader.displayName = "CardHeader";

const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
));
CardTitle.displayName = "CardTitle";

const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
CardDescription.displayName = "CardDescription";

const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
));
CardContent.displayName = "CardContent";

const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
));
CardFooter.displayName = "CardFooter";

export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent
};
Loading

0 comments on commit 0f61db3

Please sign in to comment.