Skip to content

Commit 0e8e918

Browse files
committed
Changed SPA
Now, SPA will look different for admin and normal user. Also changed how it looks a little bit to make it look more tidy.
1 parent b7b1c39 commit 0e8e918

File tree

7 files changed

+157
-60
lines changed

7 files changed

+157
-60
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
@@ -22,7 +22,7 @@
2222
"@angular/router": "^18.2.0",
2323
"primeflex": "^3.3.1",
2424
"primeicons": "^7.0.0",
25-
"primeng": "^17.18.10",
25+
"primeng": "^17.18.11",
2626
"rxjs": "~7.8.0",
2727
"tslib": "^2.3.0",
2828
"zone.js": "~0.14.10"

frontend/src/_helpers/interceptors/jwt.interceptor.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ export class JwtInterceptor implements HttpInterceptor {
1717
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
1818
// add auth header with jwt if user is logged in and request is to the api url
1919
const isLoggedIn = this.currentUser?.accessToken;
20-
const isApiUrl = request.url.startsWith(environment.UserServiceApiUrl);
21-
if (isLoggedIn && isApiUrl) {
20+
if (isLoggedIn) {
2221
request = request.clone({
2322
headers: request.headers.set('Authorization', `Bearer ${this.currentUser!.accessToken}`),
2423
});

frontend/src/_services/authentication.service.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,31 @@ export class AuthenticationService {
7272
this.userSubject.next(null);
7373
this.router.navigate(['/account/login']);
7474
}
75+
76+
// get user details from user service for authentication
77+
getUserDetails() {
78+
return this.http
79+
.get<UServRes>(`${environment.UserServiceApiUrl}/users/${this.userValue?.id}`, { observe: 'response' })
80+
.pipe(
81+
map(response => {
82+
if (response.status === 200) {
83+
let user: 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+
}
75102
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,40 @@
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 */
743
}

frontend/src/app/questions/questions.component.html

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
animationDuration=".5s" />
88
<p-toast />
99
<ng-container *ngIf="!loading">
10-
<p-toolbar styleClass="mb-2 gap-2">
10+
<p-toolbar *ngIf="isAdmin" styleClass="mb-2 gap-2">
1111
<div class="p-justify-end">
1212
<p-button
1313
icon="pi pi-plus"
@@ -24,56 +24,56 @@
2424
[disabled]="!selectedQuestions || !selectedQuestions.length" />
2525
</div>
2626
</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]="{ 'table-layout': 'fixed' }"
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>
42-
</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>
27+
<div class="table-container" style="max-width: 1000px; margin: 0 auto;">
28+
<p-table
29+
sortField="id"
30+
[sortOrder]="1"
31+
[columns]="cols"
32+
[value]="questions"
33+
[(selection)]="selectedQuestions"
34+
datakey="id"
35+
[tableStyle]="{ 'table-layout': 'fixed', 'width': '100%', 'text-align': 'center' }"
36+
[paginator]="true"
37+
[rows]="10"
38+
[rowsPerPageOptions]="[10, 25, 50]"
39+
styleClass="p-datatable-gridlines-striped">
40+
<ng-template pTemplate="caption">
41+
<div class="flex">
42+
<h3 class="m-0">Questions</h3>
43+
</div>
44+
</ng-template>
45+
<ng-template pTemplate="header" let-columns>
46+
<tr>
47+
<th *ngIf="isAdmin" style="width: 5%"><p-tableHeaderCheckbox /></th>
48+
<th pSortableColumn="id" style="width: 5%">Id <p-sortIcon field="id" /></th>
49+
<th style="width: 20%">Title</th>
50+
<th style="width: 20%">Topics</th>
51+
<th style="width: 7%">Difficulty</th>
52+
<th *ngIf="isAdmin" style="width: 6%"></th>
53+
</tr>
54+
</ng-template>
55+
<ng-template pTemplate="body" let-question>
56+
<tr (click)="onRowSelect(question)">
57+
<td *ngIf="isAdmin">
58+
<p-tableCheckbox [value]="question" />
59+
</td>
60+
<td>{{ question.id }}</td>
61+
<td>{{ question.title }}</td>
62+
<td>{{ question.topics.join(', ') }}</td>
63+
<td>{{ question.difficulty }}</td>
64+
<td *ngIf="isAdmin">
65+
<p-button
66+
label="Edit"
67+
severity="primary"
68+
icon="pi pi-file-edit"
69+
class="mr-2"
70+
[text]="true"
71+
(onClick)="editQuestion(question)" />
72+
</td>
73+
</tr>
74+
</ng-template>
75+
</p-table>
76+
</div>
7777
</ng-container>
7878
<app-question-dialog
7979
[isDialogVisible]="isDialogVisible"
@@ -85,4 +85,18 @@ <h3 class="m-0">Manage Questions</h3>
8585
(successfulRequest)="onSuccessfulRequest($event)">
8686
</app-question-dialog>
8787
<p-confirmDialog [style]="{ width: '450px' }" />
88+
<div class="sliding-panel" [class.open]="isPanelVisible">
89+
<div class="panel-header">
90+
<h4>{{ clickedOnQuestion?.title }}</h4>
91+
<p-button
92+
icon="pi pi-times"
93+
severity="secondary"
94+
label="Close"
95+
(onClick)="closePanel()"
96+
class="p-button-text" />
97+
</div>
98+
<div class="panel-content">
99+
<p style="white-space: pre-wrap">{{ clickedOnQuestion?.description }}</p>
100+
</div>
101+
</div>
88102
</div>

frontend/src/app/questions/questions.component.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import { forkJoin } from 'rxjs';
1818
import { HttpErrorResponse } from '@angular/common/http';
1919
import { QuestionDialogComponent } from './question-dialog.component';
2020
import { Column } from './column.model';
21+
import { AuthenticationService } from '../../_services/authentication.service';
22+
import { UServRes } from '../../_models/user.service.response.interface';
23+
import { User } from '../../_models/user.model';
2124

2225
@Component({
2326
selector: 'app-questions',
@@ -45,21 +48,20 @@ import { Column } from './column.model';
4548
})
4649
export class QuestionsComponent implements OnInit {
4750
loading = true;
48-
4951
questions: Question[] = [];
50-
5152
cols: Column[] = [];
52-
5353
question!: Question;
54-
5554
selectedQuestions!: Question[] | null;
56-
5755
isDialogVisible = false;
56+
isAdmin = false;
57+
isPanelVisible = false;
58+
clickedOnQuestion: Question | null = null;
5859

5960
constructor(
6061
private questionService: QuestionService,
6162
private messageService: MessageService,
6263
private confirmationService: ConfirmationService,
64+
private AuthenticationService: AuthenticationService,
6365
) {}
6466

6567
ngOnInit() {
@@ -68,6 +70,9 @@ export class QuestionsComponent implements OnInit {
6870

6971
// fetch data from API call
7072
this.handleInitData();
73+
74+
// check if user is admin
75+
this.checkIfAdmin();
7176
}
7277

7378
openNewQuestion() {
@@ -138,6 +143,16 @@ export class QuestionsComponent implements OnInit {
138143
this.isDialogVisible = false;
139144
}
140145

146+
onRowSelect(question: Question) {
147+
this.clickedOnQuestion = question;
148+
this.isPanelVisible = true;
149+
}
150+
151+
closePanel() {
152+
this.isPanelVisible = false;
153+
this.clickedOnQuestion = null;
154+
}
155+
141156
onQuestionUpdate(question: Question) {
142157
this.questions[this.questions.findIndex(x => x.id == question.id)] = question;
143158
this.questions = [...this.questions];
@@ -164,4 +179,10 @@ export class QuestionsComponent implements OnInit {
164179
life: 3000,
165180
});
166181
}
182+
183+
checkIfAdmin() {
184+
this.AuthenticationService.getUserDetails().subscribe((userData: User | null) => {
185+
this.isAdmin = userData?.isAdmin ?? false;
186+
});
187+
}
167188
}

0 commit comments

Comments
 (0)