live exam changes

This commit is contained in:
skillens.ai 2025-11-01 23:30:05 +05:30
parent 4c260ac15c
commit 4346a00435
7 changed files with 1485 additions and 181 deletions

View File

@ -1,12 +1,12 @@
robots.txt,1761396900570,bfe106a3fb878dc83461c86818bf74fc1bdc7f28538ba613cd3e775516ce8b49
manifest.json,1761396900568,a9350a49aaac9fe94d3dd77b8270cc998c04ab97944a606189675022431faa51
favicon.svg,1761396900561,a2a4880301751061a600b0bfc5c26fc413aed41e581516c4fa976bcb7fff6663
service-worker.js,1761967891625,f7744b581ca227e2a41f575b69dea1ab6d18cce982b6a960d28e71fadc44f741
static/media/tru-fals-icon.d0b962d8.svg,1761967891590,a1deab7e5db8e8a94a9c5812dcde5f8cbfbe5da790da8500bf106980b9c79c33
precache-manifest.7cad5215247b24b3b19ef8edf1d4fe9a.js,1761967891625,60375439202070d0556de010cbf10824235f73b4c2f63f9948438a94df2a5edf
index.html,1761967891604,52b58a29314f6829b20c7e23e4f7fd5b5b40e3c7bc9949341548becbc6ef7879
static/media/student-engaged.53fb7b0b.svg,1761967891589,5f8e01d1a5efcbdf3aa00ed07de843ab84404a28b672ca91f4d70d89fa8b37cf
static/media/sub-ques-icon.366b4f1e.svg,1761967891590,8e397611007ec5db7581a4c1bcca006950390ddf652cae209bb3973a645af425
service-worker.js,1762008702040,a2abab7acf1300ad85eef61ef6c7d661128c243cda0101a23720484c5ffc849d
precache-manifest.e24f7d8cb814a4ad35948f841cb14aeb.js,1762008702039,a30d4da0b56949ddfdd19623bb0d91f89672375f67930b9682d636dd2261488a
asset-manifest.json,1762008702040,32012ef3ae36fa076ed8bf5a21d65fe5d7b3b438950ba52ffe504796dd94c53a
index.html,1762008702039,30d577992c5d76398025ffe172958305a228bb89cacd3db1be0e847e5a3e7f75
static/media/sub-ques-icon.366b4f1e.svg,1762008701928,8e397611007ec5db7581a4c1bcca006950390ddf652cae209bb3973a645af425
static/media/tru-fals-icon.d0b962d8.svg,1762008701928,a1deab7e5db8e8a94a9c5812dcde5f8cbfbe5da790da8500bf106980b9c79c33
assets/images/locale-icon.svg,1761396900579,610498c7ca3b5800d268b1654473f0b1d79de2c6493a7c6982bef90456d35179
assets/images/icons/icon-96x96.png,1761396900602,43a8ac4df8945d7a44e3e4911032214f01eaea92267baa31a93e77942b424c73
assets/images/icons/icon-72x72.png,1761396900600,a42f1df8ab0a8fe921573976d49158e1a2c52fe430460c869947f221aba30a94
@ -16,52 +16,52 @@ assets/images/icons/icon-192x192.png,1761396900592,ac9fe46fbeb4c54fb3c838b645380
assets/images/icons/icon-152x152.png,1761396900590,95a5a117fbd5640f1f1f13c9923398545e3f5b66734ff6c8ce67c942319a8b67
assets/images/icons/icon-144x144.png,1761396900588,e42168e0e1abb5bd7811ebe1b5a2183d0ce287bf266c2efd8d97a23d23ca8d00
assets/images/icons/icon-128x128.png,1761396900585,45b577c86e7c03fef868bfa3f96810c5b36f31156c32c0d85620d2e6fe1fc85b
static/media/translate.610ad011.svg,1761967891590,c06f8a3d0c976b02429ce805d8a4944571ab0128ed2ce49c2d3ca134511ed120
asset-manifest.json,1761967891625,e86b1b4d49803659560b3c6cc8a81dd59bc4d766ec96f72d36ce1c06ab45c30b
static/media/quizexam.5545802e.svg,1761967891584,dfc1278bfcd264264a4d0e0e0247c229335abf0573439c5e9829c8607aacd569
static/media/questions-icon.3d3c1aaf.svg,1761967891588,197f459a359b00c1c44b6ddd71cbc9160f593d1fe08534e3711e9d72a2956ef1
static/media/PracticeKiaLogo.a8336af5.svg,1761967891581,1a592518bcfabb2f86669d06ae2fb7b2948bbdbfaf5a80d138ab30f4e482a0f8
static/media/performance.88855f12.svg,1761967891587,03fc1b19005084049382091745d3f1f29a447763091ad4a9bc5d097c338671c9
static/media/practice.f05e6f00.svg,1761967891584,13b094bcbbb8c50906b1ee0ce68305059e99319464dd4147b4c2194dbb460475
static/media/perf-icon.1597a235.svg,1761967891589,7183482b785de425506f49deb27bba02e906132ac7ce5af01f7da8452ef64f28
static/media/question.0c505ed9.svg,1761967891585,29938066f93476c487414bb7a5dd5227d891c8ab8e115a74a7e7ffccd8d87b36
static/media/mul-cho-icon.b3dc9ea9.svg,1761967891589,e8edbceb0eb49f7629cf0b8edf599f9347acbf0c6d1df2fa6145cbed03f37e1f
static/media/locale-icon.b3596424.svg,1761967891588,68618c76952aa4c5f2623bb010514871688960bb3b9edbcda0eab7eb75442054
static/media/OdiSVGlogo.f0834bb1.svg,1761967891583,3ec1cab31a32db378894d796afedaee7c35f8ff99dfb1039b0bb757225a47235
static/media/practice-icon.bf603115.svg,1761967891588,27b6a729d91914d899f4b3c1f817e26ac58d2ec0be54dc9b93c01f28adcdd00c
static/media/GrayscalePKLogo.6bb74404.svg,1761967891583,576b38300e49eda6407adce420001e7ad0333cca317eb668aff457c3258dcca2
static/media/getFetch.2b2b7da4.cjs,1761967891581,b2d82abee5b8af22b81d67fc20b3feef1eaaf04585dbc24ec755e3304c469096
static/media/dashboard.8ec7624b.svg,1761967891583,5a5b5d4bf416d414842bd5eac30432b592342a392450e52943f067db4077bec1
static/media/exam-icon.10f48851.svg,1761967891588,b766e9975582af716870a844f8deabd4d80e33a47c400d36cee6bc4840062693
static/media/feature-2.36f8d7e2.webp,1761967891581,fc1c038517abf3b731ff3a4675cbaf1a6aa1150ca970762dd0ffc4199b92b75c
static/media/delete-icon.da38c0f4.svg,1761967891595,8078772ee88a0588989eec477da1ca949430f3f06efaa9c2db860b74c65f6722
static/media/class-icon.6afd34b5.svg,1761967891588,a2f492c6c9c7b5201773062dfe90c238c12bc00bec097dc06141201f02b9588e
static/media/classes.3b73dba0.svg,1761967891585,2abfbb7015d5bccb8640fd0614d3782c048672c9543fe17d127db63287ac141d
static/media/Checkmark.1356376c.svg,1761967891595,aa56f27c8198bcae3236a881a7134cd3b7d3dbb048ec75654e8ce2d4710ce027
static/media/batch.3fcff66e.svg,1761967891585,f29038480286f091e3805a43f90ca5a70a13bbcef3076831e0b29b1fcb61d862
static/media/auth-BG.2835584f.svg,1761967891580,238d3a2ff1aead1c4aaed0e07d23d2c1164f8be0ef2d5fced5c5ade370f3c5bf
static/media/batch-icon.bf664771.svg,1761967891589,7519e2a92f436a2f46ef7420d721e24df2837d7fce7fe8c30f4bc12980b3d1a0
static/media/mul-res-icon.8ef3b097.svg,1761967891589,1f0a79350cb5546361ef02af1657ad36efeff833fd7bb61db88a20e127a290b4
static/js/4.568469c1.chunk.js,1761967891598,7f998d124bddb5cb8e916a7bb559ddd2f5c133920e79651382bbabf25affc9c4
static/media/add-circle.0011f2bc.svg,1761967891588,d9e5d90e8de1ce16df5720b43ca79543036fcf1a6cd6439a9b15c5a97d269f17
static/js/4.568469c1.chunk.js.map,1761967891628,f626752d2d2cffe76387c84c314248f1ab02d9c6a79a16442a98e124c8f7d5fa
static/js/runtime-main.2bdeab81.js.map,1761967891625,a6cd518ca4773cf5562057dad7acb281edac1b802572abcdc6ee19c33f02b744
static/js/3.d75ced33.chunk.js.map,1761967891628,a12fbfb03ced36f6a8c055bf8b3961a9170b3676c8e4f545921152b23e9bc4dd
static/js/3.d75ced33.chunk.js,1761967891595,c68f66b973c4099ec168a05edb6212303bf31b91c57a2adc0a46595d31262355
static/js/runtime-main.2bdeab81.js,1761967891595,5c04066aee33f757af11aff4783499664f995e641476c3c1552c373bf87171bb
static/js/2.a43d95bf.chunk.js.LICENSE.txt,1761967891596,9c84bc4d2f8584d32d75e01e0317e22af1e39f5ac5ded3e2e4e34984704c172b
static/media/logo.0dd03933.png,1761967891583,b9716ed1f565a052edc1154a207334de81856339e8ca43d5d8f51041f3785085
static/media/feature-1.ea5c34ea.svg,1761967891581,d802659785b69508e521d5543fca40a58cb9076521ffc3e6b006f4786b8079f8
static/media/feature-3.3d5d81e9.svg,1761967891581,ba23393d89776cb78f7a559903baff48be76665abd99bdd4e5ea103c5e5ed88b
static/js/main.e17b1c66.chunk.js,1761967891590,7881f9d9a302a7f12898e4731f9aff65871aae813e3be47dbeaa5764d3e5b8e4
static/css/main.b90ebd71.chunk.css,1761967891590,47c0c4a15920e9cea3ceba32d4bc59829a376a7f4347c15b82d7e8878d59008d
static/css/4.629184c9.chunk.css,1761967891598,dfeba86da1256521df7a877a265d56d314f65016837f30878338c2b7fc62a6ab
static/css/2.561a8df6.chunk.css,1761967891595,c87247fc5cf38902aee0ee29244f6ab0c4bc14e5a0a1d6860cd771410dc826f8
static/css/3.6c5a3051.chunk.css,1761967891595,1ae150a22b1d42322775be9055605f93c75188e7081cfbdb206a59fc2f5ab8f1
static/css/main.b90ebd71.chunk.css.map,1761967891600,bff960067cfb59c88b8ec495e5b5334532e05ae898ad3f3a78ee293f74fb7855
static/js/main.e17b1c66.chunk.js.map,1761967891604,e6ee670831252a3e7da77d48ece9502f92c19fd2d0596cf366bb0f597c320ca2
static/css/4.629184c9.chunk.css.map,1761967891625,b82aa57e6e357dbb20067334e544e9b30d20ed0d928c13e6c61e1747e6f5a205
static/css/3.6c5a3051.chunk.css.map,1761967891604,bcc301c3b3b46c96afc0fc028a6aa3cd299ccda538d7c6cb9ebc87f8de535796
static/css/2.561a8df6.chunk.css.map,1761967891600,ed9f46fc5b36bd835967cfd533fb33fd9ceec9c3aadcbe6c12bfb90b3ad1bc83
static/js/2.a43d95bf.chunk.js,1761967891596,e4a75b184cbb51bd95baa7cb97581e00e72d83a37f15540e9d8f845c7d90eb6a
static/js/2.a43d95bf.chunk.js.map,1761967891636,154c414780aa8752ddddd74528ff704d5ae7243dcdebea39ea84400667c889c1
static/media/translate.610ad011.svg,1762008701933,c06f8a3d0c976b02429ce805d8a4944571ab0128ed2ce49c2d3ca134511ed120
static/media/student-engaged.53fb7b0b.svg,1762008701925,5f8e01d1a5efcbdf3aa00ed07de843ab84404a28b672ca91f4d70d89fa8b37cf
static/media/quizexam.5545802e.svg,1762008701907,dfc1278bfcd264264a4d0e0e0247c229335abf0573439c5e9829c8607aacd569
static/media/questions-icon.3d3c1aaf.svg,1762008701925,197f459a359b00c1c44b6ddd71cbc9160f593d1fe08534e3711e9d72a2956ef1
static/media/PracticeKiaLogo.a8336af5.svg,1762008701893,1a592518bcfabb2f86669d06ae2fb7b2948bbdbfaf5a80d138ab30f4e482a0f8
static/media/question.0c505ed9.svg,1762008701902,29938066f93476c487414bb7a5dd5227d891c8ab8e115a74a7e7ffccd8d87b36
static/media/practice-icon.bf603115.svg,1762008701926,27b6a729d91914d899f4b3c1f817e26ac58d2ec0be54dc9b93c01f28adcdd00c
static/media/perf-icon.1597a235.svg,1762008701926,7183482b785de425506f49deb27bba02e906132ac7ce5af01f7da8452ef64f28
static/media/practice.f05e6f00.svg,1762008701910,13b094bcbbb8c50906b1ee0ce68305059e99319464dd4147b4c2194dbb460475
static/media/OdiSVGlogo.f0834bb1.svg,1762008701902,3ec1cab31a32db378894d796afedaee7c35f8ff99dfb1039b0bb757225a47235
static/media/mul-res-icon.8ef3b097.svg,1762008701927,1f0a79350cb5546361ef02af1657ad36efeff833fd7bb61db88a20e127a290b4
static/media/performance.88855f12.svg,1762008701915,03fc1b19005084049382091745d3f1f29a447763091ad4a9bc5d097c338671c9
static/media/mul-cho-icon.b3dc9ea9.svg,1762008701928,e8edbceb0eb49f7629cf0b8edf599f9347acbf0c6d1df2fa6145cbed03f37e1f
static/media/locale-icon.b3596424.svg,1762008701924,68618c76952aa4c5f2623bb010514871688960bb3b9edbcda0eab7eb75442054
static/media/GrayscalePKLogo.6bb74404.svg,1762008701902,576b38300e49eda6407adce420001e7ad0333cca317eb668aff457c3258dcca2
static/media/getFetch.2b2b7da4.cjs,1762008701902,b2d82abee5b8af22b81d67fc20b3feef1eaaf04585dbc24ec755e3304c469096
static/media/feature-2.36f8d7e2.webp,1762008701902,fc1c038517abf3b731ff3a4675cbaf1a6aa1150ca970762dd0ffc4199b92b75c
static/media/exam-icon.10f48851.svg,1762008701925,b766e9975582af716870a844f8deabd4d80e33a47c400d36cee6bc4840062693
static/media/delete-icon.da38c0f4.svg,1762008701941,8078772ee88a0588989eec477da1ca949430f3f06efaa9c2db860b74c65f6722
static/media/dashboard.8ec7624b.svg,1762008701904,5a5b5d4bf416d414842bd5eac30432b592342a392450e52943f067db4077bec1
static/media/classes.3b73dba0.svg,1762008701907,2abfbb7015d5bccb8640fd0614d3782c048672c9543fe17d127db63287ac141d
static/media/Checkmark.1356376c.svg,1762008701963,aa56f27c8198bcae3236a881a7134cd3b7d3dbb048ec75654e8ce2d4710ce027
static/media/batch.3fcff66e.svg,1762008701910,f29038480286f091e3805a43f90ca5a70a13bbcef3076831e0b29b1fcb61d862
static/media/auth-BG.2835584f.svg,1762008701899,238d3a2ff1aead1c4aaed0e07d23d2c1164f8be0ef2d5fced5c5ade370f3c5bf
static/media/class-icon.6afd34b5.svg,1762008701924,a2f492c6c9c7b5201773062dfe90c238c12bc00bec097dc06141201f02b9588e
static/media/batch-icon.bf664771.svg,1762008701926,7519e2a92f436a2f46ef7420d721e24df2837d7fce7fe8c30f4bc12980b3d1a0
static/media/add-circle.0011f2bc.svg,1762008701927,d9e5d90e8de1ce16df5720b43ca79543036fcf1a6cd6439a9b15c5a97d269f17
static/js/runtime-main.2bdeab81.js,1762008701965,5c04066aee33f757af11aff4783499664f995e641476c3c1552c373bf87171bb
static/js/runtime-main.2bdeab81.js.map,1762008702040,a6cd518ca4773cf5562057dad7acb281edac1b802572abcdc6ee19c33f02b744
static/js/4.568469c1.chunk.js.map,1762008702398,f626752d2d2cffe76387c84c314248f1ab02d9c6a79a16442a98e124c8f7d5fa
static/js/4.568469c1.chunk.js,1762008701978,7f998d124bddb5cb8e916a7bb559ddd2f5c133920e79651382bbabf25affc9c4
static/js/3.d75ced33.chunk.js.map,1762008702398,a12fbfb03ced36f6a8c055bf8b3961a9170b3676c8e4f545921152b23e9bc4dd
static/js/3.d75ced33.chunk.js,1762008701975,c68f66b973c4099ec168a05edb6212303bf31b91c57a2adc0a46595d31262355
static/js/2.a43d95bf.chunk.js.LICENSE.txt,1762008701979,9c84bc4d2f8584d32d75e01e0317e22af1e39f5ac5ded3e2e4e34984704c172b
static/media/logo.0dd03933.png,1762008701902,b9716ed1f565a052edc1154a207334de81856339e8ca43d5d8f51041f3785085
static/media/feature-1.ea5c34ea.svg,1762008701899,d802659785b69508e521d5543fca40a58cb9076521ffc3e6b006f4786b8079f8
static/media/feature-3.3d5d81e9.svg,1762008701901,ba23393d89776cb78f7a559903baff48be76665abd99bdd4e5ea103c5e5ed88b
static/js/main.8ff1a521.chunk.js,1762008701955,d26ec0fa3004f79313bee1eef7ae5c387092d9d85e74040358e1d6fef1f3345f
static/css/main.bc6737c7.chunk.css,1762008701945,76e80fb65abfd974532458046d6fad237c3ce9360fc1f06058c9a5497c2cb2ab
static/css/4.629184c9.chunk.css,1762008701975,dfeba86da1256521df7a877a265d56d314f65016837f30878338c2b7fc62a6ab
static/css/2.561a8df6.chunk.css,1762008701966,c87247fc5cf38902aee0ee29244f6ab0c4bc14e5a0a1d6860cd771410dc826f8
static/css/3.6c5a3051.chunk.css,1762008701972,1ae150a22b1d42322775be9055605f93c75188e7081cfbdb206a59fc2f5ab8f1
static/css/4.629184c9.chunk.css.map,1762008701995,b82aa57e6e357dbb20067334e544e9b30d20ed0d928c13e6c61e1747e6f5a205
static/js/main.8ff1a521.chunk.js.map,1762008702041,6165ce4addd97c1db3468075950c1febf7c6bb67dd712a42c3a3af585dbe0546
static/css/3.6c5a3051.chunk.css.map,1762008701984,bcc301c3b3b46c96afc0fc028a6aa3cd299ccda538d7c6cb9ebc87f8de535796
static/css/2.561a8df6.chunk.css.map,1762008701982,ed9f46fc5b36bd835967cfd533fb33fd9ceec9c3aadcbe6c12bfb90b3ad1bc83
static/css/main.bc6737c7.chunk.css.map,1762008701980,9e488d986552c43427a0a668fd428eb0cf64772473d95e905de639079896eeda
static/js/2.a43d95bf.chunk.js,1762008701969,e4a75b184cbb51bd95baa7cb97581e00e72d83a37f15540e9d8f845c7d90eb6a
static/js/2.a43d95bf.chunk.js.map,1762008702412,154c414780aa8752ddddd74528ff704d5ae7243dcdebea39ea84400667c889c1

1133
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@
"i18next": "^21.9.2",
"i18next-http-backend": "^1.4.4",
"immutability-helper": "^3.1.1",
"katex": "^0.12.0",
"katex": "^0.13.24",
"less": "^2.7.2",
"lodash": "^4.17.20",
"marked-react": "^1.3.1",
@ -41,6 +41,7 @@
"react-i18next": "^11.18.6",
"react-katex": "^2.0.2",
"react-latex": "^2.0.0",
"react-markdown": "^6.0.3",
"react-masonry-css": "^1.0.16",
"react-mathquill": "^1.0.1",
"react-plotly.js": "^2.5.0",
@ -49,7 +50,10 @@
"react-router-dom": "^5.3.4",
"react-scripts": "^3.4.3",
"react-simplemde-editor": "^5.0.2",
"rehype-katex": "^4.0.0",
"remark-math": "^3.0.1",
"rxjs": "^6.6.3",
"unified": "^9.2.2",
"uuid": "^9.0.0",
"yup": "^0.29.3",
"zod": "^3.20.2"

View File

@ -15,6 +15,7 @@ import FirebaseAction from './component/Login/FirebaseAction';
// import 'antd/dist/antd.less'; // imported inside App.less
import './App.less';
import 'katex/dist/katex.min.css';
// import AllQuestions from './component/Questions/AllQuestions';
// import MyQuestions from './component/Questions/MyQuestions';

View File

@ -14,7 +14,8 @@ import {
Statistic,
Col,
Row,
Tag
Tag,
Typography
} from "antd";
import { Link } from "react-router-dom";
import QuestionDetail from "./QuestionDetail";
@ -137,20 +138,22 @@ class AttemptExam extends React.Component {
}
showDetailQuestion(question) {
// Mark the question as visited
question.isVisited = true;
this.state.sections[question.sectionIndx].questions[question.secQIndex] =
question;
// Update the question in both section and allQuestions arrays
this.state.sections[question.sectionIndx].questions[question.secQIndex] = question;
this.state.allQuestions[question.index - 1] = question;
let tempSections = this.state.sections;
let tempAllQuestions = this.state.allQuestions;
// Save the current section index for returning to the same tab
this.setState({
showQuestion: true,
activeDetailQuestion: question,
activeQuestionIndex: question.index - 1,
sections: tempSections,
allQuestions: tempAllQuestions,
sections: this.state.sections,
allQuestions: this.state.allQuestions,
lastActiveSection: question.sectionIndx // Save the section index
});
this.forceUpdate();
}
onQuestionClick(index) {
@ -159,11 +162,14 @@ class AttemptExam extends React.Component {
}
onTabPaneClick() {
this.setState({
showQuestion: false,
activeDetailQuestion: {},
activeQuestionIndex: -1,
});
// Only handle the back navigation if we're in question detail view
if (this.state.showQuestion) {
this.setState({
showQuestion: false,
activeDetailQuestion: {},
activeQuestionIndex: -1
});
}
}
onGetExamAttempt() {
@ -317,7 +323,7 @@ class AttemptExam extends React.Component {
this.state.sections = tempSections;
let index1 = 0;
activeQuestion = (
<Layout>
<div style={{ padding: '24px 24px 0' }}>
<QuestionDetail
totalQuestion={this.state.allQuestions.length}
question={this.state.activeDetailQuestion}
@ -325,56 +331,102 @@ class AttemptExam extends React.Component {
parentCallbackNext={this.callbackFunctionNext}
parentCallback={this.callbackFunction}
/>
</Layout>
</div>
);
listItems = this.state.sections.map((item, index) => (
<TabPane tab={item.subject_name} key={index}>
{this.state.showQuestion ? (
activeQuestion
) : (
<Row gutter={40} className="AttemptQuestionGrid" style={{ padding: "20px" }}>
{item.questions.map((question, index) => {
let title = (
<>
<Avatar className={question.classN}>{question.index}</Avatar>
<div className="Outerline">
<div
className={
"HeaderQuestionStatusLabel " +
question.classN +
"Title"
}
<List
style={{ margin: '0 8px' }}
grid={{
gutter: [24, 24], // increased spacing between cards
xs: 1, // <576px screens show 1 card
sm: 2, // ≥576px screens show 2 cards
md: 3, // ≥768px screens show 3 cards
lg: 3, // ≥992px screens show 3 cards
xl: 3, // ≥1200px screens show 3 cards
xxl: 3, // ≥1600px screens show 3 cards
}}
dataSource={item.questions}
pagination={false}
renderItem={question => (
<List.Item style={{ margin: '0 8px' }}>
<Card
hoverable
className={`question-card ${question.classN}-card`}
onClick={() => this.showDetailQuestion(question)}
style={{
height: '150px',
position: 'relative',
margin: '4px' // small margin for the card itself
}}
>
<div className="question-card-header" style={{
marginBottom: '12px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Avatar
className={question.classN}
style={{
width: '28px',
height: '28px',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
{question.index}
</Avatar>
<Tag color="blue" style={{ margin: 0, fontSize: '12px' }}>
{question.type_text}
</Tag>
</div>
<div className="question-content">
<Typography.Paragraph
ellipsis={{ rows: 1 }}
style={{ margin: 0, fontSize: '14px' }}
>
{parse(question.question_text)}
</Typography.Paragraph>
</div>
<div style={{
position: 'absolute',
bottom: '12px',
right: '12px',
left: '12px',
paddingTop: '8px',
borderTop: '1px solid #f0f0f0',
textAlign: 'right'
}}>
<Tag
style={{
margin: 0,
border: 'none',
padding: '2px 8px',
fontSize: '12px',
borderRadius: '4px',
backgroundColor: question.classN === 'QuesttionStatusNotVisited' ? '#bfbfbf' :
question.classN === 'QuesttionStatusNotAnswered' ? '#ff4d4f' :
question.classN === 'QuesttionStatusAnswered' ? '#52c41a' :
question.classN === 'QuesttionStatusReview' ? '#faad14' :
question.classN === 'QuesttionStatusAnsweredReview' ? '#1890ff' : '#bfbfbf',
color: 'white'
}}
>
{question.statusLabel}
</div>
</Tag>
</div>
</>
);
return (
<Col key={question.id} className="question-col">
<Card
hoverable
className={`question-card ${question.classN}-card`}
onClick={() => this.showDetailQuestion(question)}
>
<div className="question-card-header">
<span className="question-title">{title}</span>
<Tag color="blue" className="question-type">
{question.type_text}
</Tag>
</div>
<div className="question-content">
<p>{parse(question.question_text)}</p>
</div>
</Card>
</Col>
);
})}
</Row>
</Card>
</List.Item>
)}
/>
)}
</TabPane>
));
@ -435,9 +487,9 @@ class AttemptExam extends React.Component {
return (
<>
<Layout>
<Layout>
<Header className="exam-header">
<Layout style={{ background: '#fff' }}>
<Layout style={{ background: '#fff' }}>
<Header className="exam-header" style={{ background: '#fff', borderBottom: '1px solid #f0f0f0' }}>
<div className="exam-header-left">
<span className="exam-title">{this.state.exam_name}</span>
</div>
@ -460,12 +512,51 @@ class AttemptExam extends React.Component {
</Header>
<Content
className="site-layout-background"
style={{ padding: 24, margin: 0 }}
>
<Layout className="MainExamLayout">
<Layout className="create-exam-card">
<Layout style={{ padding: '24px 0', background: '#fff' }}>
<Layout.Sider
width={300}
style={{
background: '#fff',
padding: '0 24px',
borderRight: '1px solid #f0f0f0',
height: 'calc(100vh - 64px)', // Subtracting header height
position: 'fixed',
left: 0,
overflowY: 'auto'
}}
className="exam-status-card"
>
<div className="exam-status-section" style={{ margin: '16px 0' }}>
<div className="exam-status-row" style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<div className="status-item" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Avatar className="status-avatar not-visited">{total_notVisited}</Avatar>
<span className="status-label" style={{ fontSize: '14px' }}>Not Visited</span>
</div>
<div className="status-item" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Avatar className="status-avatar not-answered">{total_notAnswered}</Avatar>
<span className="status-label" style={{ fontSize: '14px' }}>Not Answered</span>
</div>
<div className="status-item" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Avatar className="status-avatar answered">{total_Answered}</Avatar>
<span className="status-label" style={{ fontSize: '14px' }}>Answered</span>
</div>
<div className="status-item" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Avatar className="status-avatar to-review">{total_Reviwed}</Avatar>
<span className="status-label" style={{ fontSize: '14px' }}>To Review</span>
</div>
<div className="status-item" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Avatar className="status-avatar answered-review">{total_AnsweredReviwed}</Avatar>
<span className="status-label" style={{ fontSize: '14px' }}>Answered & To Review</span>
</div>
</div>
</div>
<div className="exam-accordion">
<Collapse accordion>{accordions}</Collapse>
</div>
</Layout.Sider>
<Layout.Content style={{ marginLeft: '300px', padding: '0 24px', minHeight: 280, background: '#fff' }}>
<Layout className="create-exam-card" style={{ background: '#fff' }}>
<Tabs
defaultActiveKey="1"
size="large"
@ -477,38 +568,8 @@ class AttemptExam extends React.Component {
{listItems}
</Tabs>
</Layout>
<Layout className="exam-status-card">
<div className="exam-status-section">
<div className="exam-status-row">
<div className="status-item">
<Avatar className="status-avatar not-visited">{total_notVisited}</Avatar>
<span className="status-label">Not Visited</span>
</div>
<div className="status-item">
<Avatar className="status-avatar not-answered">{total_notAnswered}</Avatar>
<span className="status-label">Not Answered</span>
</div>
<div className="status-item">
<Avatar className="status-avatar answered">{total_Answered}</Avatar>
<span className="status-label">Answered</span>
</div>
<div className="status-item">
<Avatar className="status-avatar to-review">{total_Reviwed}</Avatar>
<span className="status-label">To Review</span>
</div>
<div className="status-item">
<Avatar className="status-avatar answered-review">{total_AnsweredReviwed}</Avatar>
<span className="status-label">Answered & To Review</span>
</div>
</div>
</div>
<div className="exam-accordion">
<Collapse accordion>{accordions}</Collapse>
</div>
</Layout>
</Layout>
</Content>
</Layout.Content>
</Layout>
</Layout>
</Layout>
</>

View File

@ -0,0 +1,78 @@
.radio-group .ant-radio-wrapper {
padding: 12px 16px;
border: 1px solid #f0f0f0;
border-radius: 8px;
transition: all 0.3s ease;
margin-right: 0;
width: 100%;
display: flex;
align-items: center;
}
.radio-group .ant-radio-wrapper:hover {
background-color: #fafafa;
border-color: #e6e6e6;
}
.radio-group .ant-radio-wrapper-checked {
background-color: #e6f7ff;
border-color: #91d5ff;
}
.radio-group .ant-radio {
position: relative;
top: 0;
align-self: center;
}
.radio-group .ant-radio-wrapper span.ant-radio + * {
padding-right: 8px;
padding-left: 8px;
display: inline-block;
line-height: 1.5;
}
/* Question number circle animation */
.question-header .question-number {
transition: all 0.3s ease;
}
.question-header .question-number:hover {
transform: scale(1.05);
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.15);
}
/* Switch styling */
.ant-switch {
border-radius: 100px;
}
.ant-switch-checked {
background-color: #52c41a;
}
/* Action buttons hover effects */
.action-buttons .ant-btn {
transition: all 0.3s ease;
}
.action-buttons .ant-btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Question text styling */
.question-text {
color: #262626;
font-size: 16px;
line-height: 1.6;
}
.question-text p {
margin-bottom: 16px;
}
/* Add smooth transitions */
.ant-layout {
transition: all 0.3s ease;
}

View File

@ -1,11 +1,14 @@
import React from "react";
import "./AttemptExam.css";
import "./QuestionDetail.css";
import { authenticationService } from '../../../../_services';
import { selectorService } from "../../../../services/selectorService";
import { Layout, Avatar, Button, Radio, Switch, Tabs, Card, Collapse, Statistic, Col, Row } from 'antd';
import { Link } from 'react-router-dom';
import parse from "html-react-parser";
import { ButtonGroup } from "react-bootstrap";
import ReactMarkdown from 'react-markdown';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
class QuestionDetail extends React.Component {
constructor(props) {
@ -85,55 +88,97 @@ class QuestionDetail extends React.Component {
});
return (
<Layout className="MainExamQuestionLayout" style={{ background: "#fff" }}>
<Row className="question-header">
<Layout style={{ background: "#fff", padding: '24px', borderRadius: '8px' }}>
{/* Question Number and Text */}
<Row className="question-header" style={{
marginBottom: '32px',
borderBottom: '1px solid #f0f0f0',
paddingBottom: '20px'
}}>
<Col>
<h3 className="question-title">
{question.index}. {parse(question.question_text)}
</h3>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '12px' }}>
<div style={{
minWidth: '32px',
height: '32px',
borderRadius: '50%',
background: '#bfbfbf',
color: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '16px',
fontWeight: '500',
marginTop: '4px'
}}>
{question.index}
</div>
<div className="question-text" style={{ flex: 1 }}>
<ReactMarkdown
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]}
components={{
p: ({ node, ...props }) => <span {...props} style={{ fontSize: '16px', lineHeight: '1.6' }} />
}}
>
{question.question_text}
</ReactMarkdown>
</div>
</div>
</Col>
</Row>
<Row className="question-options">
<Col>
{/* Options */}
<Row className="question-options" style={{ marginBottom: '32px' }}>
<Col span={24}>
<Radio.Group
onChange={this.onAnswerChange}
value={answer}
className="radio-group"
style={{
display: 'flex',
flexDirection: 'column',
gap: '16px'
}}
>
{answerOptions}
</Radio.Group>
</Col>
</Row>
<Row className="review-switch">
{/* Review Switch and Actions */}
<Row style={{
borderTop: '1px solid #f0f0f0',
paddingTop: '20px',
marginTop: 'auto',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<Col>
<Switch
checkedChildren="Review On"
unCheckedChildren="Review Off"
checked={question.isReviewMarked}
onChange={this.onReviewQuestion}
style={{ width: '100px' }}
/>
</Col>
</Row>
<Row className="question-actions">
<Col>
<div className="action-buttons">
<div className="action-buttons" style={{ display: 'flex', gap: '12px' }}>
<Button
onClick={this.onClearResponse}
style={{ borderRadius: '6px' }}
>
Clear Response
</Button>
<Button
onClick={this.loadNextQuestion}
type="primary"
disabled={question.index >= totalQuestion}
className="rounded-btn"
style={{ borderRadius: '6px' }}
>
Next Question
</Button>
<Button
onClick={this.onClearResponse}
className="rounded-btn"
>
Clear Response
</Button>
</div>
</Col>
</Row>