Skip to content

Commit af43b83

Browse files
UI improvement: exercise status in sidebar tree
Several UI improvements are introduced: - New icons for the different exercise statuses (not started, in progress and completed) are generated and introduced in the left sidebar. This allows students to quickly know at a glance the status of each of their exercises. - These icons are also introduced in the teacher Dashboard, making it more visual and intuitive. - Information about the status of exercises is added for students in the tooltip displayed when hovering with the mouse over each exercise. In addition, a modification is introduced in the backend regarding the update of the status of the exercises and the tests are updated to support the new changes.
1 parent 37e31d2 commit af43b83

File tree

15 files changed

+106
-36
lines changed

15 files changed

+106
-36
lines changed

vscode4teaching-extension/resources/dashboard/dashboard.css

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
hr {
2+
border: 1px solid var(--vscode-button-background);
3+
margin: 1rem 0;
4+
}
5+
16
table {
27
border-collapse: collapse;
38
width: 100%;
@@ -23,22 +28,28 @@ body.vscode-dark td {
2328
}
2429

2530

31+
.status-icon-img {
32+
height: 32px;
33+
}
34+
2635
.not-started-cell {
27-
background-color: var(--vscode-diffEditor-removedTextBackground);
36+
background-color: rgba(239, 83, 80, 0.2);
2837
}
2938

3039
.finished-cell {
31-
background-color: var(--vscode-diffEditor-insertedTextBackground);
40+
background-color: rgba(102, 187, 106, 0.2);
3241
}
3342

34-
.onprogress-cell {
35-
background-color: rgba(76, 149, 218, 0.644);
43+
.inprogress-cell {
44+
background-color: rgba(249, 168, 37, 0.2);
3645
}
3746

47+
3848
button {
3949
background-color: var(--vscode-button-background);
4050
color: var(--vscode-button-foreground);
4151
border: none;
52+
padding: 0.25rem 1rem;
4253
margin-right: 0.5rem;
4354
}
4455

8.91 KB
Loading
8.78 KB
Loading
7.79 KB
Loading
1.86 KB
Loading
1.75 KB
Loading
1.57 KB
Loading

vscode4teaching-extension/src/components/courses/CoursesTreeProvider.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { V4TItemType } from "./V4TItem/V4TItemType";
1414
import { Validators } from "./Validators";
1515
import * as fs from "fs";
1616
import * as path from "path";
17+
import { ExerciseUserInfo } from "../../model/serverModel/exercise/ExerciseUserInfo";
1718

1819
/**
1920
* Tree view that lists extension's basic options like:
@@ -513,15 +514,29 @@ export class CoursesProvider implements vscode.TreeDataProvider<V4TItem> {
513514
type = V4TItemType.ExerciseStudent;
514515
commandName = "vscode4teaching.getexercisefiles";
515516
}
516-
const exerciseItems = course.exercises.map(
517-
(exercise) =>
518-
new V4TItem(exercise.name, type, vscode.TreeItemCollapsibleState.None, element, exercise, {
519-
command: commandName,
520-
title: "Get exercise files",
521-
arguments: [course ? course.name : null, exercise],
522-
})
523-
);
524-
return exerciseItems.length > 0 ? exerciseItems : [V4TBuildItems.NO_EXERCISES_ITEM];
517+
if (course.exercises.length > 0){
518+
if (ModelUtils.isStudent(CurrentUser.getUserInfo())){
519+
return await Promise.all(course.exercises.map(
520+
async (exercise) => {
521+
const eui: ExerciseUserInfo = (await APIClient.getExerciseUserInfo(exercise.id)).data;
522+
return new V4TItem(exercise.name, type, vscode.TreeItemCollapsibleState.None, element, exercise, {
523+
command: commandName,
524+
title: "Get exercise files",
525+
arguments: [course ? course.name : null, exercise],
526+
}, eui.status);
527+
}
528+
));
529+
} else {
530+
return course.exercises.map(
531+
(exercise) =>
532+
new V4TItem(exercise.name, type, vscode.TreeItemCollapsibleState.None, element, exercise, {
533+
command: commandName,
534+
title: "Get exercise files",
535+
arguments: [course ? course.name : null, exercise],
536+
})
537+
);
538+
}
539+
}
525540
}
526541
}
527542
return [V4TBuildItems.NO_EXERCISES_ITEM];

vscode4teaching-extension/src/components/courses/V4TItem/V4TItem.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export class V4TItem extends vscode.TreeItem {
1313
path.join(__dirname, "..", "resources") :
1414
path.join(__dirname, "..", "..", "..", "..", "resources");
1515

16+
public tooltip = this.getTooltip();
1617
public iconPath = this.getIconPath();
1718
public contextValue = this.type.toString();
1819

@@ -23,36 +24,64 @@ export class V4TItem extends vscode.TreeItem {
2324
readonly parent?: V4TItem,
2425
readonly item?: Course | Exercise,
2526
readonly command?: vscode.Command,
27+
readonly exerciseStatusOrHasSolution?: number
2628
) {
2729
super(label, collapsibleState);
2830
}
2931

32+
private getTooltip() {
33+
if (this.exerciseStatusOrHasSolution !== undefined && this.type === V4TItemType.ExerciseStudent) {
34+
switch (this.exerciseStatusOrHasSolution){
35+
case 0:
36+
return this.label + " (not started)";
37+
case 1:
38+
return this.label + " (finished)";
39+
case 2:
40+
return this.label + " (in progress)";
41+
}
42+
}
43+
return this.label;
44+
}
45+
3046
private getIconPath() {
3147
switch (this.type) {
3248
case V4TItemType.Login: {
33-
return this.iconPaths("login.png");
49+
return this.getLightDarkPaths("login.png");
50+
}
51+
case V4TItemType.ExerciseStudent: {
52+
if (this.exerciseStatusOrHasSolution !== undefined){
53+
switch (this.exerciseStatusOrHasSolution){
54+
case 0:
55+
return path.join(this.resourcesPath, "exercises_status_treeicons", "exercise_not_started.png");
56+
case 1:
57+
return path.join(this.resourcesPath, "exercises_status_treeicons", "exercise_finished.png");
58+
case 2:
59+
return path.join(this.resourcesPath, "exercises_status_treeicons", "exercise_in_progress.png");
60+
}
61+
}
62+
return this.getLightDarkPaths("noicon.png");
3463
}
3564
case V4TItemType.AddCourse: {
36-
return this.iconPaths("add.png");
65+
return this.getLightDarkPaths("add.png");
3766
}
3867
case V4TItemType.GetWithCode: {
39-
return this.iconPaths("link.png");
68+
return this.getLightDarkPaths("link.png");
4069
}
4170
case V4TItemType.Signup: // fall through case below
4271
case V4TItemType.SignupTeacher: {
43-
return this.iconPaths("add_user.png");
72+
return this.getLightDarkPaths("add_user.png");
4473
}
4574
case V4TItemType.Logout: {
46-
return this.iconPaths("logout.png");
75+
return this.getLightDarkPaths("logout.png");
4776
}
4877
case V4TItemType.NoCourses: // fall through case below
4978
case V4TItemType.NoExercises: {
50-
return this.iconPaths("noicon.png");
79+
return this.getLightDarkPaths("noicon.png");
5180
}
5281
}
5382
}
5483

55-
private iconPaths(iconPath: string) {
84+
private getLightDarkPaths(iconPath: string) {
5685
return {
5786
light: path.join(this.resourcesPath, "light", iconPath),
5887
dark: path.join(this.resourcesPath, "dark", iconPath),

vscode4teaching-extension/src/components/dashboard/DashboardWebview.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,12 @@ export class DashboardWebview {
287287

288288
// Local path to styles
289289
const cssPath = vscode.Uri.file(path.join(DashboardWebview.resourcesPath, "dashboard.css"));
290-
291290
// Styles uri
292291
const cssUri = this.panel.webview.asWebviewUri(cssPath);
293292

293+
// Local URI to images (function)
294+
const imgUri = (imgName: string) => this.panel.webview.asWebviewUri(vscode.Uri.file(path.join(DashboardWebview.resourcesPath, "img", imgName + ".png")));
295+
294296
// Transform EUIs to html table data
295297
let rows: string = "";
296298
// for (const eui of this._euis) {
@@ -308,17 +310,17 @@ export class DashboardWebview {
308310
switch (eui.status) {
309311
case 0: {
310312
// not started
311-
rows = rows + '<td class="not-started-cell">Not started</td>\n';
313+
rows = rows + `<td class="not-started-cell"><img src="${imgUri("exercise_not_started")}" alt="Not started icon" class="status-icon-img"><div>Not started</div></td>\n`;
312314
break;
313315
}
314316
case 1: {
315317
// finished
316-
rows = rows + '<td class="finished-cell">Finished</td>\n';
318+
rows = rows + `<td class="finished-cell"><img src="${imgUri("exercise_finished")}" alt="Finished icon" class="status-icon-img"><div>Finished</div></td>\n`;
317319
break;
318320
}
319321
case 2: {
320322
// started but not finished
321-
rows = rows + '<td class="onprogress-cell">On progress</td>\n';
323+
rows = rows + `<td class="inprogress-cell"><img src="${imgUri("exercise_in_progress")}" alt="In progress icon" class="status-icon-img"><div>In progress</div></td>\n`;
322324
break;
323325
}
324326
}
@@ -365,7 +367,6 @@ export class DashboardWebview {
365367
<div class="alert-info"><strong>Warning</strong>: There are no students registered in this course.</div>
366368
` : `
367369
<hr/>
368-
<br/>
369370
<table>
370371
<tr>
371372
${

0 commit comments

Comments
 (0)