Skip to content

Commit

Permalink
Merge pull request #58 from CS3219-AY2425S1/role-based-authorization-spa
Browse files Browse the repository at this point in the history
Role based authorization spa
  • Loading branch information
LimZiJia authored Nov 2, 2024
2 parents 5272e5d + 2939dff commit 33e19d8
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 84 deletions.
2 changes: 1 addition & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"codemirror": "^6.0.1",
"primeflex": "^3.3.1",
"primeicons": "^7.0.0",
"primeng": "^17.18.10",
"primeng": "^17.18.11",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"typeface-poppins": "^1.1.13",
Expand Down
1 change: 0 additions & 1 deletion frontend/src/_interceptors/error.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export class ErrorInterceptor implements HttpInterceptor {
return next.handle(request).pipe(
catchError(err => {
console.error(err);

const errorMessage = err.error.message;
return throwError(() => new Error(errorMessage, { cause: err }));
}),
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/_models/user.service.response.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface UServRes {
message: string;
data: {
id: string;
username: string;
email: string;
accessToken: string;
isAdmin: boolean;
createdAt: string;
};
}
25 changes: 25 additions & 0 deletions frontend/src/_services/authentication.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,29 @@ export class AuthenticationService extends ApiService {
this.userSubject.next(null);
this.router.navigate(['/account/login']);
}

// get user details from user service for authentication
getUserDetails() {
return this.http.get<UServRes>(`${this.apiUrl}/users/${this.userValue?.id}`, { observe: 'response' }).pipe(
map(response => {
if (response.status === 200) {
let user: User = {} as User;
if (response.body) {
const body: UServRes = response.body;
const data = body.data;
user = {
id: data.id,
username: data.username,
email: data.email,
accessToken: data.accessToken,
isAdmin: data.isAdmin,
createdAt: data.createdAt,
};
}
return user;
}
return null;
}),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
<ng-template pTemplate="end">
@if (user) {
<p-menu #menu [model]="items" [popup]="true" appendTo="body"></p-menu>
<p-button [label]="user.username" (onClick)="menu.toggle($event)" icon="pi pi-user" class="nav-dropdown" />
<p-button
[label]="user.username"
(onClick)="menu.toggle($event)"
icon="pi pi-user"
class="nav-dropdown"></p-button>
} @else {
<div class="flex flex-row gap-2 p-2">
<p-button
Expand Down
40 changes: 40 additions & 0 deletions frontend/src/app/questions/questions.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,44 @@

.p-sortable-column:not(.p-highlight):hover {
background-color: #181818 !important;
}

.sliding-panel {
position: fixed;
top: 0;
right: -600px; /* Adjust the width as needed */
width: 600px;
height: 100%;
background-color: #181818 !important;
color: var(--text-color); /* Use theme variable */
box-shadow: -2px 0 5px rgba(0,0,0,0.5);
transition: right 0.3s ease;
z-index: 1000;
}

.sliding-panel.open {
right: 0;
}

.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background-color: #181818 !important;
border-bottom: 1px solid #000000; /* Use theme variable */
}

.panel-content {
padding: 1rem;
line-height: 1.6; /* Adjust line height for better readability */
color: #ffffff; /* Ensure text color is readable */
}

.panel-content p {
margin-bottom: 1rem; /* Add margin to paragraphs for spacing */
}

tr:hover {
background-color: rgba(0, 0, 0, 0.1);
}
187 changes: 112 additions & 75 deletions frontend/src/app/questions/questions.component.html
Original file line number Diff line number Diff line change
@@ -1,80 +1,103 @@
<div class="card mx-8">
<p-progressSpinner
class="flex align-items-center justify-content-center h-screen"
*ngIf="loading"
styleClass="w-4rem h-4rem"
strokeWidth="8"
animationDuration=".5s" />
<p-toast />
<ng-container *ngIf="!loading">
<p-toolbar styleClass="mt-4 mb-2 gap-2">
<div class="p-justify-end">
<p-button
icon="pi pi-plus"
severity="primary"
[outlined]="true"
label="New"
class="mr-2"
(onClick)="openNewQuestion()" />
<p-button
icon="pi pi-trash"
severity="danger"
label="Delete"
(onClick)="deleteSelectedQuestions()"
[disabled]="!selectedQuestions || !selectedQuestions.length" />
</div>
</p-toolbar>
<p-table
sortField="id"
[sortOrder]="1"
[columns]="cols"
[value]="questions"
[(selection)]="selectedQuestions"
datakey="id"
[tableStyle]="{ 'min-width': '50rem' }"
[paginator]="true"
[rows]="5"
[rowsPerPageOptions]="[5, 10, 20]"
styleClass="p-datatable-gridlines-striped">
<ng-template pTemplate="caption">
<div class="flex">
<h3 class="m-0">Manage Questions</h3>
@if (loading) {
<p-progressSpinner
class="flex align-items-center justify-content-center h-screen"
styleClass="w-4rem h-4rem"
strokeWidth="8"
animationDuration=".5s" />
<p-toast />
} @else {
<ng-container>
<div
[ngStyle]="
isAdmin ? { 'max-width': '1150px', margin: '0 auto' } : { 'max-width': '900px', margin: '0 auto' }
">
@if (isAdmin) {
<p-toolbar styleClass="mt-4 mb-2 gap-2">
<div class="p-justify-end">
<p-button
icon="pi pi-plus"
severity="primary"
[outlined]="true"
label="New"
class="mr-2"
(onClick)="openNewQuestion()" />
<p-button
icon="pi pi-trash"
severity="danger"
label="Delete"
(onClick)="deleteSelectedQuestions()"
[disabled]="!selectedQuestions || !selectedQuestions.length" />
</div>
</p-toolbar>
} @else {
<div style="height: 50px"></div>
}
<div class="table-container">
<p-table
sortField="id"
[sortOrder]="1"
[columns]="cols"
[value]="questions"
[(selection)]="selectedQuestions"
datakey="id"
[tableStyle]="{ 'table-layout': 'fixed', width: '100%', 'text-align': 'center' }"
[paginator]="true"
[rows]="10"
[rowsPerPageOptions]="[10, 25, 50]"
styleClass="p-datatable-gridlines-striped">
<ng-template pTemplate="caption">
<div class="flex">
<h3 class="m-0">Questions</h3>
</div>
</ng-template>
<ng-template pTemplate="header" let-columns>
<tr>
@if (isAdmin) {
<th style="width: 10%"><p-tableHeaderCheckbox /></th>
<th pSortableColumn="id" style="width: 13%">Id<p-sortIcon field="id" /></th>
<th style="width: 27%">Title</th>
<th style="width: 30%">Topics</th>
<th style="width: 10%">Difficulty</th>
<th style="width: 10%"></th>
} @else {
<th style="width: 15%">Id</th>
<th style="width: 35%">Title</th>
<th style="width: 37%">Topics</th>
<th style="width: 13%">Difficulty</th>
}
</tr>
</ng-template>
<ng-template pTemplate="body" let-question>
<tr (click)="onRowSelect(question)">
@if (isAdmin) {
<td><p-tableCheckbox [value]="question" /></td>
<td>{{ question.id }}</td>
<td>{{ question.title }}</td>
<td>{{ question.topics.join(', ') }}</td>
<td>{{ question.difficulty }}</td>
<td>
<p-button
label="Edit"
severity="primary"
icon="pi pi-file-edit"
class="mr-2"
[text]="true"
(onClick)="editQuestion(question)" />
</td>
} @else {
<td>{{ question.id }}</td>
<td>{{ question.title }}</td>
<td>{{ question.topics.join(', ') }}</td>
<td>{{ question.difficulty }}</td>
}
</tr>
</ng-template>
</p-table>
</div>
</ng-template>
<ng-template pTemplate="header" let-columns>
<tr>
<th style="width: 10%"><p-tableHeaderCheckbox /></th>
<th pSortableColumn="id" style="width: 10%">Id <p-sortIcon field="id" /></th>
<th style="width: 10%">Title</th>
<th style="width: 40%">Description</th>
<th style="width: 10%">Topics</th>
<th style="width: 10%">Difficulty</th>
<th style="width: 10%"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-question>
<tr>
<td>
<p-tableCheckbox [value]="question" />
</td>
<td>{{ question.id }}</td>
<td>{{ question.title }}</td>
<td style="white-space: pre-wrap">{{ question.description }}</td>
<td>{{ question.topics.join(', ') }}</td>
<td>{{ question.difficulty }}</td>
<td>
<p-button
label="Edit"
severity="primary"
icon="pi pi-file-edit"
class="mr-2"
[text]="true"
(onClick)="editQuestion(question)" />
</td>
</tr>
</ng-template>
</p-table>
</ng-container>
</div>
</ng-container>
}
<app-question-dialog
[isDialogVisible]="isDialogVisible"
[question]="question"
Expand All @@ -85,4 +108,18 @@ <h3 class="m-0">Manage Questions</h3>
(successfulRequest)="onSuccessfulRequest($event)">
</app-question-dialog>
<p-confirmDialog [style]="{ width: '450px' }" />
<div class="sliding-panel" [class.open]="isPanelVisible">
<div class="panel-header">
<h4>{{ clickedOnQuestion?.title }}</h4>
<p-button
icon="pi pi-times"
severity="secondary"
label="Close"
(onClick)="closePanel()"
class="p-button-text" />
</div>
<div class="panel-content">
<p style="white-space: pre-wrap">{{ clickedOnQuestion?.description }}</p>
</div>
</div>
</div>
Loading

0 comments on commit 33e19d8

Please sign in to comment.