Skip to content

Commit

Permalink
QCMPLUS-40 : update display AnswersList and delete unused import
Browse files Browse the repository at this point in the history
Signed-off-by: teklit_tewolde <teklit_tewolde@connect-tech.sncf>
  • Loading branch information
teklit_tewolde committed Aug 20, 2024
1 parent 4699774 commit 700e94d
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 114 deletions.
6 changes: 3 additions & 3 deletions qcmplusweb/src/components/Answer/Answer.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { getAnswersByQuestionId, createAnswer, updateAnswer, deleteAnswer } from '../../services/AnswerService';
import { Button, Form, Container, Row, Col, Card, ListGroup } from 'react-bootstrap';
import React, {useEffect, useState} from 'react';
import {createAnswer, deleteAnswer, getAnswersByQuestionId, updateAnswer} from '../../services/AnswerService';
import {Button, Card, Col, Container, Form, ListGroup, Row} from 'react-bootstrap';

const Answer = ({ questionId }) => {
const [answers, setAnswers] = useState([]);
Expand Down
193 changes: 131 additions & 62 deletions qcmplusweb/src/components/AnswersList/AnswersList.jsx
Original file line number Diff line number Diff line change
@@ -1,104 +1,173 @@
import React, { useEffect, useState } from "react";
import { Col, Form, Row, Table, Button } from "react-bootstrap";
import { getAnswersByQuestionId, deleteAnswer } from '../../services/AnswerService'; // Assurez-vous que l'import de l'API est correct
import React, {useCallback, useEffect, useState} from "react";
import {Alert, Button, Col, Form, Row, Spinner, Table} from "react-bootstrap";
import {createAnswer, deleteAnswer, getAllAnswers, updateAnswer} from '../../services/AnswerService';

const AnswersList = ({ title, questionId }) => {
const [answers, setAnswers] = useState([]); // État pour stocker les réponses
const [searchTerm, setSearchTerm] = useState(''); // État pour la barre de recherche
const [answers, setAnswers] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
const [newAnswerText, setNewAnswerText] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

// Fonction pour récupérer les réponses depuis l'API
const fetchAnswers = async () => {
const fetchAnswers = useCallback(async () => {
setLoading(true);
setError(null);
try {
const response = await getAnswersByQuestionId(questionId); // Utilisation de l'API
console.log("Data fetched from API:", response.data); // Vérification des données reçues
setAnswers(response.data); // Mise à jour du state avec les réponses
const response = await getAllAnswers();
setAnswers(response.data);
} catch (error) {
setError("Error fetching answers.");
console.error("Error fetching answers:", error);
} finally {
setLoading(false);
}
};
}, []);

useEffect(() => {
fetchAnswers();
}, [questionId]); // Appel à l'API lorsque le component se monte ou quand questionId change
}, [fetchAnswers]);

// Fonction pour supprimer une réponse
const handleDelete = async (id) => {
setLoading(true);
setError(null);
try {
await deleteAnswer(id);
fetchAnswers(); // Récupérer à nouveau les réponses après suppression
fetchAnswers();
} catch (error) {
setError("Error deleting answer.");
console.error("Error deleting answer:", error);
setLoading(false);
}
};

// Filtrage des réponses en fonction du terme de recherche
const filteredAnswers = answers.filter((answer) =>
answer.answers_text.toLowerCase().includes(searchTerm.toLowerCase())
);
const handleCreate = async () => {
if (newAnswerText.trim() === '') return;
setLoading(true);
setError(null);
try {
await createAnswer({answer_text: newAnswerText, questionId: questionId, isCorrect: false});
setNewAnswerText('');
fetchAnswers();
} catch (error) {
setError("Error creating answer.");
console.error("Error creating answer:", error);
setLoading(false);
}
};

const handleUpdate = async (id, updatedText) => {
if (updatedText.trim() === '') return;
setLoading(true);
setError(null);
try {
const answerToUpdate = answers.find(answer => answer.id === id);
if (answerToUpdate) {
await updateAnswer(id, {...answerToUpdate, answer_text: updatedText});
fetchAnswers();
}
} catch (error) {
setError("Error updating answer.");
console.error("Error updating answer:", error);
setLoading(false);
}
};

const handleView = (answer) => {
alert("show Answer : Travail en cours");
};

const filteredAnswers = searchTerm
? answers.filter((answer) =>
answer.answer_text && answer.answer_text.toLowerCase().includes(searchTerm.toLowerCase())
)
: answers;

return (
<>
<Row className="mb-3">
<Col>
<h4 className="text-start mb-3">{title}</h4>
<h4 className="text-start mb-3 text-dark">{title}</h4>
</Col>
<Col>
<Form.Control
type="text"
placeholder="Search for answer text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="text-dark"
/>
</Col>
</Row>
<Row className="mb-3">
<Col>
<Form.Control
type="text"
placeholder="Enter new answer"
value={newAnswerText}
onChange={(e) => setNewAnswerText(e.target.value)}
className="text-dark"
/>
</Col>
<Col>
<div className="search-bar">
<Form.Control
type="text"
placeholder="Search for answer text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} // Gestion de la recherche
/>
</div>
<Button variant="primary" onClick={handleCreate} disabled={loading}>
{loading ? <Spinner animation="border" size="sm"/> : "Add Answer"}
</Button>
</Col>
</Row>
<Table striped bordered hover className="small-font">
<thead>
<tr>
<th>Answer ID</th>
<th>Question ID</th>
<th>Answer Text</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{answers.length === 0 ? (
{error && <Alert variant="danger">{error}</Alert>}
<div style={{maxHeight: '500px', overflowY: 'scroll'}}>
<Table striped bordered hover className="small-font text-dark">
<thead>
<tr>
<td colSpan="4" className="text-center">
No answers available for this question
</td>
<th className="text-dark"></th>
<th className="text-dark">Question ID</th>
<th className="text-dark">Answer ID</th>
<th className="text-dark">Answer Text</th>
<th className="text-dark">Actions</th>
</tr>
) : (
filteredAnswers.length > 0 ? (
filteredAnswers.map((answer) => (
<tr key={answer.id}>
<td>{answer.id}</td>
<td>{answer.questionId}</td>
<td>{answer.answers_text}</td>
<td>
</thead>
<tbody>
{loading ? (
<tr>
<td colSpan="4" className="text-center text-dark">
<Spinner animation="border"/>
</td>
</tr>
) : filteredAnswers.length === 0 ? (
<tr>
<td colSpan="4" className="text-center text-dark">
No answers available
</td>
</tr>
) : (
filteredAnswers.map((answer, index) => (
<tr key={answer.answerId} className="text-dark">
<td className="text-dark">{index + 1}</td>
<td className="text-dark">{answer.question.questionId}</td>
<td className="text-dark">{answer.answerId}</td>
<td className="text-dark">{answer.answerText}</td>
<td className="text-dark">
<Button variant="success" size="sm" onClick={() => handleView(answer)}>View</Button>
<Button variant="warning" size="sm"
onClick={() => handleUpdate(answer.id, answer.answer_text)}
className="ms-2">Update</Button>
<Button
variant="danger"
onClick={() => handleDelete(answer.id)}
disabled={loading}
className="ms-2"
>
Delete
{loading ? <Spinner animation="border" size="sm"/> : "Delete"}
</Button>
</td>
</tr>
))
) : (
<tr>
<td colSpan="4" className="text-center">
No answers match your search
</td>
</tr>
)
)}
</tbody>
</Table>
)}
</tbody>
</Table>
</div>
</>
);
};

export default AnswersList;

export default AnswersList;
8 changes: 4 additions & 4 deletions qcmplusweb/src/components/Exam/Exam.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { getQuizzes, getQuestions, getAnswers, submitExamSession } from '../../services/ExamService';
import { Button, Form, Container, Row, Col, Card } from 'react-bootstrap';
import { useParams } from 'react-router-dom';
import React, {useEffect, useState} from 'react';
import {getAnswers, getQuestions, submitExamSession} from '../../services/ExamService';
import {Button, Col, Container, Form, Row} from 'react-bootstrap';
import {useParams} from 'react-router-dom';

const Exam = () => {
const { quizId } = useParams();
Expand Down
11 changes: 6 additions & 5 deletions qcmplusweb/src/pages/main/Main.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useEffect, useState, useCallback} from 'react';
import React, {useCallback, useEffect, useState} from 'react';
import {Button, Col, Container, Dropdown, Row} from 'react-bootstrap';
import Sidebar from "../../components/Sidebar/Sidebar";
import AddUser from "../../components/AddUser/AddUser";
Expand All @@ -12,8 +12,6 @@ import {getLoggedInUser, isAdminUser, isUserLoggedIn, logout} from "../../servic
import QuizList from "../../components/Quiz/QuizList";
import {useNavigate} from "react-router-dom";
import ExamSelected from "../../components/ExamSelected/ExamSelected";
import Exam from "../../components/Exam/Exam";
import Answer from "../../components/Answer/Answer";
import ExamsList from "../../components/ExamsList/ExamsList";
import AnswersList from "../../components/AnswersList/AnswersList";
import QuestionsList from "../../components/QuestionsList/QuestionsList";
Expand All @@ -30,6 +28,9 @@ const Main = () => {
const [showUserList, setShowUserList] = useState(true);
const [selectedItem, setSelectedItem] = useState(isAdmin ? 'AdminDashboard' : 'UserDashboard');

const showTopBtn = selectedItem === 'Trainee' || selectedItem === 'Admin';


const handleLogout = useCallback(() => {
logout();
navigate('/');
Expand All @@ -55,7 +56,7 @@ const Main = () => {

const renderContent = () => {
if (!showUserList) {
return <AddUser />;
return <AddUser/>;
}

switch (selectedItem) {
Expand Down Expand Up @@ -99,7 +100,7 @@ const Main = () => {
<Col xs={12} md={10} className="main-content">
<div>
<div className="header d-flex justify-content-end m-3">
{isAdmin && (
{isAdmin && showTopBtn && (
<>
<Button className="ExportUserBtn me-2">Export CSV</Button>
<Button className="ToggleUserBtn" onClick={() => setShowUserList(!showUserList)}>
Expand Down
28 changes: 8 additions & 20 deletions qcmplusweb/src/services/AnswerService.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import axios from 'axios';
import {API_BASE_URL} from "./AxiosInstance";
import axiosInstance, {API_BASE_URL} from "./AxiosInstance";

const ANSWER_REST_API_URL = API_BASE_URL+"/api/answers";
const ANSWER_REST_API_URL = API_BASE_URL + "/api/answers";

export const getAnswersByQuestionId = async (questionId) => {
return axios.get(`${ANSWER_REST_API_URL}/question/${questionId}`);
};

export const getAnswerById = async (id) => {
return axios.get(`${ANSWER_REST_API_URL}/${id}`);
};
export const getAllAnswers = () => axiosInstance.get(ANSWER_REST_API_URL);

export const createAnswer = async (answer) => {
return axios.post(ANSWER_REST_API_URL, answer);
};

export const updateAnswer = async (id, answer) => {
return axios.put(`${ANSWER_REST_API_URL}/${id}`, answer);
};

export const deleteAnswer = async (id) => {
return axios.delete(`${ANSWER_REST_API_URL}/${id}`);
};
export const getAnswersByQuestionId = (questionId) => axiosInstance.get(`${ANSWER_REST_API_URL}/question/${questionId}`);
export const getAnswerById = (id) => axiosInstance.get(`${ANSWER_REST_API_URL}/${id}`);
export const createAnswer = (answer) => axiosInstance.post(ANSWER_REST_API_URL, answer);
export const updateAnswer = (id, answer) => axiosInstance.put(`${ANSWER_REST_API_URL}/${id}`, answer);
export const deleteAnswer = (id) => axiosInstance.delete(`${ANSWER_REST_API_URL}/${id}`);
24 changes: 6 additions & 18 deletions qcmplusweb/src/services/ExamService.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import axios from 'axios';
import {API_BASE_URL} from "./AxiosInstance";
import axiosInstance, {API_BASE_URL} from "./AxiosInstance";

const EXAM_REST_API_URL = API_BASE_URL+"/api";
const EXAM_REST_API_URL = API_BASE_URL + "/api";

export const getQuizzes = async () => {
return axios.get(`${EXAM_REST_API_URL}/quizzes`);
};

export const getQuestions = async (quizId) => {
return axios.get(`${EXAM_REST_API_URL}/quizzes/${quizId}/questions`);
};

export const getAnswers = async (questionId) => {
return axios.get(`${EXAM_REST_API_URL}/questions/${questionId}/answers`);
};

export const submitExamSession = async (sessionData) => {
return axios.post(`${EXAM_REST_API_URL}/exam_sessions`, sessionData);
};
export const getQuizzes = () => axiosInstance.get(`${EXAM_REST_API_URL}/quizzes`);
export const getQuestions = (quizId) => axiosInstance.get(`${EXAM_REST_API_URL}/quizzes/${quizId}/questions`);
export const getAnswers = (questionId) => axiosInstance.get(`${EXAM_REST_API_URL}/questions/${questionId}/answers`);
export const submitExamSession = (sessionData) => axiosInstance.post(`${EXAM_REST_API_URL}/exam_sessions`, sessionData);
11 changes: 9 additions & 2 deletions src/main/java/com/pmn/qcmplus/controller/AnswerController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.pmn.qcmplus.model.Answer;
import com.pmn.qcmplus.service.AnswerService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -13,9 +15,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@PreAuthorize("hasAnyRole('ADMIN', 'USER')")
@RequestMapping("/api/answers")
public class AnswerController {

Expand All @@ -26,6 +27,12 @@ public AnswerController(AnswerService answerService) {
this.answerService = answerService;
}

@GetMapping
public ResponseEntity<List<Answer>> getAllAnswers() {
List<Answer> answers = answerService.getAllAnswers();
return ResponseEntity.ok(answers);
}

@GetMapping("/question/{questionId}")
public ResponseEntity<List<Answer>> getAnswersByQuestionId(@PathVariable Integer questionId) {
List<Answer> answers = answerService.getAnswersByQuestionId(questionId);
Expand Down

0 comments on commit 700e94d

Please sign in to comment.