diff --git a/frontend/src/app/Types/types.ts b/frontend/src/app/Types/types.ts index 21e0fd04fd3c3962487ab3f90e81e65578189311..230a4b385364a8129b2c5073067b9dee35bf8fa4 100644 --- a/frontend/src/app/Types/types.ts +++ b/frontend/src/app/Types/types.ts @@ -24,6 +24,5 @@ export type Answer = { } export type Category = { - id: number; category: string; }; \ No newline at end of file diff --git a/frontend/src/app/admin/admin.component.html b/frontend/src/app/admin/admin.component.html index ac032df87e75ead76aa124c959083734d8a04d3c..b13097278006308b0b397014b5c55a5eca43dc55 100644 --- a/frontend/src/app/admin/admin.component.html +++ b/frontend/src/app/admin/admin.component.html @@ -11,12 +11,17 @@ <td class="whitespace-nowrap px-6 py-4">{{user.username}}</td> <td class="whitespace-nowrap px-6 py-4">{{user.admin}}</td> <td><button class="bg-teal-200 w-fit border-2 border-teal-600 rounded-lg px-2 hover:bg-teal-400 mt-2" (click)="showUpdateModal(user)">UPDATE</button></td> - <td><button class="bg-red-400 w-fit border-2 border-red-600 rounded-lg px-2 hover:bg-red-500 mt-2" (click)="deleteUser(user)">DELETE</button></td> + <td><button class="bg-red-400 w-fit border-2 border-red-600 rounded-lg px-2 hover:bg-red-500 mt-2" (click)="deleteUserConfirm(user)">DELETE</button></td> </tr> </tbody> </table> - <button (click)="modalQuestionCreate=true;" class="mt-20 bg-white w-fit border-2 border-black rounded-lg px-2 hover:bg-gray-300 mt-2">Create Question</button> + <div class="grid grid-cols-2 gap-10"> + <div class="flex flex-row justify-between"> + <button (click)="modalQuestionCreate=true;" class="mt-20 bg-white w-fit border-2 border-black rounded-lg px-2 hover:bg-gray-300 mt-2">Create Question</button> + <button (click)="modalCategoryCreate=true;" class="mt-20 bg-white w-fit border-2 border-black rounded-lg px-2 hover:bg-gray-300 mt-2">Create Category</button> + </div> + </div> <div class="grid grid-cols-2 gap-10"> <table class="table-auto w-full h-fit text-left"> @@ -26,14 +31,15 @@ </thead> <tbody> <tr *ngFor="let question of questions" class="border-b dark:border-neutral-500"> - <td class="whitespace-nowrap px-6 py-4 max-w-sm break-all">{{question.question}}</td> + <td class="px-6 py-4 max-w-sm break-all">{{question.question}}</td> <td class="whitespace-nowrap px-6 py-4">{{question.category}}</td> <td><button class="bg-teal-200 w-fit border-2 border-teal-600 rounded-lg px-2 hover:bg-teal-400 mt-2" (click)="showQuestionUpdateModal(question)">UPDATE</button></td> - <td><button class="bg-red-400 w-fit border-2 border-red-600 rounded-lg px-2 hover:bg-red-300 mt-2" (click)="deleteQuestion(question)">DELETE</button></td> + <td><button class="bg-red-400 w-fit border-2 border-red-600 rounded-lg px-2 hover:bg-red-300 mt-2" (click)="deleteQuestionConfirm(question)">DELETE</button></td> <td><button class="bg-teal-200 w-fit border-2 border-teal-600 rounded-lg px-2 hover:bg-teal-400 mt-2" (click)="showSelectedAnswers(question.id)">⇉</button></td> </tr> </tbody> </table> + <table class="table-auto w-full h-fit text-left"> <thead class="border-b font-medium dark:border-neutral-500"> <th class="px-6 py-4">Answer</th> @@ -44,7 +50,7 @@ <td class="whitespace-nowrap px-6 py-4">{{answer.text_answer}}</td> <td class="whitespace-nowrap px-6 py-4">{{answer.correct}}</td> <td><button class="bg-teal-200 w-fit border-2 border-teal-600 rounded-lg px-2 hover:bg-teal-400 mt-2" (click)="showAnswerUpdateModal(answer)">UPDATE</button></td> - <td><button class="bg-red-400 w-fit border-2 border-red-600 rounded-lg px-2 hover:bg-red-300 mt-2" (click)="deleteAnswer(answer)">DELETE</button></td> + <td><button class="bg-red-400 w-fit border-2 border-red-600 rounded-lg px-2 hover:bg-red-300 mt-2" (click)="deleteAnswerConfirm(answer)">DELETE</button></td> </tr> </tbody> <button *ngIf="buttonCreateAnswer" class=" bg-white w-fit border-2 border-black rounded-lg px-2 hover:bg-gray-300 mt-2" (click)="modalAnswerCreate = true;">Add Answer</button> @@ -52,7 +58,17 @@ </div> - + <div *ngIf="confirmUserDelete"> + <app-deletion-confirmation (confirm)="deleteUser(userToEdit)" (cancel)="confirmUserDelete=false" ></app-deletion-confirmation> + </div> + + <div *ngIf="confirmQuestionDelete"> + <app-deletion-confirmation (confirm)="deleteQuestion(questionToEdit)" (cancel)="confirmQuestionDelete=false" ></app-deletion-confirmation> + </div> + + <div *ngIf="confirmAnswerDelete"> + <app-deletion-confirmation (confirm)="deleteAnswer(answerToEdit)" (cancel)="confirmAnswerDelete=false" ></app-deletion-confirmation> + </div> <div *ngIf="modalUpdate"> <app-update-user [user]="userToEdit" (closeModal)="modalUpdate=false;"></app-update-user> @@ -76,4 +92,7 @@ <div *ngIf="modalAnswerUpdate"> <app-update-answer [answer]="answerToEdit" (closeModal)="modalAnswerUpdate=false" ></app-update-answer> </div> + <div *ngIf="modalCategoryCreate"> + <app-create-category (closeModal)="modalCategoryCreate=false"></app-create-category> + </div> </div> diff --git a/frontend/src/app/admin/admin.component.ts b/frontend/src/app/admin/admin.component.ts index 60bcd6ab194264867a0d6f326d45228dbd4410eb..70e60bd42a40a13cbc74d4e2e17062edaf96a764 100644 --- a/frontend/src/app/admin/admin.component.ts +++ b/frontend/src/app/admin/admin.component.ts @@ -30,6 +30,10 @@ export class AdminComponent implements OnInit { modalQuestionUpdate: boolean = false; modalAnswerCreate: boolean = false; modalAnswerUpdate: boolean = false; + modalCategoryCreate: boolean = false; + confirmUserDelete: boolean = false; + confirmQuestionDelete: boolean = false; + confirmAnswerDelete: boolean = false; constructor( private router: Router, @@ -70,14 +74,17 @@ export class AdminComponent implements OnInit { deleteUser(user:User) { this.userController.deleteUser(user); + this.confirmUserDelete = false; } deleteQuestion(question: Question) { this.questionController.deleteQuestion(question); + this.confirmQuestionDelete = false; } deleteAnswer(answer: Answer) { this.questionController.deleteAnswer(answer); + this.confirmAnswerDelete = false; } showUpdateModal(user: User){ @@ -95,4 +102,18 @@ export class AdminComponent implements OnInit { this.modalAnswerUpdate = true; } + deleteUserConfirm(user: User) { + this.userToEdit = user; + this.confirmUserDelete = true; + } + + deleteQuestionConfirm(question: Question) { + this.questionToEdit = question; + this.confirmQuestionDelete = true; + } + + deleteAnswerConfirm(answer: Answer) { + this.answerToEdit = answer; + this.confirmAnswerDelete = true; + } } diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index f70ebd70fd09e2dc40a8b9b9b2afef1764b82859..e738f97b52647680fa4c7582b0ad5f38e329c933 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -20,6 +20,7 @@ <app-user-dropdown [username]="auth.getUsername()"></app-user-dropdown> </div> + <main class="w-full h-full"> <router-outlet> </router-outlet> diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 76b9dad6d91396b55735bd506639dd96a43908e4..00645a00abca40a7ca32f0d35044870322f30638 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -10,7 +10,7 @@ import { AuthenticationService } from './services/authentication.service'; export class AppComponent { title = 'frontend'; - userDropdownModal: boolean = false + userDropdownModal: boolean = false; constructor( public router: Router, @@ -26,4 +26,4 @@ export class AppComponent { this.userDropdownModal = !this.userDropdownModal; } -} +} \ No newline at end of file diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 338f77e6e2d8ed9ed7c3255519a77cd86c62ca65..28e0c121d71287b403ba764ac3d175c9bbdad675 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -16,6 +16,8 @@ import { CreateAnswerComponent } from './question-shit/create-answer/create-answ import { UpdateQuestionComponent } from './question-shit/update-question/update-question.component'; import { UpdateAnswerComponent } from './question-shit/update-answer/update-answer.component'; import { UserDropdownComponent } from './user-dropdown/user-dropdown.component'; +import { CreateCategoryComponent } from './question-shit/create-category/create-category.component'; +import { DeletionConfirmationComponent } from './deletion-confirmation/deletion-confirmation.component'; @NgModule({ declarations: [ @@ -29,7 +31,9 @@ import { UserDropdownComponent } from './user-dropdown/user-dropdown.component'; CreateAnswerComponent, UpdateQuestionComponent, UpdateAnswerComponent, - UserDropdownComponent + UserDropdownComponent, + CreateCategoryComponent, + DeletionConfirmationComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/deletion-confirmation/deletion-confirmation.component.css b/frontend/src/app/deletion-confirmation/deletion-confirmation.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/frontend/src/app/deletion-confirmation/deletion-confirmation.component.html b/frontend/src/app/deletion-confirmation/deletion-confirmation.component.html new file mode 100644 index 0000000000000000000000000000000000000000..413da4a2508052f12fd4089acb431d614be21808 --- /dev/null +++ b/frontend/src/app/deletion-confirmation/deletion-confirmation.component.html @@ -0,0 +1,14 @@ +<div class="z-10 relative"> + <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div> + <div class="fixed inset-0 z-10 overflow-y-auto"> + <div class="flex flex-col min-h-full justify-center p-4 text-center items-center sm:p-0"> + <div class="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 p-2 w-48"> + <h1 class="mt-2 mx-2 font-bold">Are you sure ?</h1> + <div class="flex flex-row justify-between"> + <button class="bg-red-400 w-fit border-2 border-red-600 rounded-lg px-2 hover:bg-red-500 mt-2" (click)="confirm.emit()">Confirm</button> + <button class="bg-teal-200 w-fit border-2 border-teal-600 rounded-lg px-2 hover:bg-teal-400 mt-2" (click)="cancel.emit()">Cancel</button> + </div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/frontend/src/app/deletion-confirmation/deletion-confirmation.component.spec.ts b/frontend/src/app/deletion-confirmation/deletion-confirmation.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0157e7526a41edd535f4f6d0570cbb61225ed9c2 --- /dev/null +++ b/frontend/src/app/deletion-confirmation/deletion-confirmation.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeletionConfirmationComponent } from './deletion-confirmation.component'; + +describe('DeletionConfirmationComponent', () => { + let component: DeletionConfirmationComponent; + let fixture: ComponentFixture<DeletionConfirmationComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [DeletionConfirmationComponent] + }); + fixture = TestBed.createComponent(DeletionConfirmationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/deletion-confirmation/deletion-confirmation.component.ts b/frontend/src/app/deletion-confirmation/deletion-confirmation.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..720e5c97d98eec4de96bb369e590d4f873bf38e6 --- /dev/null +++ b/frontend/src/app/deletion-confirmation/deletion-confirmation.component.ts @@ -0,0 +1,12 @@ +import { Component, EventEmitter, Output } from '@angular/core'; + +@Component({ + selector: 'app-deletion-confirmation', + templateUrl: './deletion-confirmation.component.html', + styleUrls: ['./deletion-confirmation.component.css'] +}) +export class DeletionConfirmationComponent { + + @Output() cancel: EventEmitter<void> = new EventEmitter<void>(); + @Output() confirm: EventEmitter<void> = new EventEmitter<void>(); +} diff --git a/frontend/src/app/question-shit/create-category/create-category.component.css b/frontend/src/app/question-shit/create-category/create-category.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/frontend/src/app/question-shit/create-category/create-category.component.html b/frontend/src/app/question-shit/create-category/create-category.component.html new file mode 100644 index 0000000000000000000000000000000000000000..386b36d16fcbc8ddf9b9f7dc4a8d5f89bab854b3 --- /dev/null +++ b/frontend/src/app/question-shit/create-category/create-category.component.html @@ -0,0 +1,19 @@ +<div class="z-10 relative"> + <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div> + <div class="fixed inset-0 z-10 overflow-y-auto"> + <div class="flex flex-col min-h-full justify-center p-4 text-center items-center sm:p-0"> + <div class="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 "> + <h1 class="mt-2 mx-2 font-bold">Create Category</h1> + + <form [formGroup]="createCategory" (ngSubmit)="onSubmit()" class="flex flex-col pb-2 mx-2"> + <input class="border-b-4 mt-2 w-full" type="text" placeholder="Text" formControlName="category"> + <div class="flex flex-row justify-between"> + <button class="border-2 border-black rounded-lg px-2 hover:bg-gray-300 mt-2" type="submit">Create</button> + <button class="border-2 border-black rounded-lg px-2 hover:bg-gray-300 mt-2" (click)="closeModal.emit()">Close</button> + </div> + </form> + + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/frontend/src/app/question-shit/create-category/create-category.component.spec.ts b/frontend/src/app/question-shit/create-category/create-category.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad29077b693823509c0780d60ae1a6e86952a2a9 --- /dev/null +++ b/frontend/src/app/question-shit/create-category/create-category.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateCategoryComponent } from './create-category.component'; + +describe('CreateCategoryComponent', () => { + let component: CreateCategoryComponent; + let fixture: ComponentFixture<CreateCategoryComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [CreateCategoryComponent] + }); + fixture = TestBed.createComponent(CreateCategoryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/question-shit/create-category/create-category.component.ts b/frontend/src/app/question-shit/create-category/create-category.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..55a47e54891f30c71b6ba7f8ab14a55e4fe6fa69 --- /dev/null +++ b/frontend/src/app/question-shit/create-category/create-category.component.ts @@ -0,0 +1,34 @@ +import { Component, EventEmitter, Output } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { Category } from 'src/app/Types/types'; +import { QuestionsService } from 'src/app/services/questions.service'; + +@Component({ + selector: 'app-create-category', + templateUrl: './create-category.component.html', + styleUrls: ['./create-category.component.css'] +}) +export class CreateCategoryComponent { + createCategory: FormGroup; + + @Output() closeModal: EventEmitter<string> = new EventEmitter<string>(); + + constructor(private questionsController: QuestionsService) { } + + ngOnInit(): void { + this.createCategory = new FormGroup({ + category: new FormControl('', Validators.required) + }); + } + + onSubmit() { + const category = this.createCategory.get("category")!.value; + + const newCategory: Category = { + category: category + } + + this.questionsController.addCategory(newCategory); + this.closeModal.emit(); + } +} diff --git a/frontend/src/app/services/authentication.service.ts b/frontend/src/app/services/authentication.service.ts index dbe901dd65ba1e960eef2a51a2668fadea0546e3..da827c6553728c226707ece887a7d424bc04cd05 100644 --- a/frontend/src/app/services/authentication.service.ts +++ b/frontend/src/app/services/authentication.service.ts @@ -59,10 +59,14 @@ export class AuthenticationService { } getUsername(): string { - const user = this.getDecodedAccessToken() as User; + const user = this.getUser(); if(user === null) return "Guest"; return user.username; } + + getUser(): User { + return this.getDecodedAccessToken() as User; + } } diff --git a/frontend/src/app/services/questions.service.ts b/frontend/src/app/services/questions.service.ts index 1e1517657dd88eba10c884757d52dafb6524d2de..a9f7d27f5465bd17788a04c883b21845f1e2da71 100644 --- a/frontend/src/app/services/questions.service.ts +++ b/frontend/src/app/services/questions.service.ts @@ -84,4 +84,10 @@ export class QuestionsService { }) } + addCategory(category: Category) { + this.http.post(ROUTE + "/category", category).subscribe(() => { + this.fetchCategories(); + }) } + +} diff --git a/frontend/src/app/services/users.service.ts b/frontend/src/app/services/users.service.ts index 7ceb9a34cee6fe06135122d896a8fbf308d41110..2217183570928e22249df6de5fff6259f711ba46 100644 --- a/frontend/src/app/services/users.service.ts +++ b/frontend/src/app/services/users.service.ts @@ -51,4 +51,11 @@ export class UsersService { this.http.patch<User>(ROUTE + "/" + user.id.toString(), user).subscribe(res => this.fetchUsers()) } + updateUsername(user: User) { + this.http.patch<User>(ROUTE + "/" + user.id.toString(), user).subscribe(res => { + this.auth.logout(); + this.auth.login(user.username, user.password); + }) + } + } diff --git a/frontend/src/app/user-dropdown/user-dropdown.component.html b/frontend/src/app/user-dropdown/user-dropdown.component.html index acdc5d25e93b6905c10a52de676f4b2db9220426..daf5894e40a79669cbe923666ca4a88ae5bca4f3 100644 --- a/frontend/src/app/user-dropdown/user-dropdown.component.html +++ b/frontend/src/app/user-dropdown/user-dropdown.component.html @@ -4,8 +4,12 @@ <div class="rounded-lg bg-gray-300 p-2"> <h1 class="font-bold">User Infos</h1> <h1>{{username}}</h1> - <button *ngIf="auth.isLoggedIn()" class="border-2 border-black rounded-lg px-2 bg-gray-400 hover:bg-gray-500">Change Username</button> + <button *ngIf="auth.isLoggedIn()" class="border-2 border-black rounded-lg px-2 bg-gray-400 hover:bg-gray-500" (click)="userUpdateModal=true">Change Username</button> </div> </div> +</div> + +<div *ngIf="userUpdateModal"> + <app-update-user [user]="auth.getUser()" (closeModal)="userUpdateModal=false" ></app-update-user> </div> \ No newline at end of file diff --git a/frontend/src/app/user-dropdown/user-dropdown.component.ts b/frontend/src/app/user-dropdown/user-dropdown.component.ts index 5eb79caec2799f65ea5625815fe34c3d30993d94..1071ad33990d0cff45338b6776febdaed0c878d4 100644 --- a/frontend/src/app/user-dropdown/user-dropdown.component.ts +++ b/frontend/src/app/user-dropdown/user-dropdown.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { User } from '../Types/types'; import { AuthenticationService } from '../services/authentication.service'; +import { UsersService } from '../services/users.service'; @Component({ selector: 'app-user-dropdown', @@ -10,8 +11,10 @@ import { AuthenticationService } from '../services/authentication.service'; export class UserDropdownComponent implements OnInit { @Input() username!: string; + userUpdateModal: boolean = false; - constructor(public auth: AuthenticationService) {} + constructor(public auth: AuthenticationService, + private userController: UsersService) {} ngOnInit(): void { if (this.username === undefined) diff --git a/frontend/src/app/user-shit/update-user/update-user.component.html b/frontend/src/app/user-shit/update-user/update-user.component.html index e30280138fd3c5a4eda5d1ec1422a306784ac09d..b498383949a145b32320d3c8829bd7cd61b84636 100644 --- a/frontend/src/app/user-shit/update-user/update-user.component.html +++ b/frontend/src/app/user-shit/update-user/update-user.component.html @@ -7,7 +7,8 @@ <form [formGroup]="updateUser" (ngSubmit)="onSubmit()" class="flex flex-col pb-2 mx-2"> <input class="border-b-4 mt-2 w-full" type="text" formControlName="username"> - <div class="my-2 border-b-4"> + <input *ngIf="!auth.isAdmin()" class="border-b-4 mt-2 w-full" type="password" placeholder="Password" formControlName="password"> + <div *ngIf="auth.isAdmin()" class="my-2 border-b-4"> <label class="mt-2 w-fit">Admin</label><input class="ml-6" type="checkbox" formControlName="admin"> </div> <div class="flex flex-row justify-between"> diff --git a/frontend/src/app/user-shit/update-user/update-user.component.ts b/frontend/src/app/user-shit/update-user/update-user.component.ts index 749e53d75d725d0f620d8868c9ca4ed8e2b60b73..a997185a2754461210f29191c401c363e30727a5 100644 --- a/frontend/src/app/user-shit/update-user/update-user.component.ts +++ b/frontend/src/app/user-shit/update-user/update-user.component.ts @@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { User } from '../../Types/types'; import { UsersService } from '../../services/users.service'; +import { AuthenticationService } from 'src/app/services/authentication.service'; @Component({ selector: 'app-update-user', @@ -10,33 +11,39 @@ import { UsersService } from '../../services/users.service'; }) export class UpdateUserComponent implements OnInit { - updateUser: FormGroup + updateUser: FormGroup; @Input() user!: User; @Output() closeModal: EventEmitter<void> = new EventEmitter<void>(); - constructor(private userController: UsersService) { } + constructor(private userController: UsersService, + public auth: AuthenticationService) { } ngOnInit(): void { this.updateUser = new FormGroup({ username: new FormControl(this.user.username, Validators.required), + password: new FormControl('', Validators.required), admin: new FormControl(this.user.admin, Validators.required) }); } onSubmit() { const username = this.updateUser.get("username")!.value; - + const password = this.updateUser.get("password")!.value; const type = this.updateUser.get("admin")!.value; const userToEdit: User = { id: this.user.id, username : username, - password: undefined, + password: password, admin: type } - this.userController.updateUser(userToEdit); + if (this.auth.isAdmin()) + this.userController.updateUser(userToEdit); + else + this.userController.updateUsername(userToEdit); + this.closeModal.emit(); } }