diff --git a/API/db/app.db b/API/db/app.db index 8b49742970a9374e1b7dbac070064a555c13832b..918b6e267f795f9e97beb6aa37d825f69d9d17ce 100644 Binary files a/API/db/app.db and b/API/db/app.db differ diff --git a/API/src/database/Database.ts b/API/src/database/Database.ts index d5f1a102918a25dd8dac56c7f678768e7541b7ed..42c744fa4b3e48494093c30cd219bfdb5123f8d5 100644 --- a/API/src/database/Database.ts +++ b/API/src/database/Database.ts @@ -22,12 +22,14 @@ export type Question_t = { id: number; question: string; category: string; - answers: Answer_t; }; export type Answer_t = { - CORRECT: string; - WRONG: Array<string>; + // CORRECT: string; + // WRONG: Array<string>; + text_answer: string; + id_question: number; + correct: boolean; } export type Category_t = { @@ -60,6 +62,14 @@ class DBHandler { return (usernameInDB[0].username == username); } + async verifyQuestionExistence(question:string) : Promise<boolean> { + const questionInDB = await asyncdb.all<Question_t>("SELECT question FROM questions WHERE question='" + question + "'") + if (questionInDB.length == 0) + return false; + + return (questionInDB[0].question == question); + } + async comparePassword(user: User_t) : Promise<boolean> { const query = "SELECT password, admin FROM users WHERE username='"+ user.username + "'"; const password = await asyncdb.all<User_t>(query) @@ -106,30 +116,40 @@ class DBHandler { .catch(err => console.log(err)); } - async postQuestion(req:express.Request, res:express.Response) { - const a = req.body as Question_t; + // async postQuestion(req:express.Request, res:express.Response) { + // const a = req.body as Question_t; - const insert_question = "INSERT INTO questions (question, category) VALUES ('"+ a.question +"', '"+ a.category +"')"; - const get_id = "SELECT id FROM questions WHERE question='" + a.question + "'"; + // const insert_question = "INSERT INTO questions (question, category) VALUES ('"+ a.question +"', '"+ a.category +"')"; + // const get_id = "SELECT id FROM questions WHERE question='" + a.question + "'"; - await asyncdb.all(insert_question); + // await asyncdb.all(insert_question); - const id_json = await asyncdb.get(get_id); + // const id_json = await asyncdb.get(get_id); - const id_obj = id_json as Question_t; - const insert_correct = "INSERT INTO answer (text_answer, id_question, correct) \ - VALUES ('" + a.answers.CORRECT + "', " + id_obj.id + ", 'true')"; + // const id_obj = id_json as Question_t; + // const insert_correct = "INSERT INTO answer (text_answer, id_question, correct) \ + // VALUES ('" + a.answers.CORRECT + "', " + id_obj.id + ", 'true')"; - asyncdb.all(insert_correct) - .catch(err => console.log(err)); + // asyncdb.all(insert_correct) + // .catch(err => console.log(err)); - a.answers.WRONG.forEach(answer => asyncdb.all("INSERT INTO answer (text_answer, id_question, correct) \ - VALUES ('" + answer + "', " + id_obj.id + ", 'false')") - .catch(err => console.log(err))); + // a.answers.WRONG.forEach(answer => asyncdb.all("INSERT INTO answer (text_answer, id_question, correct) \ + // VALUES ('" + answer + "', " + id_obj.id + ", 'false')") + // .catch(err => console.log(err))); - res.end(); + // res.end(); + // } + + async postQuestion(req:express.Request, res:express.Response) { + const a = req.body as Question_t; + + const insert_question = "INSERT INTO questions (question, category) VALUES ('"+ a.question +"', '"+ a.category +"')"; + + asyncdb.all(insert_question) + .then(() => res.status(StatusCodes.OK).end()) + } async getQuestions(req:express.Request, res:express.Response) { @@ -140,12 +160,12 @@ class DBHandler { let questions = await asyncdb.all(query); let ans = await asyncdb.all(query_answers); - let payload = { - QUESTIONS: questions, - ANSWERS: ans - } + // let payload = { + // QUESTIONS: questions, + // ANSWERS: ans + // } - res.status(StatusCodes.OK).json(payload) + res.status(StatusCodes.OK).json(questions) } async updateQuestion(req:express.Request, res:express.Response){ @@ -190,6 +210,25 @@ class DBHandler { .then(() => res.status(StatusCodes.OK).end()) .catch(e => console.log(e)); } + + async getAnwsers(req:express.Request, res:express.Response) { + const question_id = req.params.questionId; + + const query = "SELECT * FROM answer WHERE id_question=" + question_id; + + asyncdb.all(query) + .then(ans => res.status(StatusCodes.OK).json(ans)) + } + + async postAnwsers(req:express.Request, res:express.Response) { + const answer = req.body as Answer_t; + + const query = "INSERT INTO answer (text_answer, id_question, correct) \ + VALUES ('" + answer.text_answer + "', " + answer.id_question + ", " + answer.correct + ")"; + + asyncdb.all(query) + .then(ans => res.status(StatusCodes.OK).json(ans)) + } } export default new DBHandler(); \ No newline at end of file diff --git a/API/src/routes/BaseRoutes.ts b/API/src/routes/BaseRoutes.ts index 03827838ebba38a23246000f2586475cac8baf11..bbff290626d4961dc2b6ec22974c71d86239996e 100644 --- a/API/src/routes/BaseRoutes.ts +++ b/API/src/routes/BaseRoutes.ts @@ -133,7 +133,17 @@ router.post(ROUTE+'/question', (req: express.Request, res: express.Response) => return; } - DBHandler.postQuestion(req, res); + DBHandler.verifyQuestionExistence(a.question) + .then(exists => { + if (exists) { + res.statusMessage = "Question already exists"; + res.status(StatusCodes.CONFLICT).end(); + return; + } + DBHandler.postQuestion(req, res); + + res.status(StatusCodes.OK).end(); + }); }) router.get(ROUTE+'/question', (req: express.Request, res: express.Response) => { @@ -148,6 +158,16 @@ router.delete(ROUTE+'/question/:id', (req: express.Request, res: express.Respons DBHandler.deleteQuestion(req, res); }) +// ANSWERS CRUD + +router.get(ROUTE+'/answer/:questionId', (req: express.Request, res: express.Response) => { + DBHandler.getAnwsers(req, res); +}) + +router.post(ROUTE+'/answer', (req: express.Request, res: express.Response) => { + DBHandler.postAnwsers(req, res); +}) + // CATEGORIES CRUD router.post(ROUTE+'/category', (req: express.Request, res: express.Response) => { @@ -172,7 +192,6 @@ router.post(ROUTE+"/login", (req: express.Request, res: express.Response) => { .then(loginJSON => { res.status(StatusCodes.OK).json(generateToken(loginJSON as User_t))}) .catch(err => { - console.log(err) res.status(StatusCodes.UNAUTHORIZED).json(JSON.parse(err)); }) }) diff --git a/frontend/src/app/admin/admin.component.html b/frontend/src/app/admin/admin.component.html index 830851246373c45b1a09ac5f4246de746ec2b485..5bbed70e7dac980e5dbe4965851e0a216ab2953c 100644 --- a/frontend/src/app/admin/admin.component.html +++ b/frontend/src/app/admin/admin.component.html @@ -16,8 +16,8 @@ </tbody> </table> - - <table class="table-auto mt-20"> + <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> + <table class="table-auto"> <thead class="border-b font-medium dark:border-neutral-500"> <th class="px-6 py-4">Question</th> <th class="px-6 py-4">Catégorie</th> @@ -41,4 +41,8 @@ <div *ngIf="modalCreate"> <app-create-user (closeModal)="modalCreate=false;"></app-create-user> </div> + + <div *ngIf="modalQuestionCreate"> + <app-create-question (closeModal)="modalQuestionCreate=false;"></app-create-question> + </div> </div> diff --git a/frontend/src/app/admin/admin.component.ts b/frontend/src/app/admin/admin.component.ts index a46e7de366c5ccacc68566d52d378991dbf46489..33e62978f3ad26afbf36ee3ad38184ac2cc3e93e 100644 --- a/frontend/src/app/admin/admin.component.ts +++ b/frontend/src/app/admin/admin.component.ts @@ -16,10 +16,12 @@ export class AdminComponent implements OnInit { public questions!: Question[]; public answers!: Answer[]; private usersSub!: Subscription; + private questionsSub!: Subscription; userToEdit!: User; modalUpdate: boolean = false; modalCreate: boolean = false; + modalQuestionCreate: boolean = false; constructor( private http: HttpClient, @@ -34,14 +36,11 @@ export class AdminComponent implements OnInit { this.userController.fetchUsers(); - + this.questionsSub = this.questionController.questions.subscribe(questionList => { + this.questions = questionList; + }) - this.http.get("http://0.0.0.0:30992/API/v1/question") - .subscribe(res => { - let questions = res as QandA; - this.questions = questions.QUESTIONS; - this.answers = questions.ANSWERS; - }); + this.questionController.fetchQuestions(); } deleteUser(user:User) { @@ -56,9 +55,4 @@ export class AdminComponent implements OnInit { this.userToEdit = user; this.modalUpdate = true; } - - showCreateModal(){ - this.modalCreate = true; - } - } diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index b5ae650876b87ed2e3a1a6c1c2d9ce562ea75a54..7a1b8c150228bfa40592f6018cb5ab9f30622cc6 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -11,6 +11,7 @@ import { TokenInterceptor } from './interceptors/token-interceptor'; import { SignupComponent } from './login-shit/signup/signup.component'; import { CreateUserComponent } from './user-shit/create-user/create-user.component'; import { UpdateUserComponent } from './user-shit/update-user/update-user.component'; +import { CreateQuestionComponent } from './question-shit/create-question/create-question.component'; @NgModule({ declarations: [ @@ -19,7 +20,8 @@ import { UpdateUserComponent } from './user-shit/update-user/update-user.compone LoginComponent, SignupComponent, CreateUserComponent, - UpdateUserComponent + UpdateUserComponent, + CreateQuestionComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/question-shit/create-question/create-question.component.css b/frontend/src/app/question-shit/create-question/create-question.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/frontend/src/app/question-shit/create-question/create-question.component.html b/frontend/src/app/question-shit/create-question/create-question.component.html new file mode 100644 index 0000000000000000000000000000000000000000..494fa77ecf22c65cfe09829bbcab4ecac0ed6a02 --- /dev/null +++ b/frontend/src/app/question-shit/create-question/create-question.component.html @@ -0,0 +1,23 @@ +<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 Question</h1> + + <form [formGroup]="createQuestion" (ngSubmit)="onSubmit()" class="flex flex-col pb-2 mx-2"> + <input class="border-b-4 mt-2 w-full" type="text" placeholder="Text" formControlName="question"> + <select #select id="category" class="form-control" (change)="onSelected(select.value)"> + <option default value="">Thème</option> + <option *ngFor="let c of categories" [value]="c.title">{{c.title}}</option> + </select> + <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-question/create-question.component.spec.ts b/frontend/src/app/question-shit/create-question/create-question.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..6ad0b50e1077a689570406915367a7c2d6c02e56 --- /dev/null +++ b/frontend/src/app/question-shit/create-question/create-question.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateQuestionComponent } from './create-question.component'; + +describe('CreateQuestionComponent', () => { + let component: CreateQuestionComponent; + let fixture: ComponentFixture<CreateQuestionComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [CreateQuestionComponent] + }); + fixture = TestBed.createComponent(CreateQuestionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/question-shit/create-question/create-question.component.ts b/frontend/src/app/question-shit/create-question/create-question.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..7224998718c801dcd69d225ddb9e77db9851b178 --- /dev/null +++ b/frontend/src/app/question-shit/create-question/create-question.component.ts @@ -0,0 +1,53 @@ +import { Component, EventEmitter, Output } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { Category, Question } from 'src/app/Types/types'; +import { QuestionsService } from 'src/app/services/questions.service'; + +@Component({ + selector: 'app-create-question', + templateUrl: './create-question.component.html', + styleUrls: ['./create-question.component.css'] +}) +export class CreateQuestionComponent { + createQuestion: FormGroup; + private selectedCategory!: string; + categories!: Category[]; + + + @Output() closeModal: EventEmitter<string> = new EventEmitter<string>(); + + constructor(private questionsController: QuestionsService) { } + + ngOnInit(): void { + + this.questionsController.categories.subscribe(categoriesList => { + this.categories = categoriesList; + }) + + this.questionsController.fetchCategories(); + + console.log(this.categories); + + this.createQuestion = new FormGroup({ + question: new FormControl('', Validators.required) + }); + } + + onSubmit() { + const question = this.createQuestion.get("question")!.value; + + const newQuestion: Question = { + id: undefined, + question: question, + category: this.selectedCategory + } + + this.questionsController.addQuestion(newQuestion); + this.closeModal.emit(); + } + + onSelected(c) { + this.selectedCategory = c; + } + +} diff --git a/frontend/src/app/services/questions.service.ts b/frontend/src/app/services/questions.service.ts index 82f9bbc1a862c26509c3ddbb09787e5e2aac591d..8f8f8dfb7a96ce2146aef36084f037394a468497 100644 --- a/frontend/src/app/services/questions.service.ts +++ b/frontend/src/app/services/questions.service.ts @@ -1,20 +1,50 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Question } from '../Types/types'; +import { Category, Question } from '../Types/types'; +import { BehaviorSubject } from 'rxjs'; -const ROUTE = "http://0.0.0.0:30992/API/v1/question" +const ROUTE = "http://0.0.0.0:30992/API/v1" @Injectable({ providedIn: 'root' }) export class QuestionsService { + private _categories = new BehaviorSubject<Category[]>([]); + private _questions = new BehaviorSubject<Question[]>([]); + constructor( private http: HttpClient) { } deleteQuestion(question: Question) { - this.http.delete(ROUTE + "/" + question.id.toString()).subscribe(res => { - console.log("PLZ REFRESH") + this.http.delete(ROUTE + "/question/" + question.id.toString()).subscribe(() => { + this.fetchQuestions(); + }); + } + + get categories() { + return this._categories.asObservable(); + } + + get questions() { + return this._questions.asObservable(); + } + + fetchCategories() { + this.http.get(ROUTE + "/category").subscribe(categories => { + this._categories.next(categories as Category[]) + }) + } + + fetchQuestions() { + this.http.get(ROUTE + "/question").subscribe(questions => { + this._questions.next(questions as Question[]) + }) + } + + addQuestion(question:Question) { + this.http.post<Question>(ROUTE + "/question", question).subscribe(() => { + this.fetchQuestions(); }); }