UI changes

This commit is contained in:
skillens.ai 2025-10-30 02:40:29 +05:30
parent d305baee71
commit 663d97f6dc
7 changed files with 1146 additions and 944 deletions

View File

@ -1,12 +1,12 @@
robots.txt,1761396900570,bfe106a3fb878dc83461c86818bf74fc1bdc7f28538ba613cd3e775516ce8b49
manifest.json,1761396900568,a9350a49aaac9fe94d3dd77b8270cc998c04ab97944a606189675022431faa51
favicon.svg,1761396900561,a2a4880301751061a600b0bfc5c26fc413aed41e581516c4fa976bcb7fff6663
service-worker.js,1761576002341,023c58598eaf6728e3bbc8ed608e32fd608d37851fa12ce79816a48180c9cd31
asset-manifest.json,1761576002341,d747a6089e42c15217b542311c92d5809e7cae25a755c6242d55d93072ac5281
precache-manifest.816159c176bc481e03384d794646b865.js,1761576002341,8b8a70a80ada7aa8b5e21993a642189e9d1dbed0805111893a8ae9cdd5038c23
static/media/sub-ques-icon.366b4f1e.svg,1761576002260,8e397611007ec5db7581a4c1bcca006950390ddf652cae209bb3973a645af425
static/media/translate.610ad011.svg,1761576002262,c06f8a3d0c976b02429ce805d8a4944571ab0128ed2ce49c2d3ca134511ed120
static/media/student-engaged.53fb7b0b.svg,1761576002257,5f8e01d1a5efcbdf3aa00ed07de843ab84404a28b672ca91f4d70d89fa8b37cf
service-worker.js,1761764783124,a4416108bc325f844ff2591db3c12fd9443bb2a3b4dedd09cee90e333e41bb51
precache-manifest.f827a120dee6582417cfe4b1979f19d5.js,1761764783123,a4ff8d6cbb691d416e802893cc984f2135008b6167e99ee019b88d026a476a1f
static/media/tru-fals-icon.d0b962d8.svg,1761764783104,a1deab7e5db8e8a94a9c5812dcde5f8cbfbe5da790da8500bf106980b9c79c33
asset-manifest.json,1761764783148,995b77288d91dfaddb18b8753357c286829de77928b93c12f5111f35a76670b8
index.html,1761764783123,19c7a6e51b117070c55ac4aae347ca3434878b774a65a4a185b0914d2965a1c0
static/media/translate.610ad011.svg,1761764783106,c06f8a3d0c976b02429ce805d8a4944571ab0128ed2ce49c2d3ca134511ed120
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
index.html,1761576002341,7983b57b52860a70c9a6382d9b2bae797ed4b2b744dbd7af42a74464f9f5e2b2
static/media/quizexam.5545802e.svg,1761576002242,dfc1278bfcd264264a4d0e0e0247c229335abf0573439c5e9829c8607aacd569
static/media/question.0c505ed9.svg,1761576002242,29938066f93476c487414bb7a5dd5227d891c8ab8e115a74a7e7ffccd8d87b36
static/media/questions-icon.3d3c1aaf.svg,1761576002254,197f459a359b00c1c44b6ddd71cbc9160f593d1fe08534e3711e9d72a2956ef1
static/media/practice.f05e6f00.svg,1761576002244,13b094bcbbb8c50906b1ee0ce68305059e99319464dd4147b4c2194dbb460475
static/media/PracticeKiaLogo.a8336af5.svg,1761576002241,1a592518bcfabb2f86669d06ae2fb7b2948bbdbfaf5a80d138ab30f4e482a0f8
static/media/practice-icon.bf603115.svg,1761576002256,27b6a729d91914d899f4b3c1f817e26ac58d2ec0be54dc9b93c01f28adcdd00c
static/media/perf-icon.1597a235.svg,1761576002258,7183482b785de425506f49deb27bba02e906132ac7ce5af01f7da8452ef64f28
static/media/performance.88855f12.svg,1761576002247,03fc1b19005084049382091745d3f1f29a447763091ad4a9bc5d097c338671c9
static/media/tru-fals-icon.d0b962d8.svg,1761576002260,a1deab7e5db8e8a94a9c5812dcde5f8cbfbe5da790da8500bf106980b9c79c33
static/media/mul-res-icon.8ef3b097.svg,1761576002257,1f0a79350cb5546361ef02af1657ad36efeff833fd7bb61db88a20e127a290b4
static/media/mul-cho-icon.b3dc9ea9.svg,1761576002260,e8edbceb0eb49f7629cf0b8edf599f9347acbf0c6d1df2fa6145cbed03f37e1f
static/media/locale-icon.b3596424.svg,1761576002254,68618c76952aa4c5f2623bb010514871688960bb3b9edbcda0eab7eb75442054
static/media/OdiSVGlogo.f0834bb1.svg,1761576002242,3ec1cab31a32db378894d796afedaee7c35f8ff99dfb1039b0bb757225a47235
static/media/getFetch.2b2b7da4.cjs,1761576002242,b2d82abee5b8af22b81d67fc20b3feef1eaaf04585dbc24ec755e3304c469096
static/media/exam-icon.10f48851.svg,1761576002256,b766e9975582af716870a844f8deabd4d80e33a47c400d36cee6bc4840062693
static/media/GrayscalePKLogo.6bb74404.svg,1761576002242,576b38300e49eda6407adce420001e7ad0333cca317eb668aff457c3258dcca2
static/media/delete-icon.da38c0f4.svg,1761576002263,8078772ee88a0588989eec477da1ca949430f3f06efaa9c2db860b74c65f6722
static/media/dashboard.8ec7624b.svg,1761576002242,5a5b5d4bf416d414842bd5eac30432b592342a392450e52943f067db4077bec1
static/media/classes.3b73dba0.svg,1761576002244,2abfbb7015d5bccb8640fd0614d3782c048672c9543fe17d127db63287ac141d
static/media/Checkmark.1356376c.svg,1761576002270,aa56f27c8198bcae3236a881a7134cd3b7d3dbb048ec75654e8ce2d4710ce027
static/media/feature-2.36f8d7e2.webp,1761576002242,fc1c038517abf3b731ff3a4675cbaf1a6aa1150ca970762dd0ffc4199b92b75c
static/media/class-icon.6afd34b5.svg,1761576002254,a2f492c6c9c7b5201773062dfe90c238c12bc00bec097dc06141201f02b9588e
static/media/batch.3fcff66e.svg,1761576002244,f29038480286f091e3805a43f90ca5a70a13bbcef3076831e0b29b1fcb61d862
static/media/auth-BG.2835584f.svg,1761576002241,238d3a2ff1aead1c4aaed0e07d23d2c1164f8be0ef2d5fced5c5ade370f3c5bf
static/media/add-circle.0011f2bc.svg,1761576002255,d9e5d90e8de1ce16df5720b43ca79543036fcf1a6cd6439a9b15c5a97d269f17
static/media/batch-icon.bf664771.svg,1761576002260,7519e2a92f436a2f46ef7420d721e24df2837d7fce7fe8c30f4bc12980b3d1a0
static/js/runtime-main.96ceaa04.js,1761576002273,455ae6008568041ddd40d50a96c614534f9c268f4d0fff4c2700d0d9646ddc57
static/js/4.7247a6ee.chunk.js.map,1761576002344,e708b3fc1a1b324a3b6c468da749aaa4361b2f44d7c9b2e1614165faa04e072b
static/js/runtime-main.96ceaa04.js.map,1761576002341,454ee9efc7767f76d20d24cb4395b51fe7735f63f7ceb89ebbe56f138716b92b
static/js/4.7247a6ee.chunk.js,1761576002280,32fa7d3f8777fffae2bc6dc1de3608e836c27d9044fe4be8fab745f6c08cb373
static/js/3.5470394c.chunk.js.map,1761576002343,0aed88423ebfa4792f1a2daf5809524a872b20f67a0ecb6e1becc7a52fa1c423
static/js/3.5470394c.chunk.js,1761576002279,564d1d12db256f1c984a60c05dfc4e34c782c3c8b1b548447c5c30dd71aba0d6
static/js/2.337cf2a6.chunk.js.LICENSE.txt,1761576002280,9c84bc4d2f8584d32d75e01e0317e22af1e39f5ac5ded3e2e4e34984704c172b
static/media/logo.0dd03933.png,1761576002242,b9716ed1f565a052edc1154a207334de81856339e8ca43d5d8f51041f3785085
static/media/feature-1.ea5c34ea.svg,1761576002234,d802659785b69508e521d5543fca40a58cb9076521ffc3e6b006f4786b8079f8
static/media/feature-3.3d5d81e9.svg,1761576002242,ba23393d89776cb78f7a559903baff48be76665abd99bdd4e5ea103c5e5ed88b
static/js/main.e80be951.chunk.js,1761576002272,b15fab5b06dc6f90c0f6677def9a66a9d9609026af14db6e2c114c27c1c22db9
static/css/main.3624148f.chunk.css,1761576002265,6a13470e019853946811aebb68baac2e094470affc5520d835a986b420b1edab
static/css/4.629184c9.chunk.css,1761576002280,dfeba86da1256521df7a877a265d56d314f65016837f30878338c2b7fc62a6ab
static/css/2.561a8df6.chunk.css,1761576002273,c87247fc5cf38902aee0ee29244f6ab0c4bc14e5a0a1d6860cd771410dc826f8
static/css/3.6c5a3051.chunk.css,1761576002280,1ae150a22b1d42322775be9055605f93c75188e7081cfbdb206a59fc2f5ab8f1
static/js/main.e80be951.chunk.js.map,1761576002342,c7567119cb47238c88452f154ae648333c59cd204a9f3734b3b90ae2a422f534
static/css/4.629184c9.chunk.css.map,1761576002292,b82aa57e6e357dbb20067334e544e9b30d20ed0d928c13e6c61e1747e6f5a205
static/css/3.6c5a3051.chunk.css.map,1761576002289,bcc301c3b3b46c96afc0fc028a6aa3cd299ccda538d7c6cb9ebc87f8de535796
static/css/2.561a8df6.chunk.css.map,1761576002281,ed9f46fc5b36bd835967cfd533fb33fd9ceec9c3aadcbe6c12bfb90b3ad1bc83
static/css/main.3624148f.chunk.css.map,1761576002281,588c07ee54c0d7bc5ec65c6e4ab3890e922520cac4eac29b6fd638715a5ac5d1
static/js/2.337cf2a6.chunk.js,1761576002282,e0edd93ede2589762ee74717e09fd5917dcb2660daa4ac6332bd8bc5b3c676ec
static/js/2.337cf2a6.chunk.js.map,1761576002358,b607efb90aec0e30321307c0738064e935f3d4b90d423885c732d57f1ebba603
static/media/student-engaged.53fb7b0b.svg,1761764783104,5f8e01d1a5efcbdf3aa00ed07de843ab84404a28b672ca91f4d70d89fa8b37cf
static/media/quizexam.5545802e.svg,1761764783100,dfc1278bfcd264264a4d0e0e0247c229335abf0573439c5e9829c8607aacd569
static/media/sub-ques-icon.366b4f1e.svg,1761764783106,8e397611007ec5db7581a4c1bcca006950390ddf652cae209bb3973a645af425
static/media/questions-icon.3d3c1aaf.svg,1761764783104,197f459a359b00c1c44b6ddd71cbc9160f593d1fe08534e3711e9d72a2956ef1
static/media/PracticeKiaLogo.a8336af5.svg,1761764783097,1a592518bcfabb2f86669d06ae2fb7b2948bbdbfaf5a80d138ab30f4e482a0f8
static/media/practice.f05e6f00.svg,1761764783099,13b094bcbbb8c50906b1ee0ce68305059e99319464dd4147b4c2194dbb460475
static/media/performance.88855f12.svg,1761764783101,03fc1b19005084049382091745d3f1f29a447763091ad4a9bc5d097c338671c9
static/media/perf-icon.1597a235.svg,1761764783104,7183482b785de425506f49deb27bba02e906132ac7ce5af01f7da8452ef64f28
static/media/mul-res-icon.8ef3b097.svg,1761764783104,1f0a79350cb5546361ef02af1657ad36efeff833fd7bb61db88a20e127a290b4
static/media/OdiSVGlogo.f0834bb1.svg,1761764783097,3ec1cab31a32db378894d796afedaee7c35f8ff99dfb1039b0bb757225a47235
static/media/practice-icon.bf603115.svg,1761764783104,27b6a729d91914d899f4b3c1f817e26ac58d2ec0be54dc9b93c01f28adcdd00c
static/media/question.0c505ed9.svg,1761764783097,29938066f93476c487414bb7a5dd5227d891c8ab8e115a74a7e7ffccd8d87b36
static/media/mul-cho-icon.b3dc9ea9.svg,1761764783104,e8edbceb0eb49f7629cf0b8edf599f9347acbf0c6d1df2fa6145cbed03f37e1f
static/media/locale-icon.b3596424.svg,1761764783104,68618c76952aa4c5f2623bb010514871688960bb3b9edbcda0eab7eb75442054
static/media/GrayscalePKLogo.6bb74404.svg,1761764783097,576b38300e49eda6407adce420001e7ad0333cca317eb668aff457c3258dcca2
static/media/getFetch.2b2b7da4.cjs,1761764783097,b2d82abee5b8af22b81d67fc20b3feef1eaaf04585dbc24ec755e3304c469096
static/media/exam-icon.10f48851.svg,1761764783104,b766e9975582af716870a844f8deabd4d80e33a47c400d36cee6bc4840062693
static/media/feature-2.36f8d7e2.webp,1761764783097,fc1c038517abf3b731ff3a4675cbaf1a6aa1150ca970762dd0ffc4199b92b75c
static/media/delete-icon.da38c0f4.svg,1761764783107,8078772ee88a0588989eec477da1ca949430f3f06efaa9c2db860b74c65f6722
static/media/dashboard.8ec7624b.svg,1761764783097,5a5b5d4bf416d414842bd5eac30432b592342a392450e52943f067db4077bec1
static/media/classes.3b73dba0.svg,1761764783100,2abfbb7015d5bccb8640fd0614d3782c048672c9543fe17d127db63287ac141d
static/media/class-icon.6afd34b5.svg,1761764783103,a2f492c6c9c7b5201773062dfe90c238c12bc00bec097dc06141201f02b9588e
static/media/Checkmark.1356376c.svg,1761764783113,aa56f27c8198bcae3236a881a7134cd3b7d3dbb048ec75654e8ce2d4710ce027
static/media/batch.3fcff66e.svg,1761764783097,f29038480286f091e3805a43f90ca5a70a13bbcef3076831e0b29b1fcb61d862
static/media/auth-BG.2835584f.svg,1761764783094,238d3a2ff1aead1c4aaed0e07d23d2c1164f8be0ef2d5fced5c5ade370f3c5bf
static/media/batch-icon.bf664771.svg,1761764783104,7519e2a92f436a2f46ef7420d721e24df2837d7fce7fe8c30f4bc12980b3d1a0
static/media/add-circle.0011f2bc.svg,1761764783104,d9e5d90e8de1ce16df5720b43ca79543036fcf1a6cd6439a9b15c5a97d269f17
static/js/runtime-main.2bdeab81.js,1761764783115,5c04066aee33f757af11aff4783499664f995e641476c3c1552c373bf87171bb
static/js/4.568469c1.chunk.js.map,1761764783148,f626752d2d2cffe76387c84c314248f1ab02d9c6a79a16442a98e124c8f7d5fa
static/js/runtime-main.2bdeab81.js.map,1761764783148,a6cd518ca4773cf5562057dad7acb281edac1b802572abcdc6ee19c33f02b744
static/js/4.568469c1.chunk.js,1761764783115,7f998d124bddb5cb8e916a7bb559ddd2f5c133920e79651382bbabf25affc9c4
static/js/3.d75ced33.chunk.js.map,1761764783148,a12fbfb03ced36f6a8c055bf8b3961a9170b3676c8e4f545921152b23e9bc4dd
static/js/3.d75ced33.chunk.js,1761764783115,c68f66b973c4099ec168a05edb6212303bf31b91c57a2adc0a46595d31262355
static/js/2.ac9f0d75.chunk.js.LICENSE.txt,1761764783115,9c84bc4d2f8584d32d75e01e0317e22af1e39f5ac5ded3e2e4e34984704c172b
static/media/logo.0dd03933.png,1761764783097,b9716ed1f565a052edc1154a207334de81856339e8ca43d5d8f51041f3785085
static/media/feature-1.ea5c34ea.svg,1761764783097,d802659785b69508e521d5543fca40a58cb9076521ffc3e6b006f4786b8079f8
static/media/feature-3.3d5d81e9.svg,1761764783097,ba23393d89776cb78f7a559903baff48be76665abd99bdd4e5ea103c5e5ed88b
static/js/main.962d5966.chunk.js,1761764783108,79a04f8bcc7d0c6dc396754ba1e4010597781782bc2743b2074a2c5a4107412b
static/css/main.b90ebd71.chunk.css,1761764783107,47c0c4a15920e9cea3ceba32d4bc59829a376a7f4347c15b82d7e8878d59008d
static/css/2.561a8df6.chunk.css,1761764783113,c87247fc5cf38902aee0ee29244f6ab0c4bc14e5a0a1d6860cd771410dc826f8
static/css/4.629184c9.chunk.css,1761764783115,dfeba86da1256521df7a877a265d56d314f65016837f30878338c2b7fc62a6ab
static/css/3.6c5a3051.chunk.css,1761764783113,1ae150a22b1d42322775be9055605f93c75188e7081cfbdb206a59fc2f5ab8f1
static/js/main.962d5966.chunk.js.map,1761764783148,c15e557e07ac2c5dd288726a7e260295a75111bcdd614be16ec21bb71dcb9e8c
static/css/4.629184c9.chunk.css.map,1761764783148,b82aa57e6e357dbb20067334e544e9b30d20ed0d928c13e6c61e1747e6f5a205
static/css/3.6c5a3051.chunk.css.map,1761764783124,bcc301c3b3b46c96afc0fc028a6aa3cd299ccda538d7c6cb9ebc87f8de535796
static/css/main.b90ebd71.chunk.css.map,1761764783116,bff960067cfb59c88b8ec495e5b5334532e05ae898ad3f3a78ee293f74fb7855
static/css/2.561a8df6.chunk.css.map,1761764783122,ed9f46fc5b36bd835967cfd533fb33fd9ceec9c3aadcbe6c12bfb90b3ad1bc83
static/js/2.ac9f0d75.chunk.js,1761764783116,a2f10af26647312232ac4cb0601a77496d561a7494b9db1206e2f8632813d0b6
static/js/2.ac9f0d75.chunk.js.map,1761764783158,093171b5c14d2415bee5b4dbbf9caa643ee948acbb18398fbc5293ec5e9009aa

View File

@ -32,7 +32,7 @@ const Headerc = (props) => {
language_code: "",
language_name: "",
classID: "",
batchID: "",
batch_id: "",
});
const [languages, setlanguages] = useState([]);
@ -114,13 +114,13 @@ const Headerc = (props) => {
// Persist last selected batch if available
const storedUser = JSON.parse(sessionStorage.getItem("currentUser")) || {};
if (storedUser.batchID) {
setState(prev => ({ ...prev, batchID: storedUser.batchID }));
if (storedUser.batch_id) {
setState(prev => ({ ...prev, batch_id: storedUser.batch_id }));
} else if (batchData.length > 0) {
// Default to first batch
storedUser.batchID = batchData[0].id;
authenticationService.currentUserValue.batchID = batchData[0].id;
setState(prev => ({ ...prev, batchID: batchData[0].id }));
storedUser.batch_id = batchData[0].id;
authenticationService.currentUserValue.batch_id = batchData[0].id;
setState(prev => ({ ...prev, batch_id: batchData[0].id }));
sessionStorage.setItem("currentUser", JSON.stringify(storedUser));
}
@ -166,11 +166,11 @@ const Headerc = (props) => {
/** ✅ Handle Batch Change **/
const onClickBatchChange = (_, objectSelected) => {
authenticationService.currentUserValue.batchID = objectSelected.value;
authenticationService.currentUserValue.batch_id = objectSelected.value;
const storedUser = JSON.parse(sessionStorage.getItem("currentUser")) || {};
storedUser.batchID = objectSelected.value;
storedUser.batch_id = objectSelected.value;
sessionStorage.setItem("currentUser", JSON.stringify(storedUser));
setState(prev => ({ ...prev, batchID: objectSelected.value }));
setState(prev => ({ ...prev, batch_id: objectSelected.value }));
};
function onClickLanguageChange(_, objectSelected) {
@ -246,7 +246,7 @@ const Headerc = (props) => {
)}
dropdownClassName="batch-select-dropdown"
onChange={onClickBatchChange}
value={batches.length > 0 ? state.batchID : undefined}
value={batches.length > 0 ? state.batch_id : undefined}
>
{batches.map((batchItem) => (
<Select.Option

View File

@ -1,242 +1,307 @@
li.MyExam{
-moz-box-shadow: 0 0 5px #888;
-webkit-box-shadow: 0 0 5px#888;
box-shadow: 0 0 5px #E1E1E1;
margin-bottom: 10px;
padding:5px 35px 5px 25px;
border-radius: 7px;
}
li.MyExam table{
width:80%;
}
span.MyExamImage{
width: 100px;
height: 100px;
line-height: 40px;
}
span.MyExamImage img{
width: 100px;
height: 100px;
}
div.MyExamDiv table tr> th{
border:none;
padding: 0 0 0 10px;
text-align: left;
font-weight: normal;
}
div.MyExamDiv table tr> td{
border:none;
padding: 0 0 0 10px;
text-align: left;
font-weight: bolder;
}
li.MyExam h4.ant-list-item-meta-title{
padding: 0 0 0 10px;
}
li.MyExam span.MyExamSpan{
padding: 0 0 0 10px;
}
.ExamNameHeader {
font-size: 18pt;
float: left;
color: white;
}
.ExamTimeHeader{
font-size: 16pt;
float: right;
color: white;
}
.ExamTimeHeader div{
font-size: 13pt;
float:none;
color: white;
}
.MainExamLayout{
flex-direction: row;
}
.createExamLayoutLeft{
display: flex;
flex: auto;
flex-direction: column;
background: #ffffff !important;
border-style: solid;
border-width: 1px;
border-color: rgb(231, 230, 230);
padding: 10px;
width:70%;
}
.createExamLayoutRight{
display: flex;
flex: auto;
flex-direction: column;
background: #ffffff !important;
border-style: solid;
border-width: 1px;
border-color: rgb(231, 230, 230);
padding: 10px;
position:sticky;
position: -webkit-sticky;
top:0
}
.StatusLabel{
padding: 5px;
}
.ExamAvatar{
padding: 25px 0px 25px 0px;
}
.ExamStatusTable td{
padding: 5px;
}
div.AttemptQuestionGrid{
grid-template-columns: repeat(auto-fit, minmax(221px, 1fr));
display: grid;
margin-left: 0px!important;
margin-right: 0px!important;
}
div.AttemptQuestionGrid > div.ant-col{
padding-right: 0px !important;
padding-left: 0px !important;
}
.QuestionCard{
height: 336px;
margin: 12px;
max-width: 282px;
float: left;
border-radius: 16px;
cursor: pointer;
color:#150F2D;
font-size: 13px;
text-overflow: ellipsis;
width:90%;
box-shadow: 0 0 5px #e1e1e1;
}
.QuestionCard div.ant-card-head{
border-bottom:none;
padding: 0 10px;
}
.QuestionCard div.ant-card-head div.ant-card-head-wrapper .ant-card-head-title
{
.exam-header {
display: flex;
align-items: center;
justify-content: space-between;
background: #ffffff;
border-bottom: 1px solid #f0f0f0;
padding: 0 24px;
height: 64px;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
}
.QuestionCard div.ant-card-body{
height: 67%;
padding:16px;
.exam-header-left .exam-title {
font-size: 18px;
font-weight: 600;
color: #1f1f1f;
}
.QuestionCard ul.ant-card-actions{
background: none;
border-top:none;
width: max-content;
background-color: #EAECF1;
height: 20px;
padding: 1px 4px;
box-sizing: border-box;
.exam-header-center {
flex: 1;
display: flex;
justify-content: center;
}
.exam-countdown .ant-statistic-title {
color: #555;
font-size: 14px;
}
.exam-countdown .ant-statistic-content {
font-size: 16px;
font-weight: 600;
color: #1677ff;
}
.exam-header-right {
display: flex;
gap: 10px;
}
.exam-header-right .ant-btn {
border-radius: 6px;
}
/* Card container */
.create-exam-card {
background: #ffffff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
padding: 24px;
margin: 24px;
transition: box-shadow 0.3s ease;
}
.create-exam-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}
/* Tabs styling */
.create-exam-tabs {
font-size: 16px;
}
.create-exam-tabs .ant-tabs-nav {
margin-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.create-exam-tabs .ant-tabs-tab {
font-weight: 500;
color: #555;
transition: color 0.2s ease;
padding: 10px 16px;
border-radius: 0;
background: transparent !important;
}
/* ✨ Hover effect only text color changes */
.create-exam-tabs .ant-tabs-tab:hover {
color: #1677ff;
background: transparent !important;
}
/* Active tab only bold text + blue ink bar */
.create-exam-tabs .ant-tabs-tab-active {
color: #1677ff !important;
font-weight: 600;
background: transparent !important;
}
/* Ink bar thin blue underline */
.create-exam-tabs .ant-tabs-ink-bar {
background: #1677ff;
height: 3px;
border-radius: 3px;
}
.exam-status-card {
background: #ffffff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
padding: 24px;
margin: 24px;
transition: box-shadow 0.3s ease;
}
.exam-status-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}
.exam-status-section {
margin-bottom: 24px;
}
.exam-status-table {
width: 100%;
border-collapse: separate;
border-spacing: 16px 12px;
}
.status-item {
display: flex;
align-items: center;
justify-content: space-around;
border-radius: 4px;
float: right;
margin-right: 5px;
}
.QuestionCard ul.ant-card-actions > li > span{
font-size: 12px;
}
.QuestionCard ul.ant-card-actions > li:not(:last-child) {
border-right: none;
}
.Options{
margin-left:13px;
gap: 10px;
}
.QuesttionStatusNotVisited{
background-color: #F0F0F0
}
.QuesttionStatusNotAnswered{
background-color: #FED911
}
.QuesttionStatusAnswered{
background-color: #54C51D
}
.QuesttionStatusReview{
background-color: #A028FF
}
.QuesttionStatusAnsweredReview{
background-color: #FFA500
}
.QuesttionStatusNotVisitedCard{
border-color: #F0F0F0
}
.QuesttionStatusNotAnsweredCard{
border-color: #FED911
}
.QuesttionStatusAnsweredCard{
border-color: #54C51D
}
.QuesttionStatusReviewCard{
border-color: #A028FF
}
.QuesttionStatusAnsweredReviewCard{
border-color: #FFA500
}
div.QuestionStatusHeaderIcon{
height: 8px;
width: 8px;
border-radius: 274.76px;
box-shadow: 0 0 15px 0 rgba(0,0,0,0.1);
}
div.HeaderQuestionStatusLabel{
-webkit-font-smoothing: antialiased;
font-size: 12px;
line-height: 1.125rem;
.status-label {
font-size: 15px;
font-weight: 500;
letter-spacing: .25px;
text-decoration: none;
text-transform: none;
color: #444;
}
.QuesttionStatusNotVisitedTitle{
color: #F0F0F0
/* Avatar badges */
.status-avatar {
font-weight: 600;
color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
}
.QuesttionStatusNotAnsweredTitle{
color: #FED911
}
.QuesttionStatusAnsweredTitle{
color: #54C51D
}
.QuesttionStatusReviewTitle{
color: #A028FF
}
.QuesttionStatusAnsweredReviewTitle{
color: #FFA500
/* Individual colors for clarity */
.status-avatar.not-visited {
background-color: #d9d9d9;
}
.Outline{ display: flex;
justify-content: flex-end;
align-items: center;
width: 77%;
font-size: 14px;
.status-avatar.not-answered {
background-color: #ff7875;
}
.MainExamQuestionLayout{
padding: 20px;
.status-avatar.answered {
background-color: #52c41a;
}
.status-avatar.to-review {
background-color: #faad14;
}
.status-avatar.answered-review {
background-color: #1890ff;
}
/* Collapse styling */
.exam-accordion .ant-collapse {
border: none;
background: #fafafa;
border-radius: 8px;
}
.exam-accordion .ant-collapse-item {
border-bottom: 1px solid #f0f0f0;
}
.exam-accordion .ant-collapse-header {
font-weight: 500;
color: #333;
transition: color 0.2s ease;
}
.exam-accordion .ant-collapse-header:hover {
color: #1677ff;
}
.AttemptQuestionGrid {
display: flex;
flex-wrap: wrap;
gap: 20px; /* space between cards */
justify-content: flex-start;
}
.question-col {
flex: 1 1 250px; /* make responsive */
min-width: 250px;
max-width: 300px;
}
.question-card {
margin: 10px;
border-radius: 12px !important;
overflow: hidden;
transition: all 0.2s ease-in-out;
}
/* Add a subtle hover effect */
.question-card:hover {
transform: translateY(-4px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
/* Header (title + type tag) */
.question-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
}
/* Title (avatar + status label) */
.question-title {
display: flex;
align-items: center;
gap: 10px;
}
/* Question content */
.question-content {
margin-top: 10px;
line-height: 1.6;
font-size: 15px;
}
/* Tag styling */
.question-type {
font-size: 12px;
border-radius: 8px;
padding: 2px 8px;
}
.MainExamQuestionLayout {
background: #ffffff;
border-radius: 16px;
padding: 28px 36px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
margin: 30px 20px; /* ⬅️ More margin around the card */
transition: box-shadow 0.3s ease, transform 0.2s ease;
}
.MainExamQuestionLayout:hover {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
transform: translateY(-2px);
}
.question-header {
margin-bottom: 20px;
}
.question-title {
font-size: 1.3rem;
font-weight: 600;
color: #1f1f1f;
line-height: 1.6;
}
.question-options {
margin-top: 16px;
margin-bottom: 24px;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 12px;
}
.radioStyle {
font-size: 16px;
line-height: 1.5;
}
.review-switch {
margin-bottom: 28px;
}
.question-actions {
display: flex;
justify-content: flex-end;
}
.action-buttons {
display: flex;
gap: 12px; /* ⬅️ spacing between buttons */
}
.rounded-btn {
border-radius: 10px !important; /* ⬅️ rounded corners */
font-size: 15px;
height: 40px;
padding: 0 20px;
}
.ant-btn-primary {
background-color: #1677ff;
border-color: #1677ff;
}
.ant-btn-primary:hover {
background-color: #3b89ff;
border-color: #3b89ff;
}

View File

@ -1,9 +1,21 @@
import React from "react";
import "./AttemptExam.css";
import { authenticationService } from '../../../../_services';
import { authenticationService } from "../../../../_services";
import { selectorService } from "../../../../services/selectorService";
import { Layout, Avatar, Button, List, Tabs, Card, Collapse, Statistic, Col, Row } from 'antd';
import { Link } from 'react-router-dom';
import {
Layout,
Avatar,
Button,
List,
Tabs,
Card,
Collapse,
Statistic,
Col,
Row,
Tag
} from "antd";
import { Link } from "react-router-dom";
import QuestionDetail from "./QuestionDetail";
import parse from "html-react-parser";
@ -12,24 +24,25 @@ const { Header } = Layout;
const { TabPane } = Tabs;
const { Panel } = Collapse;
const { Countdown } = Statistic;
class AttemptExam extends React.Component {
constructor(props) {
super(props);
this.state = {
currentUser: authenticationService.currentUserValue,
exam_id: 'props.location.state.examId',
exam_id: "props.location.state.examId",
showQuestion: false,
activeDetailQuestion:{},
activeQuestionIndex:-1,
pauseUpdate: false
activeDetailQuestion: {},
activeQuestionIndex: -1,
pauseUpdate: false,
};
this.onGetExamAttempt = this.onGetExamAttempt.bind(this);
this.callbackFunction = this.callbackFunction.bind(this);
this.callbackFunctionNext = this.callbackFunctionNext.bind(this);
this.sendExamUpdate =this.sendExamUpdate.bind(this);
this.sendHeartbeat = this.sendHeartbeat.bind(this);
this.stopExamUpdate = this.stopExamUpdate.bind(this);
this.updateExamStatus = this.updateExamStatus.bind(this);
this.onSubmitExam = this.onSubmitExam.bind(this);
this.onPauseExam = this.onPauseExam.bind(this);
this.getExamModelToUpdate = this.getExamModelToUpdate.bind(this);
@ -37,15 +50,13 @@ class AttemptExam extends React.Component {
componentDidMount() {
this.onGetExamAttempt();
this.sendExamUpdate();
this.sendHeartbeat();
}
getExamModelToUpdate(){
getExamModelToUpdate() {
let tempAllQuestions = this.state.allQuestions;
let questionsArray = [];
if(tempAllQuestions === undefined)
return;
if (tempAllQuestions === undefined) return;
tempAllQuestions.map((question, index) => {
let quest = {};
quest.question_id = question.id;
@ -53,138 +64,130 @@ class AttemptExam extends React.Component {
quest.is_reviewed = question.isReviewMarked;
quest.is_visited = question.isVisited;
let options = question.options;
let answers =[];
let answers = [];
for (let i = 0; i < options.length; i++) {
if(options[i].isSelected){
let ans = {}
if (options[i].isSelected) {
let ans = {};
ans.id = options[i].id;
answers.push(ans);
}
}
quest.answers = answers;
if(quest.is_visited)
questionsArray.push(quest);
})
if (quest.is_visited) questionsArray.push(quest);
});
return questionsArray;
}
onPauseExam(){
let questionsArray = this.getExamModelToUpdate();
onPauseExam() {
this.stopExamUpdate();
selectorService.pauseExam(this.state.attempt_id).then((data) => {
selectorService.pauseExam(this.state.attempt_id)
.then((_data) => {
console.log("Exam Paused");
// ✅ Go back to the previous page after pausing the exam
window.history.back();
})
.catch((error) => {
console.error("Error pausing exam:", error);
});
}
onSubmitExam(){
onSubmitExam() {
let questionsArray = this.getExamModelToUpdate();
this.stopExamUpdate();
selectorService.endExam(this.state.attempt_id,questionsArray).then((data) => {
console.log(data);
selectorService.endExam(this.state.attempt_id, questionsArray)
.then((_data) => {
console.log("Exam Ended");
// ✅ Go back to the previous page after exam ends
window.history.back();
})
.catch((error) => {
console.error("Error ending exam:", error);
});
}
stopExamUpdate(){
stopExamUpdate() {
clearInterval(this.interval);
}
componentWillUnmount(){
componentWillUnmount() {
this.stopExamUpdate();
}
sendExamUpdate(){
sendHeartbeat() {
this.interval = setInterval(() => {
if(this.state.pauseUpdate){
if (this.state.pauseUpdate) {
this.stopExamUpdate();
}
else{
this.updateExamStatus();
console.log("UpdateAnswer");
} else {
selectorService
.heartbeat(this.state.attempt_id)
.then((data) => {
const result = data.result;
this.setState({
time_left: result,
});
});
}
}, 60000);
}
updateExamStatus(){
let questionsArray = this.getExamModelToUpdate();
console.log(questionsArray);
selectorService.updateExamStatus(this.state.attempt_id,questionsArray).then((data) => {
const result = data.result;
this.setState({
time_left: result,
});
console.log(result);
});
}
showDetailQuestion(question) {
question.isVisited = true;
this.state.sections[question.sectionIndx].questions[question.secQIndex] = question;
this.state.allQuestions[question.index-1] = question;
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;
this.setState({
showQuestion: true,
activeDetailQuestion: question,
activeQuestionIndex: question.index-1,
activeQuestionIndex: question.index - 1,
sections: tempSections,
allQuestions: tempAllQuestions
})
allQuestions: tempAllQuestions,
});
this.forceUpdate();
}
onQuestionClick(index){
let q =this.state.allQuestions[index-1];
onQuestionClick(index) {
let q = this.state.allQuestions[index - 1];
this.showDetailQuestion(q);
}
onTabPaneClick(){
onTabPaneClick() {
this.setState({
showQuestion: false,
activeDetailQuestion: {},
activeQuestionIndex: -1
})
activeQuestionIndex: -1,
});
}
onGetExamAttempt() {
var attemptId;
selectorService.createExamAttempt(this.props.location.state.examId).then((data) => {
selectorService
.createExamAttempt(this.props.location.state.examId)
.then((data) => {
const result = data.result;
attemptId = result.attempt_id;
let index1 = 0;
selectorService.getExamAttempt(attemptId).then((data) => {
const result = data.result;
let sections1 = [];
let questionsArray = [];
sections1 = result.sections.map((item, secIndex) => {
let quest = [];
quest = item.questions.map((question, index) => {
index1 = index1 + 1
index1 = index1 + 1;
question.index = index1;
question.sectionIndx = secIndex;
question.secQIndex = index;
questionsArray.push(question);
return question;
})
});
item.questions = quest;
return item
return item;
});
this.setState({
attempt_id: attemptId,
@ -193,218 +196,295 @@ class AttemptExam extends React.Component {
exam_name: result.exam_name,
sections: sections1,
time_left: result.time_left,
deadline: Date.now() + result.time_left*1000,
allQuestions:questionsArray
deadline: Date.now() + result.time_left * 1000,
allQuestions: questionsArray,
});
});
});
};
}
callbackFunction = (question) => {
// console.log("Parent recieved Selector Data: "+ childData);
let q = question;
let indx = q.index;
let secQuesIndx = q.secQIndex;
this.state.sections[q.sectionIndx].questions[secQuesIndx] =q;
this.state.allQuestions[indx-1] = q;
this.state.sections[q.sectionIndx].questions[secQuesIndx] = q;
this.state.allQuestions[indx - 1] = q;
let tempSections = this.state.sections;
let tempAllQuestions = this.state.allQuestions;
this.setState({
showQuestion: true,
sections: tempSections,
allQuestions: tempAllQuestions
})
}
allQuestions: tempAllQuestions,
});
};
callbackFunctionNext = (childData) => {
// console.log("Parent recieved Selector Data: "+ childData);
let q = childData;
let indx = q.index;
let nxtQuestion = this.state.allQuestions[(indx)];
// ✅ Extract selected answer IDs (just numbers)
const selectedAnswers = childData.options
.filter(option => option.isSelected)
.map(option => option.id); // <-- plain IDs, not objects
// ✅ Build the JSON object like your Kotlin version
const answerObject = {
question_id: childData.id,
answer_duration: childData.answer_duration || 10,
is_reviewed: childData.is_reviewed || false,
answers: selectedAnswers
};
// ✅ Send it (stringify the full object)
selectorService.updateExamStatus(this.state.attempt_id, JSON.stringify(answerObject));
// ✅ Load next question
const q = childData;
const indx = q.index;
const nxtQuestion = this.state.allQuestions[indx];
this.showDetailQuestion(nxtQuestion);
}
};
callbackFunctionUpdateAnswer = (question) => {
console.log("Answer Updated", question);
}
render() {
let listItems = "";
let accordions = "";
let activeQuestion ="";
let activeQuestion = "";
let total_notVisited = 0;
let total_notAnswered = 0;
let total_Answered = 0;
let total_AnsweredReviwed = 0;
let total_Reviwed = 0;
let questionStatusLabel ={"QuesttionStatusNotAnswered":"Not Answered","QuesttionStatusAnswered":"Answered","QuesttionStatusReview":"To Review","QuesttionStatusAnsweredReview":"Answered and To Review"};
let questionStatusLabel = {
QuesttionStatusNotAnswered: "Not Answered",
QuesttionStatusAnswered: "Answered",
QuesttionStatusReview: "To Review",
QuesttionStatusAnsweredReview: "Answered and To Review",
};
if (this.state.sections !== undefined) {
let tempSections = this.state.sections.map((item, index) =>{
let tempSections = this.state.sections.map((item, index) => {
let tempQuestions = item.questions.map((question) => {
let isAnswered = false;
let options = question.options;
let answered =[];
let answered = [];
for (let i = 0; i < options.length; i++) {
if(options[i].isSelected){
if (options[i].isSelected) {
answered.push(options[i]);
}
}
if(answered.length>0)
isAnswered = true;
if (answered.length > 0) isAnswered = true;
let classN = "QuesttionStatusNotVisited";
if(question.isVisited)
classN = "QuesttionStatusNotAnswered";
if(isAnswered && !question.isReviewMarked)
if (question.isVisited) classN = "QuesttionStatusNotAnswered";
if (isAnswered && !question.isReviewMarked)
classN = "QuesttionStatusAnswered";
if(question.isReviewMarked && isAnswered){
if (question.isReviewMarked && isAnswered) {
classN = "QuesttionStatusAnsweredReview";
}
if(question.isReviewMarked && !isAnswered){
if (question.isReviewMarked && !isAnswered) {
classN = "QuesttionStatusReview";
}
question.classN = classN;
question.statusLabel = questionStatusLabel[classN];
if(classN === "QuesttionStatusReview")
total_Reviwed=total_Reviwed+1;
if(classN === "QuesttionStatusAnsweredReview")
total_AnsweredReviwed=total_AnsweredReviwed+1;
if(classN === "QuesttionStatusAnswered")
total_Answered=total_Answered+1;
if(classN === "QuesttionStatusNotAnswered")
total_notAnswered=total_notAnswered+1;
if(classN === "QuesttionStatusNotVisited")
total_notVisited=total_notVisited+1;
if (classN === "QuesttionStatusReview") total_Reviwed += 1;
if (classN === "QuesttionStatusAnsweredReview")
total_AnsweredReviwed += 1;
if (classN === "QuesttionStatusAnswered") total_Answered += 1;
if (classN === "QuesttionStatusNotAnswered") total_notAnswered += 1;
if (classN === "QuesttionStatusNotVisited") total_notVisited += 1;
return question;
})
});
item.questions = tempQuestions;
return item;
})
});
this.state.sections = tempSections;
let index1 = 0;
activeQuestion = <Layout><QuestionDetail totalQuestion={this.state.allQuestions.length} question={this.state.activeDetailQuestion} parentCallbackNext = {this.callbackFunctionNext} parentCallback = {this.callbackFunction}/></Layout>;
listItems = this.state.sections.map((item, index) =>
activeQuestion = (
<Layout>
<QuestionDetail
totalQuestion={this.state.allQuestions.length}
question={this.state.activeDetailQuestion}
updateAnswerCallback={this.callbackFunctionUpdateAnswer}
parentCallbackNext={this.callbackFunctionNext}
parentCallback={this.callbackFunction}
/>
</Layout>
);
<TabPane tab={item.subject_name} key={index} >
{
this.state.showQuestion?activeQuestion:
<Row gutter={40} className="AttemptQuestionGrid">
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"}>{question.statusLabel}</div>
let title = (
<>
<Avatar className={question.classN}>{question.index}</Avatar>
<div className="Outerline">
<div
className={
"HeaderQuestionStatusLabel " +
question.classN +
"Title"
}
>
{question.statusLabel}
</div>
</div>
</>
);
</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>
return <Col key={question.id} >
<Card title={title} actions={[
question.type_text
]} className={"QuestionCard "+question.classN+"Card"} onClick={this.showDetailQuestion.bind(this,question)}>
<div className="question-content">
<p>{parse(question.question_text)}</p>
</div>
</Card>
</Col>
);
})}
</Row>
}
)}
</TabPane>
));
);
accordions = this.state.sections.map((item, index) =>
accordions = this.state.sections.map((item, index) => (
<Panel header={item.subject_name} key={index}>
<Row gutter={20}>
{item.questions.map((question) => {
return <Col key={question.id}>
<Avatar className={question.classN} onClick={this.onQuestionClick.bind(this,question.index)}>{question.index}</Avatar>
</Col>})}
{item.questions.map((question) => (
<Col key={question.id}>
<Avatar
className={question.classN}
onClick={this.onQuestionClick.bind(this, question.index)}
>
{question.index}
</Avatar>
</Col>
))}
</Row>
</Panel>
);
));
}
return (
<>
<Layout>
<Layout>
<Header className="header">
<div style={{ paddingRight: 10, display: 'inline' }} className="ExamNameHeader">
<span >{this.state.exam_name}</span>
<Header className="exam-header">
<div className="exam-header-left">
<span className="exam-title">{this.state.exam_name}</span>
</div>
<div className="ExamTimeHeader">
<Countdown title="Time Left" value={this.state.deadline} onFinish={this.onSubmitExam} />
<div className="exam-header-center">
<Countdown
title="Time Left"
value={this.state.deadline}
onFinish={this.onSubmitExam}
className="exam-countdown"
/>
</div>
<div style={{ paddingLeft: 10, display: 'inline' }}>
<Button onClick={this.onPauseExam} style={{ marginRight: 8 }}>Leave</Button>
</div>
<div style={{ paddingRight: 10, display: 'inline' }}>
<Button type="primary" onClick={this.onSubmitExam}
style={{ marginRight: 8 }}>Submit</Button>
<div className="exam-header-right">
<Button onClick={this.onPauseExam}>Leave</Button>
<Button type="primary" onClick={this.onSubmitExam}>
Submit
</Button>
</div>
</Header>
<Content
className="site-layout-background"
style={{
padding: 24,
margin: 0,
}}
style={{ padding: 24, margin: 0 }}
>
<Layout className="MainExamLayout">
<Layout className="createExamLayoutLeft">{
<Tabs defaultActiveKey="1" size={"large"} type={"line"} style={{ marginBottom: 32 }} onTabClick={this.onTabPaneClick.bind(this)}>
{ listItems }</Tabs>
}
<Layout className="create-exam-card">
<Tabs
defaultActiveKey="1"
size="large"
type="line"
animated
onTabClick={this.onTabPaneClick.bind(this)}
className="create-exam-tabs"
>
{listItems}
</Tabs>
</Layout>
<Layout className="createExamLayoutRight">
<div>
<table className="ExamStatusTable">
<tbody>
<tr >
<td><span className="ExamAvatar"><Avatar className="StatusAvatar QuesttionStatusNotVisited" >{total_notVisited}</Avatar><span className="StatusLabel">{" Not Visited "}</span></span></td>
<td><span className="ExamAvatar"><Avatar className="StatusAvatar QuesttionStatusNotAnswered" >{total_notAnswered}</Avatar><span className="StatusLabel">{" Not Answered "}</span></span></td>
</tr>
<tr>
<td><span className="ExamAvatar"><Avatar className="StatusAvatar QuesttionStatusAnswered">{total_Answered}</Avatar><span className="StatusLabel">{" Answered "}</span></span></td>
<td><span className="ExamAvatar"><Avatar className="StatusAvatar QuesttionStatusReview" >{total_Reviwed}</Avatar><span className="StatusLabel">{" To Review "}</span></span></td>
</tr>
<tr>
<td><span className="ExamAvatar"><Avatar className="StatusAvatar QuesttionStatusAnsweredReview">{total_AnsweredReviwed}</Avatar><span className="StatusLabel">{" Answered & To Review "}</span></span></td>
<Layout className="exam-status-card">
<div className="exam-status-section">
<table className="exam-status-table">
<tbody>
<tr>
<td>
<div className="status-item">
<Avatar className="status-avatar not-visited">{total_notVisited}</Avatar>
<span className="status-label">Not Visited</span>
</div>
</td>
<td>
<div className="status-item">
<Avatar className="status-avatar not-answered">{total_notAnswered}</Avatar>
<span className="status-label">Not Answered</span>
</div>
</td>
</tr>
<tr>
<td>
<div className="status-item">
<Avatar className="status-avatar answered">{total_Answered}</Avatar>
<span className="status-label">Answered</span>
</div>
</td>
<td>
<div className="status-item">
<Avatar className="status-avatar to-review">{total_Reviwed}</Avatar>
<span className="status-label">To Review</span>
</div>
</td>
</tr>
<tr>
<td colSpan="2">
<div className="status-item">
<Avatar className="status-avatar answered-review">{total_AnsweredReviwed}</Avatar>
<span className="status-label">Answered & To Review</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<Collapse accordion>
{accordions}
</Collapse>
<div className="exam-accordion">
<Collapse accordion>{accordions}</Collapse>
</div>
</Layout>
</Layout>
</Content>
</Layout>
</Layout>
</>
)
};
};
);
}
}
export default AttemptExam;

View File

@ -2,12 +2,11 @@ import React from "react";
import "./AttemptExam.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 { 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";
class QuestionDetail extends React.Component {
constructor(props) {
super(props);
@ -28,20 +27,21 @@ class QuestionDetail extends React.Component {
loadNextQuestion = () => {
this.props.parentCallbackNext(this.props.question);
}
};
onReviewQuestion = (val) => {
this.state.question.isReviewMarked = val;
this.sendData(this.state.question);
}
onAnswerChange = (event) =>{
onAnswerChange = (event) => {
let questionType = this.state.question.type_code;
let id = event.target.value;
let tempQuestion = this.state.question;
if(questionType ==='MCQ' || questionType ==='TNF'){
if (questionType === 'MCQ' || questionType === 'TNF') {
let options = this.state.question.options.map((option, index) => {
if(option.id === id)
if (option.id === id)
option.isSelected = event.target.checked;
else
option.isSelected = false;
@ -51,8 +51,9 @@ class QuestionDetail extends React.Component {
}
this.setState({
question : tempQuestion
question: tempQuestion
})
this.sendData(this.state.question);
}
@ -64,81 +65,81 @@ class QuestionDetail extends React.Component {
let tempQuestion = this.state.question;
tempQuestion.options = options;
this.setState({
question : tempQuestion
question: tempQuestion
})
this.sendData(this.state.question);
}
render() {
this.state.question = this.props.question;
const { question, totalQuestion } = this.props;
this.state.question = question;
let answer = "";
let answerOption = this.state.question.options.map((option,i)=>{
if(option.isSelected)
answer = option.id;
return <Radio className='radioStyle' key={option.id} value={option.id} checked={option.isSelected}>
const answerOptions = question.options.map((option) => {
if (option.isSelected) answer = option.id;
return (
<Radio className="radioStyle" key={option.id} value={option.id}>
{option.text}
</Radio>
);
});
})
let questionDetails= <Layout className="MainExamQuestionLayout">
<Row>
return (
<Layout className="MainExamQuestionLayout" style={{ background: "#fff" }}>
<Row className="question-header">
<Col>
<h3>{this.props.question.index}. {parse(this.props.question.question_text)}</h3>
<h3 className="question-title">
{question.index}. {parse(question.question_text)}
</h3>
</Col>
</Row>
<Row className="Options">
<Col>
<div> <Radio.Group onChange={this.onAnswerChange} value={answer}>
{
answerOption
}
<Row className="question-options">
<Col>
<Radio.Group
onChange={this.onAnswerChange}
value={answer}
className="radio-group"
>
{answerOptions}
</Radio.Group>
</Col>
</Row>
<Row className="review-switch">
<Col>
<Switch
checkedChildren="Review On"
unCheckedChildren="Review Off"
checked={question.isReviewMarked}
onChange={this.onReviewQuestion}
/>
</Col>
</Row>
<Row className="question-actions">
<Col>
<div className="action-buttons">
<Button
onClick={this.loadNextQuestion}
type="primary"
disabled={question.index >= totalQuestion}
className="rounded-btn"
>
Next Question
</Button>
<Button
onClick={this.onClearResponse}
className="rounded-btn"
>
Clear Response
</Button>
</div>
</Col>
</Row>
<Row>
<Col>
<br/>
<Switch checkedChildren="Review On" unCheckedChildren="Review Off" checked={this.state.question.isReviewMarked} onChange={this.onReviewQuestion} />
<br/>
</Col>
</Row>
<Row>
<Col>
<br/>
<br/>
</Col>
</Row>
<ButtonGroup><Button
onClick={this.loadNextQuestion}
type="primary"
disabled={this.props.question.index>=this.props.totalQuestion}
style={{ marginRight: 8 }}
>
{" "}
Next Question{" "}
</Button>
<Button
onClick={this.onClearResponse}
style={{ marginRight: 8 }}
>
{" "}
Clear Response{" "}
</Button></ButtonGroup>
</Layout>
return (
<>
{questionDetails}
</>
)
};
);
}
};
export default QuestionDetail;

View File

@ -28,7 +28,8 @@ class LiveExams extends React.Component {
const json = {};
json.pageSize = 10;
json.pageNumber = 1;
json.batch = this.state.batch;
const storedUser = JSON.parse(sessionStorage.getItem("currentUser")) || {};
json.batch = storedUser.batch_id || null;
selectorService.loadLiveExams(json).then((data1) => {
// console.log('Success:', data1);
//data = $.parseJSON(data);

View File

@ -5,7 +5,7 @@ import { TEACHER_API, INSTITUTE_API, LOAD_CLASSES, LOAD_LANGAUGES, LOAD_QUESTION
// checking role from login -
function checkRole() {
const role = authenticationService.currentUserValue?.role_id;
const GATEWAY = role === 3? TEACHER_API : role === 2 && INSTITUTE_API;
const GATEWAY = role === 3 ? TEACHER_API : role === 2 && INSTITUTE_API;
// console.log(role, GATEWAY);
return GATEWAY;
}
@ -101,6 +101,7 @@ export const selectorService = {
createPracticeAttempt,
getPracticeAttempt,
stopPublishedExam,
heartbeat,
};
async function stopPublishedExam(examId) {
@ -119,7 +120,7 @@ async function stopPublishedExam(examId) {
try {
const res = await fetch(`${checkRole()}/Exams/${examId}/StopExam`, requestOptions);
const data = await res.json();
if(res.ok && +data.status.code > 0) {
if (res.ok && +data.status.code > 0) {
return 1;
}
throw "Something went wrong";
@ -168,112 +169,159 @@ function loadReport(examId) {
});
}
function pauseExam(attempt_id) {
async function pauseExam(attemptId) {
const currentUser = authenticationService.currentUserValue;
const requestOptions = {
method: "PUT",
if (!currentUser?.jwtToken) {
console.error("❌ No valid user token found");
return null;
}
const languageCode = currentUser.language_code || "En";
const url = `https://api.odiprojects.com/api-student/v1/${languageCode}/ExamAttempts/${attemptId}/Pause`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Access-Control-Allow-Origin": "*",
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer ".concat(currentUser.jwtToken),
Authorization: `Bearer ${currentUser.jwtToken}`,
},
//body: JSON.stringify(json)
// body: JSON.stringify(this.state)
//body: JSON.stringify({ emailId:"sa@odiware.com", userPassword:"aaaa" })
};
return fetch(
"https://api.odiprojects.com/api-institute/v1/en/ExamAttempts/" +
attempt_id +
"/Pause",
requestOptions
)
.then((response) => {
if (response.ok) {
return response.json();
}
});
if (!response.ok) {
console.error(`❌ Failed to pause exam (HTTP ${response.status})`);
// Optional: handle session expiration
// if (response.status === 401) {
// window.localStorage.clear();
// history.push("/login");
})
.then((data) => {
console.log(data);
// }
return null;
}
const data = await response.json();
console.log("⏸️ Exam paused successfully:", data);
return data;
})
.catch((error) => {
// console.error('Error:', error);
});
} catch (error) {
console.error("🚨 Error pausing exam:", error);
return null;
}
}
function endExam(attempt_id, json) {
async function endExam(attemptId, payload) {
const currentUser = authenticationService.currentUserValue;
const requestOptions = {
method: "PUT",
if (!currentUser?.jwtToken) {
console.error("❌ No valid user token found");
return null;
}
const languageCode = currentUser.language_code || "En";
const url = `https://api.odiprojects.com/api-student/v1/${languageCode}/ExamAttempts/${attemptId}/End`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Access-Control-Allow-Origin": "*",
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer ".concat(currentUser.jwtToken),
Authorization: `Bearer ${currentUser.jwtToken}`,
},
body: JSON.stringify(json),
// body: JSON.stringify(this.state)
//body: JSON.stringify({ emailId:"sa@odiware.com", userPassword:"aaaa" })
};
return fetch(
"https://api.odiprojects.com/api-institute/v1/en/ExamAttempts/" +
attempt_id +
"/End",
requestOptions
)
.then((response) => {
if (response.ok) {
return response.json();
}
body: JSON.stringify(payload),
});
if (!response.ok) {
console.error(`❌ Failed to end exam (HTTP ${response.status})`);
// Optional: handle expired sessions
// if (response.status === 401) {
// window.localStorage.clear();
// history.push("/login");
})
.then((data) => {
console.log(data);
// }
return null;
}
const data = await response.json();
console.log("✅ Exam ended successfully:", data);
return data;
})
.catch((error) => {
// console.error('Error:', error);
});
} catch (error) {
console.error("🚨 Error ending exam:", error);
return null;
}
}
function updateExamStatus(attempt_id, json) {
async function heartbeat(attemptId) {
const currentUser = authenticationService.currentUserValue;
const requestOptions = {
method: "PUT",
if (!currentUser?.jwtToken) {
console.error("❌ No valid user token found");
return null;
}
const url = `https://api.odiprojects.com/api-student/v1/ExamAttempts/${attemptId}/Heartbeat`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Access-Control-Allow-Origin": "*",
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer ".concat(currentUser.jwtToken),
Authorization: `Bearer ${currentUser.jwtToken}`,
},
body: JSON.stringify(json),
// body: JSON.stringify(this.state)
//body: JSON.stringify({ emailId:"sa@odiware.com", userPassword:"aaaa" })
};
return fetch(
"https://api.odiprojects.com/api-institute/v1/ExamAttempts/" +
attempt_id +
"/HeartBeat",
requestOptions
)
.then((response) => {
if (response.ok) {
return response.json();
body: JSON.stringify({
attempt_id: attemptId,
timestamp: new Date().toISOString(),
}),
});
if (!response.ok) {
console.error(`❌ Heartbeat failed (HTTP ${response.status})`);
return null;
}
const data = await response.json();
console.log("💓 Heartbeat sent successfully:", data);
return data; // ✅ Return the parsed response data
} catch (error) {
console.error("🚨 Error sending heartbeat:", error);
return null;
}
}
async function updateExamStatus(attemptId, payload) {
const currentUser = authenticationService.currentUserValue;
if (!currentUser?.jwtToken) {
console.error("❌ No valid user token found");
return null;
}
const url = `https://api.odiprojects.com/api-student/v1/ExamAttempts/${attemptId}/UpdateAnswer`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${currentUser.jwtToken}`,
},
body: payload,
});
if (!response.ok) {
console.error(`❌ Failed to update exam status (HTTP ${response.status})`);
// Optional: Handle unauthorized users
// if (response.status === 401) {
// window.localStorage.clear();
// history.push("/login");
})
.then((data) => {
console.log(data);
// }
return null;
}
const data = await response.json();
console.log("✅ Exam status updated:", data);
return data;
})
.catch((error) => {
// console.error('Error:', error);
});
} catch (error) {
console.error("🚨 Error updating exam status:", error);
return null;
}
}
function updateBatchName(obj) {
@ -353,43 +401,45 @@ function getPracticeAttempt(practiceId) {
}
function getExamAttempt(attemptId) {
async function getExamAttempt(attemptId) {
const currentUser = authenticationService.currentUserValue;
const requestOptions = {
if (!currentUser?.jwtToken) {
console.error("❌ No valid user token found");
return null;
}
const languageCode = currentUser.language_code || "En";
const languageName = currentUser.language_name || "English";
const url = `https://api.odiprojects.com/api-student/v1/${languageCode}/ExamAttempts/${attemptId}/Questions`;
try {
const response = await fetch(url, {
method: "GET",
headers: {
"Access-Control-Allow-Origin": "*",
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer ".concat(currentUser.jwtToken),
Authorization: `Bearer ${currentUser.jwtToken}`,
},
};
if (currentUser.language_code === null) {
currentUser.language_code = "En";
currentUser.language_name = "English";
}
return fetch(
"https://api.odiprojects.com/api-institute/v1/" +
currentUser.language_code +
"/ExamAttempts/" +
attemptId +
"/Questions",
requestOptions
)
.then((response) => {
if (response.ok) {
return response.json();
}
});
if (!response.ok) {
console.error(`❌ Request failed with status: ${response.status}`);
// Optional: handle unauthorized user
// if (response.status === 401) {
// window.localStorage.clear();
// history.push("/login");
})
.then((data) => {
console.log(data);
// }
return null;
}
const data = await response.json();
console.log("✅ Exam attempt data:", data);
return data;
})
.catch((error) => {
// console.error('Error:', error);
});
} catch (error) {
console.error("🚨 Error fetching exam attempt:", error);
return null;
}
}
function getAllUser(page_size, page_number) {
@ -404,7 +454,7 @@ function getAllUser(page_size, page_number) {
},
};
return fetch(
checkRole() + "/users?"+ `pageNumber=${page_number}&pageSize=${page_size}`,
checkRole() + "/users?" + `pageNumber=${page_number}&pageSize=${page_size}`,
requestOptions
)
.then((response) => {
@ -641,42 +691,47 @@ function createPracticeAttempt(practiceId) {
});
}
function createExamAttempt(examId) {
async function createExamAttempt(examId) {
const currentUser = authenticationService.currentUserValue;
const requestOptions = {
if (!currentUser?.jwtToken) {
console.error("❌ No valid user token found");
return null;
}
const languageCode = currentUser.language_code || "En";
const languageName = currentUser.language_name || "English";
const url = `https://api.odiprojects.com/api-student/v1/${languageCode}/ExamAttempts/${examId}`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Access-Control-Allow-Origin": "*",
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer ".concat(currentUser.jwtToken),
Authorization: `Bearer ${currentUser.jwtToken}`,
},
};
if (currentUser.language_code === null) {
currentUser.language_code = "En";
currentUser.language_name = "English";
}
return fetch(
"https://api.odiprojects.com/api-institute/v1/" +
currentUser.language_code +
"/ExamAttempts/" +
examId,
requestOptions
)
.then((response) => {
if (response.ok) {
return response.json();
}
});
console.log(response);
if (!response.ok) {
console.error(`❌ Request failed with status: ${response.status}`);
// Optionally handle unauthorized user
// if (response.status === 401) {
// window.localStorage.clear();
// history.push("/login");
})
.then((data) => {
console.log(data);
// }
return null;
}
const data = await response.json();
console.log("✅ Exam attempt created:", data);
return data;
})
.catch((error) => {
// console.error('Error:', error);
});
} catch (error) {
console.error("🚨 Error creating exam attempt:", error);
return null;
}
}
function loadLiveExams(jsonObj) {
@ -701,7 +756,7 @@ function loadLiveExams(jsonObj) {
currentUser.language_name = "English";
}
return fetch(
"https://api.odiprojects.com/api-institute/v1/" +
"https://api.odiprojects.com/api-student/v1/" +
currentUser.language_code +
"/Batches/" +
jsonObj.batch +
@ -721,7 +776,7 @@ function loadLiveExams(jsonObj) {
console.log(data);
return data;
})
.catch((error) => {});
.catch((error) => { });
}
function loadUpcomingExam(jsonObj) {
@ -766,7 +821,7 @@ function loadUpcomingExam(jsonObj) {
console.log(data);
return data;
})
.catch((error) => {});
.catch((error) => { });
}
function getAllClass() {
@ -1151,7 +1206,7 @@ async function addBatchToPractice(obj) {
};
try {
const res = await fetch( checkRole() + "/Practices/" + obj.id + "/AttachBatch", requestOptions );
const res = await fetch(checkRole() + "/Practices/" + obj.id + "/AttachBatch", requestOptions);
const data = res.json();
return data;
} catch (error) {
@ -1212,7 +1267,7 @@ async function detachBatchFromPractice(obj) {
};
try {
const res = await fetch( checkRole() + "/Practices/" + obj.id + "/DetachBatch", requestOptions );
const res = await fetch(checkRole() + "/Practices/" + obj.id + "/DetachBatch", requestOptions);
const data = res.json();
return data;
} catch (error) {
@ -1314,8 +1369,8 @@ async function removeSubjectFromClass(id) {
};
try {
const res = await fetch(checkRole()+"/Subjects/"+id, requestOptions);
if(!res.ok) {
const res = await fetch(checkRole() + "/Subjects/" + id, requestOptions);
if (!res.ok) {
throw "Something went wrong";
}
const data = await res.json();
@ -1342,8 +1397,8 @@ async function removeTopicFromClass(id) {
};
try {
const res = await fetch(checkRole()+"/Categories/"+id, requestOptions);
if(!res.ok) {
const res = await fetch(checkRole() + "/Categories/" + id, requestOptions);
if (!res.ok) {
throw "Something went wrong";
}
const data = await res.json();
@ -1675,7 +1730,7 @@ function loadReviewQuestions(sectionId) {
// console.log(data)
return data;
})
.catch((error) => {});
.catch((error) => { });
}
function loadReviewQuestionsPractice(practiceId) {
@ -1747,7 +1802,7 @@ function loadSections(examId) {
// console.log(data)
return data;
})
.catch((error) => {});
.catch((error) => { });
}
function loadSectionsForPractice(practiceId) {
@ -1799,7 +1854,7 @@ function getQuestion(question_id, questionLang = undefined) {
currentUser.language_code = "En";
currentUser.language_name = "English";
}
if(!questionLang) {
if (!questionLang) {
return fetch(checkRole() + "/" + currentUser.language_code + "/Questions/" + question_id, requestOptions)
.then((response) => {
// console.log(response);
@ -1813,7 +1868,7 @@ function getQuestion(question_id, questionLang = undefined) {
// console.log(data);
return data;
})
.catch((error) => {});
.catch((error) => { });
} else {
return fetch(checkRole() + "/" + questionLang + "/Questions/" + question_id, requestOptions)
.then((response) => {
@ -1828,7 +1883,7 @@ function getQuestion(question_id, questionLang = undefined) {
// console.log(data);
return data;
})
.catch((error) => {});
.catch((error) => { });
}
}
@ -2010,7 +2065,7 @@ async function createQuestion(question) {
// });
try {
const res = await fetch(checkRole() + "/" + currentUser.language_code + "/Questions", requestOptions);
if(res.ok) {
if (res.ok) {
const data = await res.json();
return data;
}
@ -2043,7 +2098,7 @@ async function createBulkQuestion(questions) {
try {
const res = await fetch(checkRole() + "/" + currentUser.language_code + "/AddBulkQuestions", requestOptions);
if(res.ok) {
if (res.ok) {
const data = await res.json();
return data;
}
@ -2076,7 +2131,7 @@ async function editQuestion(questionID, questionModifications) {
}
try {
const res = await fetch(checkRole() + "/" + currentUser.language_code + "/Questions/" + questionID, requestOptions);
if(res.ok) {
if (res.ok) {
const data = await res.json();
return data;
}
@ -2108,7 +2163,7 @@ async function cloneQuestionToLanguage(questionID, questionObj) {
}
try {
const res = await fetch(checkRole() + "/" + currentUser.language_code + "/Questions/" + questionID + "/CloneQuestionLanguage", requestOptions);
if(res.ok) {
if (res.ok) {
const data = await res.json();
return data;
}
@ -2184,7 +2239,7 @@ function getExamDetails(examId) {
// console.log(data);
return data;
})
.catch((error) => {});
.catch((error) => { });
}
function getPracticeDetails(practiceId) {
@ -2274,7 +2329,7 @@ function attachQuestionsToExam(examId, questions) {
};
return fetch(
// "https://api.odiprojects.com/api-institute/v"+
checkRole()+
checkRole() +
"/ExamSections/" +
examId +
"/AttachQuestions",
@ -2681,8 +2736,8 @@ function loadQuestions(jsonObj) {
}
}
if (jsonObj.translationMissing) {
queryStr === "" ? queryStr = "?"+"translation_missing="+jsonObj.translationMissing
: queryStr = queryStr+ "&translation_missing="+jsonObj.translationMissing
queryStr === "" ? queryStr = "?" + "translation_missing=" + jsonObj.translationMissing
: queryStr = queryStr + "&translation_missing=" + jsonObj.translationMissing
}
if (currentUser.language_code === null) {
@ -2759,7 +2814,7 @@ function draftExam(jsonObj) {
)
.then((response) => {
if (!currentClass) {
throw new Error ("No class is set, please set a class and try again.");
throw new Error("No class is set, please set a class and try again.");
}
if (response.ok) {
return response.json();
@ -2912,7 +2967,7 @@ function upcomingPractice(jsonObj) {
}
return fetch(
// "https://api.odiprojects.com/api-institute/v1"+
checkRole()+ "/Classes/" + currentClass +
checkRole() + "/Classes/" + currentClass +
"/UpcomingSubjectPractices" +
queryStr,
requestOptions
@ -3139,7 +3194,7 @@ function liveExam(jsonObj) {
// history.push("/login");
})
.then((data) => {
if(!data) throw ("Something went wrong, please reload the page and try again.");
if (!data) throw ("Something went wrong, please reload the page and try again.");
// console.log('Success:', data);
return data;
@ -3360,8 +3415,8 @@ function loadQuestionsBookmark(jsonObj) {
}
}
if (jsonObj.translationMissing) {
queryStr === "" ? queryStr = "?"+"translation_missing="+jsonObj.translationMissing
: queryStr = queryStr+ "&translation_missing="+jsonObj.translationMissing
queryStr === "" ? queryStr = "?" + "translation_missing=" + jsonObj.translationMissing
: queryStr = queryStr + "&translation_missing=" + jsonObj.translationMissing
}
if (currentUser.language_code === null) {
currentUser.language_code = "En";
@ -3399,7 +3454,7 @@ function loadQuestionsBookmark(jsonObj) {
}
async function loadCategory(subject) {
if(subject != "") {
if (subject != "") {
const currentUser = authenticationService.currentUserValue;
// console.log(subject);
const requestOptions = {
@ -3415,7 +3470,7 @@ async function loadCategory(subject) {
}
try {
const response= await fetch( `${checkRole()}/Subjects/` + subject + "/Categories", requestOptions )
const response = await fetch(`${checkRole()}/Subjects/` + subject + "/Categories", requestOptions)
const data = await response.json();
return data;
} catch (error) {
@ -3442,7 +3497,7 @@ async function loadSubjects(classes) {
try {
if (!currentClass) throw 'Class has not been set. Please set a class or try reloading the page again'; // error handling
const response = await fetch(`${checkRole()}/Classes` + "/" + currentClass +"/Subjects", requestOptions)
const response = await fetch(`${checkRole()}/Classes` + "/" + currentClass + "/Subjects", requestOptions)
const data = await response.json();
return data;
@ -3549,7 +3604,7 @@ function draftQuestion(jsonObj) {
"?module_id=" + jsonObj.module_id + "&module=" + jsonObj.module;
}
if (jsonObj.complexity !== undefined && jsonObj.complexity !== "") {
queryStr !== ""? queryStr = queryStr + "&complexity=" + jsonObj.complexity
queryStr !== "" ? queryStr = queryStr + "&complexity=" + jsonObj.complexity
: queryStr = "?complexity=" + jsonObj.complexity;
}
if (queryStr !== "") {
@ -3564,8 +3619,8 @@ function draftQuestion(jsonObj) {
jsonObj.pageSize;
}
if (jsonObj.translationMissing) {
queryStr === "" ? queryStr = "?"+"translation_missing="+jsonObj.translationMissing
: queryStr = queryStr+ "&translation_missing="+jsonObj.translationMissing
queryStr === "" ? queryStr = "?" + "translation_missing=" + jsonObj.translationMissing
: queryStr = queryStr + "&translation_missing=" + jsonObj.translationMissing
}
// console.log(queryStr);