diff --git a/default_frame.php b/default_frame.php index 20e2334..f660bb1 100644 --- a/default_frame.php +++ b/default_frame.php @@ -15,15 +15,24 @@ if (!get_autologin_state()) { SpreadQuiz - + +
+
+
+
+
+ +
+ +
+ @@ -31,7 +32,13 @@ $privilege = $user_data["privilege"];
-
+
+ +
+
+ +
+
diff --git a/style/quizmaster_area.css b/style/quizmaster_area.css new file mode 100644 index 0000000..025e7dd --- /dev/null +++ b/style/quizmaster_area.css @@ -0,0 +1,45 @@ +section#table_section { + position: sticky; + height: calc(100vh - 4em); + overflow-y: auto; +} + +table.management { + width: 100%; + border-collapse: collapse; +} + +table.management thead th { + padding: 0.5em 0; + position: sticky; + top: 0; + background-color: darkgray; +} + +table.management tbody td { + border: 1pt lightgrey solid; + padding: 0.3em 0.5em; +} + +table.management tbody tr[highlight="true"] { + background-color: antiquewhite; +} + +table.management tbody tr[highlight="false"] td input { + pointer-events: none; +} + +section#user_manager_action_bar { + margin-top: 0.5em; +} + +td.checkbox { + width: 0; +} + +.hintbox-window { + position: fixed; + display: block; + width: 15em; + height: 8em; +} \ No newline at end of file diff --git a/style/spreadquiz.css b/style/spreadquiz.css index b707720..3114d6c 100644 --- a/style/spreadquiz.css +++ b/style/spreadquiz.css @@ -1,3 +1,10 @@ +@import url('https://fonts.googleapis.com/css2?family=Autour+One&family=Kanit:wght@500&display=swap'); +@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"); + +body { + font-family: 'Autour One', sans-serif; +} + *[shown="false"] { visibility: hidden; display: none; @@ -10,6 +17,10 @@ opacity: 1; } +.material { + font-family: 'Material Symbols Outlined', sans-serif; +} + /* ----------------- */ section#screen_panel { @@ -31,13 +42,15 @@ section#content_pane, section#info_pane { section#content_pane { left: 0; width: 80vw; - background-color: lightcyan; + /*background-color: lightcyan;*/ } section#info_pane { right: 0; width: 20vw; - background-color: beige; + background-color: whitesmoke; + border-left: 5px solid #176767; + box-shadow: -5px 0 #d3e5e5; } .info-pane-element { @@ -50,9 +63,19 @@ section#info_pane { section#user_info { position: relative; top: 0; - background-color: aquamarine; + background-color: #176767; + color: whitesmoke; font-size: 18pt; text-align: center; + height: 1.2em; + overflow: clip; + transition: 0.3s ease; + border-bottom: 0 dashed whitesmoke; +} + +section#user_info:hover { + height: 5em; + border-width: 0.5em; } section#action_panel { @@ -61,46 +84,11 @@ section#action_panel { iframe#content_frame { display: block; - width: 100%; + width: calc(100% - 1em); height: 100%; border: 0 transparent; } -section#table_section { - position: sticky; - height: calc(100vh - 4em); - overflow-y: auto; -} - -table.management { - width: 100%; - border-collapse: collapse; -} - -table.management thead th { - padding: 0.5em 0; - position: sticky; - top: 0; - background-color: darkgray; -} - -table.management tbody td { - border: 1pt lightgrey solid; - padding: 0.3em 0.5em; -} - -table.management tbody tr[highlight="true"] { - background-color: antiquewhite; -} - -table.management tbody tr[highlight="false"] td input { - pointer-events: none; -} - -section#user_manager_action_bar { - margin-top: 0.5em; -} - section.window { position: fixed; display: block; @@ -115,23 +103,12 @@ section.window { section.window-inner { margin: 0 auto; display: block; - border: 2pt solid darkslategray; + border: 2pt solid #176767; background-color: whitesmoke; padding: 1em; width: fit-content; } -td.checkbox { - width: 0; -} - -.hintbox-window { - position: fixed; - display: block; - width: 15em; - height: 8em; -} - /* ----- */ section#game_list_panel { @@ -145,15 +122,30 @@ section.group-box { margin: 1em; left: 0; right: 0; - background-color: beige; + background-color: #f8fbfb; + border: 0.5px solid #176767; + /*border-radius: 0 0 0.2em 0.2em;*/ + box-shadow: 5px 5px #d3e5e5; } -span.group-box-caption { +span.group-box-caption-container { display: block; left: 0; right: 0; + background-color: #176767; + box-shadow: 0 5px #d3e5e5; +} + +span.group-box-caption { + display: inline-block; + /*position: relative;*/ + /*top: -0.5em;*/ + color: #176767; + background-color: #d3e5e5; + font-weight: bold; + font-size: 1.2em; padding: 1em; - background-color: chartreuse; + margin-left: 1em; } section.group-box-inner { @@ -164,13 +156,45 @@ section.group-box-inner { } section.game-box { - display: table-cell; - width: 8em; - height: 8em; - border: 2pt dashed gray; + display: inline-block; + position: relative; + width: 12em; + height: 12em; + border: 2pt solid #176767; text-align: center; vertical-align: middle; cursor: pointer; + margin-right: 1em; + overflow: clip; + box-shadow: 5px 5px #d3e5e5; + transform: scale(100%) rotate(0); + background-color: whitesmoke; + transition: ease 0.3s; +} + +section.game-box:hover { + transform: scale(105%) rotate(3deg); + z-index: 1000; +} + +section.game-box-caption { + margin-top: 2em; + font-size: 1.3em; + rotate: -10deg; + background-color: #176767; + width: 13em; + position: relative; + left: -3em; + padding: 0.6em; + color: whitesmoke; + box-shadow: 0 5px #d3e5e5; +} + +section.list-results-btn { + display: block; + position: absolute; + right: 0.2em; + bottom: 0.2em; } section#test_area { @@ -181,11 +205,20 @@ section#test_area { } section.challenge { - + display: block; + margin: 0 auto; + width: 40em; + /*border: 1px solid black;*/ + padding: 1em; + background-color: #d3e5e5; + margin-bottom: 0.5em; + border-radius: 0.3em; } span.question { - + font-size: 1.3em; + font-weight: bold; + color: #176767; } section.answer-container { @@ -193,5 +226,112 @@ section.answer-container { } section.answer { + margin: 0.3em 0.8em; + display: block; +} +section.answer label { + margin-left: 0.5em; + padding: 0.3em 0.5em; + border-radius: 0.3em; +} + +section.answer label.correct-answer { + border: 2px solid #176767 !important; + padding: 0.1em; +} + +section.answer input[type="radio"]:checked+label:not(.correct-answer) { + background-color: #176767; + color: whitesmoke; +} + +section#infobox { + display: block; + position: fixed; + left: 0; + top: 0; + margin: 1em; + /*padding: 1em;*/ + background: #f8fbfb; + border: 0.5px solid #176767; + min-width: 15em; + min-height: 5em; + box-shadow: 5px 5px #d3e5e5; + overflow: clip; +} + +section#infobox > section { + padding: 1em; +} + +span#game_name { + display: block; + width: 100%; + padding: 1em; + background-color: #176767; + box-shadow: 0px 5px #d3e5e5; + color: whitesmoke; + font-size: 1.2em; + font-weight: bold; +} + +section#timer { + color: #176767; + margin-bottom: 0.5em; +} + +section#percentage { + color: #176767; +} + +span.infobox-description { + font-size: 0.8em; +} + +.btn { + background-color: transparent; + padding: 0.3em; + border-radius: 0.2em; + font-size: 1.3em; +} + +.btn:hover { + background-color: rgba(128, 128, 128, 0.30); +} + +section.test-summary-record { + position: relative; + cursor: pointer; + border: 0.5px solid #176767; + margin-bottom: 0.3em; +} + +.summary-sequence-number { + font-size: 2em; + display: inline-block; + background-color: #176767; + color: whitesmoke; + padding: 0.5em; + width: 1.5em; + box-shadow: 5px 0 #d3e5e5; +} + +.summary-duration { + display: inline-block; + margin-left: 1em; + color: #176767; + font-size: 0.7em; +} + +.summary-percentage { + display: inline-block; + position: absolute; + right: 0; + margin-left: 1em; + background-color: #176767; + padding: 0 1em; + box-shadow: -5px 0 #d3e5e5; + color: whitesmoke; + bottom: 0; } \ No newline at end of file diff --git a/testground.php b/testground.php index f0beeed..176bcb7 100644 --- a/testground.php +++ b/testground.php @@ -8,7 +8,10 @@ if (!get_autologin_state() || !isset($_REQUEST["testid"])) { exit(); } -$testid = $_REQUEST["testid"]; +$testid = trim($_REQUEST["testid"] ?: ""); +if ($testid === "") { + exit(); +} ?> @@ -17,15 +20,35 @@ $testid = $_REQUEST["testid"]; SpreadQuiz - + + + -
+
+
+
+ +
+
+ Hátralevő idő: +
10:00:00
+ +
+
+
+ Eredmény: +
95% (19/20)
+
+
+ \ No newline at end of file diff --git a/testmgr.php b/testmgr.php index 5215d80..8fd7699 100644 --- a/testmgr.php +++ b/testmgr.php @@ -29,7 +29,7 @@ function create_or_continue_test(string $gameid, string $nickname): string } // check if the user had taken this test before - $fetch_criteria = [["gameid", "=", $gameid], "AND", ["nickname", "=", $nickname]]; + $fetch_criteria = [["gameid", "=", (int)$gameid], "AND", ["nickname", "=", $nickname]]; $previous_tests = $testdb->findBy($fetch_criteria); if (count($previous_tests) > 0) { // if there are previous attempts, then... // update timed tests to see if they had expired @@ -53,12 +53,15 @@ function create_or_continue_test(string $gameid, string $nickname): string function create_test(array $game_data, array $user_data) : string { + global $testdb; $gameid = $game_data["_id"]; + $game_properties = $game_data["properties"]; // fill basic data - $game_data = [ + $test_data = [ "gameid" => $gameid, "nickname" => $user_data["nickname"], + "gamename" => $game_data["name"] ]; // fill challenges @@ -67,11 +70,33 @@ function create_test(array $game_data, array $user_data) : string // shuffle answers foreach ($challenges as &$ch) { shuffle($ch["answers"]); + $ch["correct_answer"] = ""; + $ch["player_answer"] = ""; } - $game_data["challenges"] = $challenges; - // involve properties + $now = time(); + $properties = [ + "state" => TEST_ONGOING, + "time_limited" => (($game_properties["time_limit"] ?: -1) > -1), + "start_time" => $now, + "repeatable" => $game_properties["repeatable"] ?: false + ]; + if ($properties["time_limited"]) { + $properties["end_limit_time"] = $now + $game_properties["time_limit"]; + } + + // merge properties and test data + $test_data = array_merge($test_data, $properties); + + // add challenges + $test_data["challenges"] = $challenges; + + // store game + $test_data = $testdb->insert($test_data); + + $testid = $test_data["_id"]; + return $testid; } function update_timed_tests(array $test_data_array) @@ -79,9 +104,8 @@ function update_timed_tests(array $test_data_array) $now = time(); foreach ($test_data_array as $test_data) { // look for unprocessed expired tests - if (($test_data["state"] === TEST_ONGOING) && ($test_data["end_limit_time"] < $now)) { - $test_data["state"] = TEST_CONCLUDED; - update_test($test_data); + if (($test_data["state"] === TEST_ONGOING) && ($test_data["time_limited"]) && ($test_data["end_limit_time"] < $now)) { + conclude_test($test_data["_id"]); } } } @@ -90,4 +114,69 @@ function update_test(array $test_data) { global $testdb; $testdb->update($test_data); +} + +function get_test(string $testid): array +{ + global $testdb; + return $testdb->findById($testid); +} + +function save_answer(string $testid, $chidx, $ansidx) +{ + $test_data = get_test($testid); + $chidx = (int)$chidx; + if ((count($test_data) > 0) && ($test_data["state"] === TEST_ONGOING)) { + if ($chidx < count($test_data["challenges"])) { + $test_data["challenges"][$chidx]["player_answer"] = $ansidx; + update_test($test_data); + } + } +} + +function get_concluded_tests(string $gameid, string $nickname) { + global $testdb; + $fetch_criteria = [["gameid", "=", (int)$gameid], "AND", ["nickname", "=", $nickname], "AND", ["state", "=", TEST_CONCLUDED]]; + $test_data_array = $testdb->findBy($fetch_criteria); + return $test_data_array; +} + +function conclude_test(string $testid) { + $test_data = get_test($testid); + if (count($test_data) === 0) { + return; + } + + // load game data + //$game_data = get_game($test_data["gameid"]); + $game_challenges = load_challenges($test_data["gameid"]); + + // check the answers + $challenge_n = count($test_data["challenges"]); // number of challenges + $cans_n = 0; // number of correct answers + for ($chidx = 0; $chidx < $challenge_n; $chidx++) { + // get challenge + $tch = &$test_data["challenges"][$chidx]; + $gch = $game_challenges[$chidx]; + + // translate correct answer into an index by the shuffled answer order + $cans_idx = array_search($gch["correct_answer"], $tch["answers"]); + $tch["correct_answer"] = $cans_idx; + + // check the player's answer + $player_answer = trim($tch["player_answer"]); + if (($player_answer !== "") && ($cans_idx === (int)$player_answer)) { + $cans_n++; + } + } + + // set state and fill summary + $test_data["state"] = TEST_CONCLUDED; + $test_data["end_time"] = time(); + $test_data["summary"] = [ + "challenge_n" => $challenge_n, + "correct_answer_n" => $cans_n + ]; + + update_test($test_data); } \ No newline at end of file