Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
Scenarioswebapp
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
TokaKaram
Scenarioswebapp
Commits
482c9d87
Commit
482c9d87
authored
Jan 11, 2026
by
TokaKaram
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor quiz flow
parent
51309db4
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
149 additions
and
46 deletions
+149
-46
quizController.js
backend/src/controllers/quizController.js
+16
-1
userController.js
backend/src/controllers/userController.js
+2
-1
auth.js
backend/src/middleware/auth.js
+4
-4
Navbar.jsx
frontend/src/components/Layout/Navbar.jsx
+117
-34
QuestionCard.jsx
frontend/src/components/Quiz/QuestionCard.jsx
+2
-1
QuizTake.jsx
frontend/src/components/Quiz/QuizTake.jsx
+7
-4
Login.jsx
frontend/src/components/auth/Login.jsx
+1
-1
No files found.
backend/src/controllers/quizController.js
View file @
482c9d87
...
@@ -42,11 +42,13 @@ exports.getQuizById = async (req, res) => {
...
@@ -42,11 +42,13 @@ exports.getQuizById = async (req, res) => {
exports
.
startQuiz
=
async
(
req
,
res
)
=>
{
exports
.
startQuiz
=
async
(
req
,
res
)
=>
{
try
{
try
{
// console.log("Start Quiz");
const
[
existing
]
=
await
pool
.
execute
(
const
[
existing
]
=
await
pool
.
execute
(
'SELECT * FROM quiz_attempts WHERE user_id = ? AND quiz_id = ? AND status = "in_progress"'
,
'SELECT * FROM quiz_attempts WHERE user_id = ? AND quiz_id = ? AND status = "in_progress"'
,
[
req
.
user
.
id
,
req
.
params
.
id
]
[
req
.
user
.
id
,
req
.
params
.
id
]
);
);
if
(
existing
.
length
>
0
)
{
if
(
existing
.
length
>
0
)
{
// console.log("existing", existing);
return
res
.
json
({
return
res
.
json
({
attemptId
:
existing
[
0
].
id
,
attemptId
:
existing
[
0
].
id
,
message
:
"Continuing existing attempt"
,
message
:
"Continuing existing attempt"
,
...
@@ -56,6 +58,7 @@ exports.startQuiz = async (req, res) => {
...
@@ -56,6 +58,7 @@ exports.startQuiz = async (req, res) => {
"SELECT COUNT(*) as count FROM quiz_scenarios WHERE quiz_id = ?"
,
"SELECT COUNT(*) as count FROM quiz_scenarios WHERE quiz_id = ?"
,
[
req
.
params
.
id
]
[
req
.
params
.
id
]
);
);
// console.log("secenarios", scenarios[0].count);
const
[
result
]
=
await
pool
.
execute
(
const
[
result
]
=
await
pool
.
execute
(
"INSERT INTO quiz_attempts (user_id, quiz_id, total_questions) VALUES (?, ?, ?)"
,
"INSERT INTO quiz_attempts (user_id, quiz_id, total_questions) VALUES (?, ?, ?)"
,
[
req
.
user
.
id
,
req
.
params
.
id
,
scenarios
[
0
].
count
]
[
req
.
user
.
id
,
req
.
params
.
id
,
scenarios
[
0
].
count
]
...
@@ -69,6 +72,7 @@ exports.startQuiz = async (req, res) => {
...
@@ -69,6 +72,7 @@ exports.startQuiz = async (req, res) => {
exports
.
submitAnswer
=
async
(
req
,
res
)
=>
{
exports
.
submitAnswer
=
async
(
req
,
res
)
=>
{
try
{
try
{
const
{
attemptId
,
scenarioId
,
selectedAnswer
}
=
req
.
body
;
const
{
attemptId
,
scenarioId
,
selectedAnswer
}
=
req
.
body
;
// console.log("req.body", req.body);
const
[
scenarios
]
=
await
pool
.
execute
(
const
[
scenarios
]
=
await
pool
.
execute
(
"SELECT * FROM scenarios WHERE id = ?"
,
"SELECT * FROM scenarios WHERE id = ?"
,
[
scenarioId
]
[
scenarioId
]
...
@@ -86,7 +90,12 @@ exports.submitAnswer = async (req, res) => {
...
@@ -86,7 +90,12 @@ exports.submitAnswer = async (req, res) => {
"SELECT answers FROM quiz_attempts WHERE id = ?"
,
"SELECT answers FROM quiz_attempts WHERE id = ?"
,
[
attemptId
]
[
attemptId
]
);
);
let
answers
=
attempt
[
0
].
answers
?
JSON
.
parse
(
attempt
[
0
].
answers
)
:
{};
let
answers
=
attempt
[
0
].
answers
?
typeof
attempt
[
0
].
answers
===
"string"
?
JSON
.
parse
(
attempt
[
0
].
answers
)
:
attempt
[
0
].
answers
:
{};
answers
[
scenarioId
]
=
{
selectedAnswer
,
isCorrect
};
answers
[
scenarioId
]
=
{
selectedAnswer
,
isCorrect
};
const
correctCount
=
Object
.
values
(
answers
).
filter
(
const
correctCount
=
Object
.
values
(
answers
).
filter
(
(
a
)
=>
a
.
isCorrect
(
a
)
=>
a
.
isCorrect
...
@@ -101,6 +110,7 @@ exports.submitAnswer = async (req, res) => {
...
@@ -101,6 +110,7 @@ exports.submitAnswer = async (req, res) => {
rationale
:
scenario
.
best_answer_rationale
,
rationale
:
scenario
.
best_answer_rationale
,
});
});
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
log
(
"error"
,
error
);
res
.
status
(
500
).
json
({
message
:
"Server error"
,
error
:
error
.
message
});
res
.
status
(
500
).
json
({
message
:
"Server error"
,
error
:
error
.
message
});
}
}
};
};
...
@@ -108,6 +118,7 @@ exports.submitAnswer = async (req, res) => {
...
@@ -108,6 +118,7 @@ exports.submitAnswer = async (req, res) => {
exports
.
completeQuiz
=
async
(
req
,
res
)
=>
{
exports
.
completeQuiz
=
async
(
req
,
res
)
=>
{
try
{
try
{
const
{
attemptId
,
timeTaken
}
=
req
.
body
;
const
{
attemptId
,
timeTaken
}
=
req
.
body
;
// console.log("req.body", req.body);
const
[
attempts
]
=
await
pool
.
execute
(
const
[
attempts
]
=
await
pool
.
execute
(
"SELECT * FROM quiz_attempts WHERE id = ? AND user_id = ?"
,
"SELECT * FROM quiz_attempts WHERE id = ? AND user_id = ?"
,
[
attemptId
,
req
.
user
.
id
]
[
attemptId
,
req
.
user
.
id
]
...
@@ -116,6 +127,9 @@ exports.completeQuiz = async (req, res) => {
...
@@ -116,6 +127,9 @@ exports.completeQuiz = async (req, res) => {
return
res
.
status
(
404
).
json
({
message
:
"Attempt not found"
});
return
res
.
status
(
404
).
json
({
message
:
"Attempt not found"
});
}
}
const
attempt
=
attempts
[
0
];
const
attempt
=
attempts
[
0
];
// console.log("attempt", attempt);
// console.log("attempt.correct_answers=>", attempt.correct_answers);
// console.log(" attempt.total_questions=>", attempt.total_questions);
const
score
=
Math
.
round
(
const
score
=
Math
.
round
(
(
attempt
.
correct_answers
/
attempt
.
total_questions
)
*
100
(
attempt
.
correct_answers
/
attempt
.
total_questions
)
*
100
);
);
...
@@ -133,6 +147,7 @@ exports.completeQuiz = async (req, res) => {
...
@@ -133,6 +147,7 @@ exports.completeQuiz = async (req, res) => {
totalQuestions
:
attempt
.
total_questions
,
totalQuestions
:
attempt
.
total_questions
,
});
});
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
log
(
"erroir"
,
error
);
res
.
status
(
500
).
json
({
message
:
"Server error"
,
error
:
error
.
message
});
res
.
status
(
500
).
json
({
message
:
"Server error"
,
error
:
error
.
message
});
}
}
};
};
...
...
backend/src/controllers/userController.js
View file @
482c9d87
...
@@ -56,7 +56,8 @@ exports.getHistory = async (req, res) => {
...
@@ -56,7 +56,8 @@ exports.getHistory = async (req, res) => {
SELECT qa.*, q.title as quiz_title
SELECT qa.*, q.title as quiz_title
FROM quiz_attempts qa
FROM quiz_attempts qa
JOIN quizzes q ON qa.quiz_id = q.id
JOIN quizzes q ON qa.quiz_id = q.id
WHERE qa.user_id = ?
WHERE qa.user_id = ?
AND qa.total_questions > 0
ORDER BY qa.started_at DESC
ORDER BY qa.started_at DESC
`
,
`
,
[
req
.
user
.
id
]
[
req
.
user
.
id
]
...
...
backend/src/middleware/auth.js
View file @
482c9d87
...
@@ -3,16 +3,16 @@ const pool = require("../config/database");
...
@@ -3,16 +3,16 @@ const pool = require("../config/database");
module
.
exports
=
async
(
req
,
res
,
next
)
=>
{
module
.
exports
=
async
(
req
,
res
,
next
)
=>
{
try
{
try
{
console
.
log
(
"token"
,
req
.
header
(
"Authorization"
));
//
console.log("token", req.header("Authorization"));
const
token
=
req
.
header
(
"Authorization"
)?.
replace
(
"Bearer "
,
""
);
const
token
=
req
.
header
(
"Authorization"
)?.
replace
(
"Bearer "
,
""
);
console
.
log
(
"token without bearer"
,
token
);
//
console.log("token without bearer", token);
if
(
!
token
)
{
if
(
!
token
)
{
return
res
return
res
.
status
(
401
)
.
status
(
401
)
.
json
({
message
:
"No token, authorization denied"
});
.
json
({
message
:
"No token, authorization denied"
});
}
}
const
decoded
=
jwt
.
verify
(
token
,
process
.
env
.
JWT_SECRET
);
const
decoded
=
jwt
.
verify
(
token
,
process
.
env
.
JWT_SECRET
);
console
.
log
(
"decoded"
,
decoded
);
//
console.log("decoded", decoded);
const
[
users
]
=
await
pool
.
execute
(
const
[
users
]
=
await
pool
.
execute
(
"SELECT id, username, email, role FROM users WHERE id = ?"
,
"SELECT id, username, email, role FROM users WHERE id = ?"
,
[
decoded
.
id
]
[
decoded
.
id
]
...
@@ -23,7 +23,7 @@ module.exports = async (req, res, next) => {
...
@@ -23,7 +23,7 @@ module.exports = async (req, res, next) => {
req
.
user
=
users
[
0
];
req
.
user
=
users
[
0
];
next
();
next
();
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
log
(
"error"
,
error
);
//
console.log("error", error);
res
.
status
(
401
).
json
({
message
:
"Token is not valid error"
});
res
.
status
(
401
).
json
({
message
:
"Token is not valid error"
});
}
}
};
};
frontend/src/components/Layout/Navbar.jsx
View file @
482c9d87
import
{
NavLink
,
useNavigate
}
from
'react-router-dom'
;
import
{
NavLink
,
useNavigate
}
from
'react-router-dom'
;
import
{
useAuth
}
from
'../../context/AuthContext'
;
import
{
useAuth
}
from
'../../context/AuthContext'
;
import
{
FiHome
,
FiUser
,
FiLogOut
,
FiAward
,
FiClock
,
FiSettings
}
from
'react-icons/fi'
;
import
{
FiX
,
FiMenu
,
FiHome
,
FiUser
,
FiLogOut
,
FiAward
,
FiClock
,
FiSettings
}
from
'react-icons/fi'
;
import
{
useState
}
from
'react'
;
export
default
function
Navbar
()
{
export
default
function
Navbar
()
{
const
[
isOpen
,
setIsOpen
]
=
useState
(
false
);
const
toggleMenu
=
()
=>
setIsOpen
(
!
isOpen
);
const
navLinkClass
=
({
isActive
})
=>
`flex items-center gap-2 transition px-4 py-2 rounded-md
${
isActive
?
'text-indigo-600 bg-indigo-50 lg:bg-transparent'
:
'text-gray-600 hover:text-indigo-600 hover:bg-gray-50 lg:hover:bg-transparent'
}
`
;
const
{
user
,
logout
}
=
useAuth
();
const
{
user
,
logout
}
=
useAuth
();
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
...
@@ -10,43 +19,117 @@ export default function Navbar() {
...
@@ -10,43 +19,117 @@ export default function Navbar() {
logout
();
logout
();
navigate
(
'/login'
);
navigate
(
'/login'
);
};
};
return
(
return
(
<
nav
className=
"bg-white shadow-lg relative z-50"
>
<
nav
className=
"bg-white shadow-lg"
>
<
div
className=
"container mx-auto px-4"
>
<
div
className=
"container mx-auto px-4"
>
<
div
className=
"flex justify-between items-center h-16"
>
<
div
className=
"flex justify-between items-center h-16"
>
<
NavLink
to=
"/"
className=
"text-2xl font-bold text-indigo-600"
>
MCQ App
</
NavLink
>
{
/* Logo */
}
{
user
?
(
<
NavLink
to=
"/"
className=
"text-2xl font-bold text-indigo-600"
>
<
div
className=
"flex items-center gap-6"
>
MCQ App
<
NavLink
to=
"/"
className=
{
({
isActive
})
=>
(
isActive
?
'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition'
:
'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition'
)
}
>
</
NavLink
>
<
FiHome
/>
Quizzes
</
NavLink
>
{
/* Hamburger Button (Visible on mobile/tablet) */
}
<
NavLink
to=
"/leaderboard"
className=
{
({
isActive
})
=>
(
isActive
?
'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition'
:
'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition'
)
}
>
<
div
className=
"lg:hidden"
>
<
FiAward
/>
Leaderboard
<
button
onClick=
{
toggleMenu
}
className=
"text-gray-600 focus:outline-none p-2"
>
</
NavLink
>
{
isOpen
?
<
FiX
size=
{
24
}
/>
:
<
FiMenu
size=
{
24
}
/>
}
<
NavLink
to=
"/history"
className=
{
({
isActive
})
=>
(
isActive
?
'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition'
:
'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition'
)
}
>
</
button
>
<
FiClock
/>
History
</
div
>
</
NavLink
>
{
user
.
role
===
'admin'
&&
(
{
/* Desktop Menu */
}
<
NavLink
to=
"/admin"
className=
{
({
isActive
})
=>
(
isActive
?
'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition'
:
'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition'
)
}
>
<
div
className=
"hidden lg:flex items-center gap-6"
>
<
FiSettings
/>
Admin
<
NavContent
user=
{
user
}
handleLogout=
{
handleLogout
}
navLinkClass=
{
navLinkClass
}
/>
</
NavLink
>
</
div
>
)
}
</
div
>
<
NavLink
to=
"/profile"
className=
{
({
isActive
})
=>
(
isActive
?
'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition'
:
'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition'
)
}
>
<
FiUser
/>
{
user
.
username
}
{
/* Mobile & Tablet Menu (Dropdown) */
}
</
NavLink
>
<
div
<
button
onClick=
{
handleLogout
}
className=
"flex items-center gap-2 text-red-500 hover:text-red-700 transition"
>
className=
{
`lg:hidden overflow-hidden transition-all duration-300 ease-in-out ${
<
FiLogOut
/>
Logout
isOpen ? 'max-h-screen border-t py-4' : 'max-h-0'
</
button
>
}`
}
</
div
>
>
)
:
(
<
div
className=
"flex flex-col gap-2"
>
<
div
className=
"flex items-center gap-4"
>
<
NavContent
<
NavLink
to=
"/login"
className=
"text-gray-600 hover:text-indigo-600 transition"
>
Login
</
NavLink
>
user=
{
user
}
<
NavLink
to=
"/register"
className=
"bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition"
>
Sign Up
</
NavLink
>
handleLogout=
{
handleLogout
}
</
div
>
navLinkClass=
{
navLinkClass
}
)
}
isMobile=
{
true
}
closeMenu=
{
()
=>
setIsOpen
(
false
)
}
/>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
nav
>
</
nav
>
);
);
};
// مكون داخلي لتجنب تكرار الروابط
const
NavContent
=
({
user
,
handleLogout
,
navLinkClass
,
isMobile
,
closeMenu
})
=>
{
if
(
!
user
)
{
return
(
<
div
className=
{
`flex ${isMobile ? 'flex-col gap-2' : 'items-center gap-4'}`
}
>
<
NavLink
to=
"/login"
onClick=
{
closeMenu
}
className=
"text-gray-600 hover:text-indigo-600 px-4 py-2"
>
Login
</
NavLink
>
<
NavLink
to=
"/register"
onClick=
{
closeMenu
}
className=
"bg-indigo-600 text-white px-4 py-2 rounded-lg text-center"
>
Sign Up
</
NavLink
>
</
div
>
);
}
return
(
<>
<
NavLink
to=
"/"
onClick=
{
closeMenu
}
className=
{
navLinkClass
}
><
FiHome
/>
Quizzes
</
NavLink
>
<
NavLink
to=
"/leaderboard"
onClick=
{
closeMenu
}
className=
{
navLinkClass
}
><
FiAward
/>
Leaderboard
</
NavLink
>
<
NavLink
to=
"/history"
onClick=
{
closeMenu
}
className=
{
navLinkClass
}
><
FiClock
/>
History
</
NavLink
>
{
user
.
role
===
'admin'
&&
(
<
NavLink
to=
"/admin"
onClick=
{
closeMenu
}
className=
{
navLinkClass
}
><
FiSettings
/>
Admin
</
NavLink
>
)
}
<
NavLink
to=
"/profile"
onClick=
{
closeMenu
}
className=
{
navLinkClass
}
><
FiUser
/>
{
user
.
username
}
</
NavLink
>
<
button
onClick=
{
()
=>
{
handleLogout
();
if
(
isMobile
)
closeMenu
();
}
}
className=
"flex items-center gap-2 text-red-500 hover:text-red-700 transition px-4 py-2"
>
<
FiLogOut
/>
Logout
</
button
>
</>
);
// return (
// <nav className="bg-white shadow-lg">
// <div className="container mx-auto px-4">
// <div className="flex justify-between items-center h-16">
// <NavLink to="/" className="text-2xl font-bold text-indigo-600">MCQ App</NavLink>
// {user ? (
// <div className="flex items-center gap-6">
// <NavLink to="/" className={({ isActive }) => (isActive ? 'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition' : 'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition')} >
// <FiHome /> Quizzes
// </NavLink>
// <NavLink to="/leaderboard" className={({ isActive }) => (isActive ? 'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition' : 'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition')}>
// <FiAward /> Leaderboard
// </NavLink>
// <NavLink to="/history" className={({ isActive }) => (isActive ? 'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition' : 'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition')}>
// <FiClock /> History
// </NavLink>
// {user.role === 'admin' && (
// <NavLink to="/admin" className={({ isActive }) => (isActive ? 'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition' : 'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition')}>
// <FiSettings /> Admin
// </NavLink>
// )}
// <NavLink to="/profile" className={({ isActive }) => (isActive ? 'text-indigo-600 flex items-center gap-2 hover:text-indigo-600 transition' : 'flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition')}>
// <FiUser /> {user.username}
// </NavLink>
// <button onClick={handleLogout} className="flex items-center gap-2 text-red-500 hover:text-red-700 transition">
// <FiLogOut /> Logout
// </button>
// </div>
// ) : (
// <div className="flex items-center gap-4">
// <NavLink to="/login" className="text-gray-600 hover:text-indigo-600 transition">Login</NavLink>
// <NavLink to="/register" className="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition">Sign Up</NavLink>
// </div>
// )}
// </div>
// </div>
// </nav>
// );
}
}
\ No newline at end of file
frontend/src/components/Quiz/QuestionCard.jsx
View file @
482c9d87
import
{
useMemo
,
useState
}
from
"react"
;
import
{
useMemo
,
useState
}
from
"react"
;
export
default
function
QuestionCard
({
scenario
,
selectedAnswer
,
onSelectAnswer
,
showResult
,
answerResult
})
{
export
default
function
QuestionCard
({
scenario
,
selectedAnswer
,
onSelectAnswer
,
showResult
,
answerResult
})
{
const
givensTable
=
typeof
scenario
.
givens_table
===
'string'
?
JSON
.
parse
(
scenario
.
givens_table
)
:
scenario
.
givens_table
;
// console.log("giventtable=>", scenario);
const
givensTable
=
typeof
scenario
?.
givens_table
===
'string'
?
JSON
.
parse
(
scenario
.
givens_table
)
:
scenario
.
givens_table
;
const
options
=
useMemo
(()
=>
{
const
options
=
useMemo
(()
=>
{
return
[
return
[
...
...
frontend/src/components/Quiz/QuizTake.jsx
View file @
482c9d87
...
@@ -24,6 +24,7 @@ export default function QuizTake() {
...
@@ -24,6 +24,7 @@ export default function QuizTake() {
const
loadQuiz
=
async
()
=>
{
const
loadQuiz
=
async
()
=>
{
try
{
try
{
const
quizRes
=
await
api
.
get
(
`/quiz/
${
id
}
`
);
const
quizRes
=
await
api
.
get
(
`/quiz/
${
id
}
`
);
console
.
log
(
"quizRes"
,
quizRes
);
setQuiz
(
quizRes
.
data
);
setQuiz
(
quizRes
.
data
);
const
startRes
=
await
api
.
post
(
`/quiz/
${
id
}
/start`
);
const
startRes
=
await
api
.
post
(
`/quiz/
${
id
}
/start`
);
setAttemptId
(
startRes
.
data
.
attemptId
);
setAttemptId
(
startRes
.
data
.
attemptId
);
...
@@ -75,21 +76,23 @@ export default function QuizTake() {
...
@@ -75,21 +76,23 @@ export default function QuizTake() {
if
(
loading
)
return
<
Loading
/>;
if
(
loading
)
return
<
Loading
/>;
if
(
!
quiz
)
return
null
;
if
(
!
quiz
)
return
null
;
const
currentScenario
=
quiz
.
scenarios
[
currentIndex
];
const
currentScenario
=
quiz
?
.
scenarios
[
currentIndex
];
const
progress
=
((
currentIndex
+
1
)
/
quiz
.
scenarios
.
length
)
*
100
;
const
progress
=
((
currentIndex
+
1
)
/
quiz
?
.
scenarios
.
length
)
*
100
;
return
(
return
(
<
div
className=
"max-w-4xl mx-auto"
>
<
div
className=
"max-w-4xl mx-auto"
>
<
div
className=
"bg-white rounded-xl shadow-lg p-6 mb-6"
>
<
div
className=
"bg-white rounded-xl shadow-lg p-6 mb-6"
>
<
div
className=
"flex justify-between items-center mb-4"
>
<
div
className=
"flex justify-between items-center mb-4"
>
<
h1
className=
"text-2xl font-bold text-gray-800"
>
{
quiz
.
title
}
</
h1
>
<
h1
className=
"text-2xl font-bold text-gray-800"
>
{
quiz
.
title
}
</
h1
>
<
span
className=
"text-gray-500"
>
Question
{
currentIndex
+
1
}
of
{
quiz
.
scenarios
.
length
}
</
span
>
<
span
className=
"text-gray-500"
>
Question
{
quiz
.
scenarios
.
length
===
0
?
0
:
currentIndex
+
1
}
of
{
quiz
.
scenarios
.
length
}
</
span
>
</
div
>
</
div
>
<
div
className=
"w-full bg-gray-200 rounded-full h-2"
>
<
div
className=
"w-full bg-gray-200 rounded-full h-2"
>
<
div
className=
"bg-indigo-600 h-2 rounded-full transition-all duration-300"
style=
{
{
width
:
`${progress}%`
}
}
></
div
>
<
div
className=
"bg-indigo-600 h-2 rounded-full transition-all duration-300"
style=
{
{
width
:
`${progress}%`
}
}
></
div
>
</
div
>
</
div
>
</
div
>
</
div
>
<
QuestionCard
scenario=
{
currentScenario
}
selectedAnswer=
{
selectedAnswer
}
onSelectAnswer=
{
setSelectedAnswer
}
showResult=
{
showResult
}
answerResult=
{
answerResult
}
/>
{
quiz
.
scenarios
.
length
===
0
?
<
div
className=
"bg-white rounded-xl shadow-lg p-6 mb-6"
>
No questions found for this quiz.
</
div
>
:
<
QuestionCard
scenario=
{
currentScenario
}
selectedAnswer=
{
selectedAnswer
}
onSelectAnswer=
{
setSelectedAnswer
}
showResult=
{
showResult
}
answerResult=
{
answerResult
}
/>
}
<
div
className=
"mt-6 flex justify-end"
>
<
div
className=
"mt-6 flex justify-end"
>
{
!
showResult
?
(
{
!
showResult
?
(
<
button
onClick=
{
handleSubmitAnswer
}
disabled=
{
submitting
||
!
selectedAnswer
}
className=
"bg-indigo-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-indigo-700 transition flex items-center gap-2 disabled:opacity-50"
>
<
button
onClick=
{
handleSubmitAnswer
}
disabled=
{
submitting
||
!
selectedAnswer
}
className=
"bg-indigo-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-indigo-700 transition flex items-center gap-2 disabled:opacity-50"
>
...
...
frontend/src/components/auth/Login.jsx
View file @
482c9d87
...
@@ -16,7 +16,7 @@ export default function Login() {
...
@@ -16,7 +16,7 @@ export default function Login() {
setLoading
(
true
);
setLoading
(
true
);
try
{
try
{
const
r
=
await
login
(
email
,
password
);
const
r
=
await
login
(
email
,
password
);
console
.
log
(
"r=>"
,
r
);
//
console.log("r=>",r);
toast
.
success
(
'Login successful!'
);
toast
.
success
(
'Login successful!'
);
if
(
r
.
user
.
role
===
'admin'
)
if
(
r
.
user
.
role
===
'admin'
)
navigate
(
'/admin'
);
navigate
(
'/admin'
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment