Skip to content

Commit 33e19d8

Browse files
authored
Merge pull request #58 from CS3219-AY2425S1/role-based-authorization-spa
Role based authorization spa
2 parents 5272e5d + 2939dff commit 33e19d8

File tree

9 files changed

+223
-84
lines changed

9 files changed

+223
-84
lines changed

frontend/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"codemirror": "^6.0.1",
2626
"primeflex": "^3.3.1",
2727
"primeicons": "^7.0.0",
28-
"primeng": "^17.18.10",
28+
"primeng": "^17.18.11",
2929
"rxjs": "~7.8.0",
3030
"tslib": "^2.3.0",
3131
"typeface-poppins": "^1.1.13",

frontend/src/_interceptors/error.interceptor.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export class ErrorInterceptor implements HttpInterceptor {
99
return next.handle(request).pipe(
1010
catchError(err => {
1111
console.error(err);
12-
1312
const errorMessage = err.error.message;
1413
return throwError(() => new Error(errorMessage, { cause: err }));
1514
}),
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export interface UServRes {
2+
message: string;
3+
data: {
4+
id: string;
5+
username: string;
6+
email: string;
7+
accessToken: string;
8+
isAdmin: boolean;
9+
createdAt: string;
10+
};
11+
}

frontend/src/_services/authentication.service.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,29 @@ export class AuthenticationService extends ApiService {
7474
this.userSubject.next(null);
7575
this.router.navigate(['/account/login']);
7676
}
77+
78+
// get user details from user service for authentication
79+
getUserDetails() {
80+
return this.http.get<UServRes>(`${this.apiUrl}/users/${this.userValue?.id}`, { observe: 'response' }).pipe(
81+
map(response => {
82+
if (response.status === 200) {
83+
let user: User = {} as User;
84+
if (response.body) {
85+
const body: UServRes = response.body;
86+
const data = body.data;
87+
user = {
88+
id: data.id,
89+
username: data.username,
90+
email: data.email,
91+
accessToken: data.accessToken,
92+
isAdmin: data.isAdmin,
93+
createdAt: data.createdAt,
94+
};
95+
}
96+
return user;
97+
}
98+
return null;
99+
}),
100+
);
101+
}
77102
}

frontend/src/app/navigation-bar/navigation-bar.component.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
<ng-template pTemplate="end">
99
@if (user) {
1010
<p-menu #menu [model]="items" [popup]="true" appendTo="body"></p-menu>
11-
<p-button [label]="user.username" (onClick)="menu.toggle($event)" icon="pi pi-user" class="nav-dropdown" />
11+
<p-button
12+
[label]="user.username"
13+
(onClick)="menu.toggle($event)"
14+
icon="pi pi-user"
15+
class="nav-dropdown"></p-button>
1216
} @else {
1317
<div class="flex flex-row gap-2 p-2">
1418
<p-button

frontend/src/app/questions/questions.component.css

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,44 @@
44

55
.p-sortable-column:not(.p-highlight):hover {
66
background-color: #181818 !important;
7+
}
8+
9+
.sliding-panel {
10+
position: fixed;
11+
top: 0;
12+
right: -600px; /* Adjust the width as needed */
13+
width: 600px;
14+
height: 100%;
15+
background-color: #181818 !important;
16+
color: var(--text-color); /* Use theme variable */
17+
box-shadow: -2px 0 5px rgba(0,0,0,0.5);
18+
transition: right 0.3s ease;
19+
z-index: 1000;
20+
}
21+
22+
.sliding-panel.open {
23+
right: 0;
24+
}
25+
26+
.panel-header {
27+
display: flex;
28+
justify-content: space-between;
29+
align-items: center;
30+
padding: 1rem;
31+
background-color: #181818 !important;
32+
border-bottom: 1px solid #000000; /* Use theme variable */
33+
}
34+
35+
.panel-content {
36+
padding: 1rem;
37+
line-height: 1.6; /* Adjust line height for better readability */
38+
color: #ffffff; /* Ensure text color is readable */
39+
}
40+
41+
.panel-content p {
42+
margin-bottom: 1rem; /* Add margin to paragraphs for spacing */
43+
}
44+
45+
tr:hover {
46+
background-color: rgba(0, 0, 0, 0.1);
747
}
Lines changed: 112 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,103 @@
11
<div class="card mx-8">
2-
<p-progressSpinner
3-
class="flex align-items-center justify-content-center h-screen"
4-
*ngIf="loading"
5-
styleClass="w-4rem h-4rem"
6-
strokeWidth="8"
7-
animationDuration=".5s" />
8-
<p-toast />
9-
<ng-container *ngIf="!loading">
10-
<p-toolbar styleClass="mt-4 mb-2 gap-2">
11-
<div class="p-justify-end">
12-
<p-button
13-
icon="pi pi-plus"
14-
severity="primary"
15-
[outlined]="true"
16-
label="New"
17-
class="mr-2"
18-
(onClick)="openNewQuestion()" />
19-
<p-button
20-
icon="pi pi-trash"
21-
severity="danger"
22-
label="Delete"
23-
(onClick)="deleteSelectedQuestions()"
24-
[disabled]="!selectedQuestions || !selectedQuestions.length" />
25-
</div>
26-
</p-toolbar>
27-
<p-table
28-
sortField="id"
29-
[sortOrder]="1"
30-
[columns]="cols"
31-
[value]="questions"
32-
[(selection)]="selectedQuestions"
33-
datakey="id"
34-
[tableStyle]="{ 'min-width': '50rem' }"
35-
[paginator]="true"
36-
[rows]="5"
37-
[rowsPerPageOptions]="[5, 10, 20]"
38-
styleClass="p-datatable-gridlines-striped">
39-
<ng-template pTemplate="caption">
40-
<div class="flex">
41-
<h3 class="m-0">Manage Questions</h3>
2+
@if (loading) {
3+
<p-progressSpinner
4+
class="flex align-items-center justify-content-center h-screen"
5+
styleClass="w-4rem h-4rem"
6+
strokeWidth="8"
7+
animationDuration=".5s" />
8+
<p-toast />
9+
} @else {
10+
<ng-container>
11+
<div
12+
[ngStyle]="
13+
isAdmin ? { 'max-width': '1150px', margin: '0 auto' } : { 'max-width': '900px', margin: '0 auto' }
14+
">
15+
@if (isAdmin) {
16+
<p-toolbar styleClass="mt-4 mb-2 gap-2">
17+
<div class="p-justify-end">
18+
<p-button
19+
icon="pi pi-plus"
20+
severity="primary"
21+
[outlined]="true"
22+
label="New"
23+
class="mr-2"
24+
(onClick)="openNewQuestion()" />
25+
<p-button
26+
icon="pi pi-trash"
27+
severity="danger"
28+
label="Delete"
29+
(onClick)="deleteSelectedQuestions()"
30+
[disabled]="!selectedQuestions || !selectedQuestions.length" />
31+
</div>
32+
</p-toolbar>
33+
} @else {
34+
<div style="height: 50px"></div>
35+
}
36+
<div class="table-container">
37+
<p-table
38+
sortField="id"
39+
[sortOrder]="1"
40+
[columns]="cols"
41+
[value]="questions"
42+
[(selection)]="selectedQuestions"
43+
datakey="id"
44+
[tableStyle]="{ 'table-layout': 'fixed', width: '100%', 'text-align': 'center' }"
45+
[paginator]="true"
46+
[rows]="10"
47+
[rowsPerPageOptions]="[10, 25, 50]"
48+
styleClass="p-datatable-gridlines-striped">
49+
<ng-template pTemplate="caption">
50+
<div class="flex">
51+
<h3 class="m-0">Questions</h3>
52+
</div>
53+
</ng-template>
54+
<ng-template pTemplate="header" let-columns>
55+
<tr>
56+
@if (isAdmin) {
57+
<th style="width: 10%"><p-tableHeaderCheckbox /></th>
58+
<th pSortableColumn="id" style="width: 13%">Id<p-sortIcon field="id" /></th>
59+
<th style="width: 27%">Title</th>
60+
<th style="width: 30%">Topics</th>
61+
<th style="width: 10%">Difficulty</th>
62+
<th style="width: 10%"></th>
63+
} @else {
64+
<th style="width: 15%">Id</th>
65+
<th style="width: 35%">Title</th>
66+
<th style="width: 37%">Topics</th>
67+
<th style="width: 13%">Difficulty</th>
68+
}
69+
</tr>
70+
</ng-template>
71+
<ng-template pTemplate="body" let-question>
72+
<tr (click)="onRowSelect(question)">
73+
@if (isAdmin) {
74+
<td><p-tableCheckbox [value]="question" /></td>
75+
<td>{{ question.id }}</td>
76+
<td>{{ question.title }}</td>
77+
<td>{{ question.topics.join(', ') }}</td>
78+
<td>{{ question.difficulty }}</td>
79+
<td>
80+
<p-button
81+
label="Edit"
82+
severity="primary"
83+
icon="pi pi-file-edit"
84+
class="mr-2"
85+
[text]="true"
86+
(onClick)="editQuestion(question)" />
87+
</td>
88+
} @else {
89+
<td>{{ question.id }}</td>
90+
<td>{{ question.title }}</td>
91+
<td>{{ question.topics.join(', ') }}</td>
92+
<td>{{ question.difficulty }}</td>
93+
}
94+
</tr>
95+
</ng-template>
96+
</p-table>
4297
</div>
43-
</ng-template>
44-
<ng-template pTemplate="header" let-columns>
45-
<tr>
46-
<th style="width: 10%"><p-tableHeaderCheckbox /></th>
47-
<th pSortableColumn="id" style="width: 10%">Id <p-sortIcon field="id" /></th>
48-
<th style="width: 10%">Title</th>
49-
<th style="width: 40%">Description</th>
50-
<th style="width: 10%">Topics</th>
51-
<th style="width: 10%">Difficulty</th>
52-
<th style="width: 10%"></th>
53-
</tr>
54-
</ng-template>
55-
<ng-template pTemplate="body" let-question>
56-
<tr>
57-
<td>
58-
<p-tableCheckbox [value]="question" />
59-
</td>
60-
<td>{{ question.id }}</td>
61-
<td>{{ question.title }}</td>
62-
<td style="white-space: pre-wrap">{{ question.description }}</td>
63-
<td>{{ question.topics.join(', ') }}</td>
64-
<td>{{ question.difficulty }}</td>
65-
<td>
66-
<p-button
67-
label="Edit"
68-
severity="primary"
69-
icon="pi pi-file-edit"
70-
class="mr-2"
71-
[text]="true"
72-
(onClick)="editQuestion(question)" />
73-
</td>
74-
</tr>
75-
</ng-template>
76-
</p-table>
77-
</ng-container>
98+
</div>
99+
</ng-container>
100+
}
78101
<app-question-dialog
79102
[isDialogVisible]="isDialogVisible"
80103
[question]="question"
@@ -85,4 +108,18 @@ <h3 class="m-0">Manage Questions</h3>
85108
(successfulRequest)="onSuccessfulRequest($event)">
86109
</app-question-dialog>
87110
<p-confirmDialog [style]="{ width: '450px' }" />
111+
<div class="sliding-panel" [class.open]="isPanelVisible">
112+
<div class="panel-header">
113+
<h4>{{ clickedOnQuestion?.title }}</h4>
114+
<p-button
115+
icon="pi pi-times"
116+
severity="secondary"
117+
label="Close"
118+
(onClick)="closePanel()"
119+
class="p-button-text" />
120+
</div>
121+
<div class="panel-content">
122+
<p style="white-space: pre-wrap">{{ clickedOnQuestion?.description }}</p>
123+
</div>
124+
</div>
88125
</div>

0 commit comments

Comments
 (0)