From 08e6c1bef34a387b14657e78382f186f66036ab1 Mon Sep 17 00:00:00 2001
From: Philippe Ozil <5071767+pozil@users.noreply.github.com>
Date: Fri, 10 Apr 2020 00:41:07 +0200
Subject: [PATCH] Added question picker (#57)

---
 package-lock.json                             | 222 ++++++++++--------
 package.json                                  |  12 +-
 src/main/default/classes/QuizController.cls   |  10 +-
 .../default/classes/QuizEditorController.cls  |  28 +++
 .../classes/QuizEditorController.cls-meta.xml |   5 +
 .../default/classes/QuizQuestionService.cls   |   5 +
 .../classes/QuizQuestionService.cls-meta.xml  |   5 +
 .../default/classes/QuizSessionService.cls    |  29 +++
 ...uiz_Session_Record_Page.flexipage-meta.xml |  42 ++++
 ...ion__c-Quiz Session Layout.layout-meta.xml |  22 +-
 .../lwc/answerBarChart/answerBarChart.js      |   8 +-
 src/main/default/lwc/errorUtils/errorUtils.js |   8 +-
 src/main/default/lwc/gameApp/gameApp.js       |  16 +-
 .../default/lwc/leaderBoard/leaderBoard.js    |   4 +-
 src/main/default/lwc/playerList/playerList.js |  12 +-
 .../lwc/questionPicker/questionPicker.html    |  34 +++
 .../lwc/questionPicker/questionPicker.js      | 131 +++++++++++
 .../questionPicker/questionPicker.js-meta.xml |  16 ++
 .../Quiz_Session__c.object-meta.xml           | 107 +++++++++
 .../fields/Current_Question__c.field-meta.xml |   4 +-
 20 files changed, 593 insertions(+), 127 deletions(-)
 create mode 100755 src/main/default/classes/QuizEditorController.cls
 create mode 100755 src/main/default/classes/QuizEditorController.cls-meta.xml
 create mode 100755 src/main/default/classes/QuizQuestionService.cls
 create mode 100755 src/main/default/classes/QuizQuestionService.cls-meta.xml
 create mode 100644 src/main/default/flexipages/Quiz_Session_Record_Page.flexipage-meta.xml
 create mode 100755 src/main/default/lwc/questionPicker/questionPicker.html
 create mode 100755 src/main/default/lwc/questionPicker/questionPicker.js
 create mode 100755 src/main/default/lwc/questionPicker/questionPicker.js-meta.xml

diff --git a/package-lock.json b/package-lock.json
index 5429c77..7e2a316 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
     "name": "sfdx-quiz",
-    "version": "1.0.2",
+    "version": "1.1.0",
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {
@@ -626,9 +626,9 @@
             }
         },
         "@babel/runtime": {
-            "version": "7.8.7",
-            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz",
-            "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==",
+            "version": "7.9.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
+            "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
             "dev": true,
             "requires": {
                 "regenerator-runtime": "^0.13.4"
@@ -684,17 +684,17 @@
             },
             "dependencies": {
                 "minimist": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-                    "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+                    "version": "1.2.5",
+                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+                    "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
                     "dev": true
                 }
             }
         },
         "@hapi/address": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.0.0.tgz",
-            "integrity": "sha512-GDDpkCdSUfkQCznmWUHh9dDN85BWf/V8TFKQ2JLuHdGB4Yy3YTEGBzZxoBNxfNBEvreSR/o+ZxBBSNNEVzY+lQ==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.0.1.tgz",
+            "integrity": "sha512-0oEP5UiyV4f3d6cBL8F3Z5S7iWSX39Knnl0lY8i+6gfmmIBj44JCBNtcMgwyS+5v7j3VYavNay0NFHDS+UGQcw==",
             "dev": true,
             "requires": {
                 "@hapi/hoek": "^9.0.0"
@@ -707,18 +707,18 @@
             "dev": true
         },
         "@hapi/hoek": {
-            "version": "9.0.3",
-            "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.3.tgz",
-            "integrity": "sha512-jKtjLLDiH95b002sJVc5c74PE6KKYftuyVdVmsuYId5stTaWcRFqE+5ukZI4gDUKjGn8wv2C3zPn3/nyjEI7gg==",
+            "version": "9.0.4",
+            "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz",
+            "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==",
             "dev": true
         },
         "@hapi/joi": {
-            "version": "17.1.0",
-            "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.0.tgz",
-            "integrity": "sha512-ob67RcPlwRWxBzLCnWvcwx5qbwf88I3ykD7gcJLWOTRfLLgosK7r6aeChz4thA3XRvuBfI0KB1tPVl2EQFlPXw==",
+            "version": "17.1.1",
+            "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz",
+            "integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==",
             "dev": true,
             "requires": {
-                "@hapi/address": "^4.0.0",
+                "@hapi/address": "^4.0.1",
                 "@hapi/formula": "^2.0.0",
                 "@hapi/hoek": "^9.0.0",
                 "@hapi/pinpoint": "^2.0.0",
@@ -1393,9 +1393,9 @@
             },
             "dependencies": {
                 "acorn": {
-                    "version": "6.4.0",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
-                    "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==",
+                    "version": "6.4.1",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+                    "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
                     "dev": true
                 }
             }
@@ -4340,14 +4340,14 @@
             "dev": true
         },
         "husky": {
-            "version": "4.2.3",
-            "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.3.tgz",
-            "integrity": "sha512-VxTsSTRwYveKXN4SaH1/FefRJYCtx+wx04sSVcOpD7N2zjoHxa+cEJ07Qg5NmV3HAK+IRKOyNVpi2YBIVccIfQ==",
+            "version": "4.2.5",
+            "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz",
+            "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==",
             "dev": true,
             "requires": {
-                "chalk": "^3.0.0",
+                "chalk": "^4.0.0",
                 "ci-info": "^2.0.0",
-                "compare-versions": "^3.5.1",
+                "compare-versions": "^3.6.0",
                 "cosmiconfig": "^6.0.0",
                 "find-versions": "^3.2.0",
                 "opencollective-postinstall": "^2.0.2",
@@ -4368,9 +4368,9 @@
                     }
                 },
                 "chalk": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
-                    "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
+                    "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
                     "dev": true,
                     "requires": {
                         "ansi-styles": "^4.1.0",
@@ -4418,9 +4418,9 @@
                     }
                 },
                 "p-limit": {
-                    "version": "2.2.2",
-                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
-                    "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+                    "version": "2.3.0",
+                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+                    "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
                     "dev": true,
                     "requires": {
                         "p-try": "^2.0.0"
@@ -6107,9 +6107,9 @@
             "dev": true
         },
         "lint-staged": {
-            "version": "10.0.8",
-            "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.0.8.tgz",
-            "integrity": "sha512-Oa9eS4DJqvQMVdywXfEor6F4vP+21fPHF8LUXgBbVWUSWBddjqsvO6Bv1LwMChmgQZZqwUvgJSHlu8HFHAPZmA==",
+            "version": "10.1.3",
+            "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.1.3.tgz",
+            "integrity": "sha512-o2OkLxgVns5RwSC5QF7waeAjJA5nz5gnUfqL311LkZcFipKV7TztrSlhNUK5nQX9H0E5NELAdduMQ+M/JPT7RQ==",
             "dev": true,
             "requires": {
                 "chalk": "^3.0.0",
@@ -6178,9 +6178,9 @@
                     "dev": true
                 },
                 "cross-spawn": {
-                    "version": "7.0.1",
-                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
-                    "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
+                    "version": "7.0.2",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
+                    "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
                     "dev": true,
                     "requires": {
                         "path-key": "^3.1.0",
@@ -6732,9 +6732,9 @@
             }
         },
         "minimist": {
-            "version": "0.0.8",
-            "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-            "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+            "version": "1.2.5",
+            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
             "dev": true
         },
         "mixin-deep": {
@@ -6759,12 +6759,12 @@
             }
         },
         "mkdirp": {
-            "version": "0.5.1",
-            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-            "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+            "version": "0.5.5",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+            "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
             "dev": true,
             "requires": {
-                "minimist": "0.0.8"
+                "minimist": "^1.2.5"
             }
         },
         "ms": {
@@ -7227,9 +7227,9 @@
             "dev": true
         },
         "picomatch": {
-            "version": "2.2.1",
-            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
-            "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==",
+            "version": "2.2.2",
+            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+            "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
             "dev": true
         },
         "pify": {
@@ -9089,15 +9089,15 @@
             "dev": true
         },
         "prettier": {
-            "version": "1.19.1",
-            "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
-            "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.4.tgz",
+            "integrity": "sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w==",
             "dev": true
         },
         "prettier-plugin-apex": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/prettier-plugin-apex/-/prettier-plugin-apex-1.2.0.tgz",
-            "integrity": "sha512-6cD08DsmIGTj/p1FHBGQ98JhL+ocbvfGahtsczDwfTXLQbJZ3UpFY8eAfB17gLuA7Z0HwUC/1u3NWddhjYsuJQ==",
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/prettier-plugin-apex/-/prettier-plugin-apex-1.3.0.tgz",
+            "integrity": "sha512-rgfUP5OQIqHKkh/XtGzvcJirO9WA5BgMb235tCQ9dA+8X5PTldk1oJJJSJd1/WfzRhCE6W4obKHbs99nJL+cCQ==",
             "dev": true,
             "requires": {
                 "axios": "^0.19.0",
@@ -9177,9 +9177,9 @@
                     "dev": true
                 },
                 "jest-docblock": {
-                    "version": "25.1.0",
-                    "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.1.0.tgz",
-                    "integrity": "sha512-370P/mh1wzoef6hUKiaMcsPtIapY25suP6JqM70V9RJvdKLrV4GaGbfUseUVk4FZJw4oTZ1qSCJNdrClKt5JQA==",
+                    "version": "25.3.0",
+                    "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.3.0.tgz",
+                    "integrity": "sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg==",
                     "dev": true,
                     "requires": {
                         "detect-newline": "^3.0.0"
@@ -9195,9 +9195,9 @@
                     }
                 },
                 "p-limit": {
-                    "version": "2.2.2",
-                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
-                    "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+                    "version": "2.3.0",
+                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+                    "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
                     "dev": true,
                     "requires": {
                         "p-try": "^2.0.0"
@@ -9256,9 +9256,9 @@
                     }
                 },
                 "yargs": {
-                    "version": "15.3.0",
-                    "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz",
-                    "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==",
+                    "version": "15.3.1",
+                    "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
+                    "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
                     "dev": true,
                     "requires": {
                         "cliui": "^6.0.0",
@@ -9271,13 +9271,13 @@
                         "string-width": "^4.2.0",
                         "which-module": "^2.0.0",
                         "y18n": "^4.0.0",
-                        "yargs-parser": "^18.1.0"
+                        "yargs-parser": "^18.1.1"
                     }
                 },
                 "yargs-parser": {
-                    "version": "18.1.0",
-                    "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.0.tgz",
-                    "integrity": "sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ==",
+                    "version": "18.1.2",
+                    "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
+                    "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
                     "dev": true,
                     "requires": {
                         "camelcase": "^5.0.0",
@@ -9452,9 +9452,9 @@
             }
         },
         "regenerator-runtime": {
-            "version": "0.13.4",
-            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz",
-            "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==",
+            "version": "0.13.5",
+            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
+            "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
             "dev": true
         },
         "regenerator-transform": {
@@ -9990,9 +9990,9 @@
             },
             "dependencies": {
                 "minimist": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-                    "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+                    "version": "1.2.5",
+                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+                    "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
                     "dev": true
                 }
             }
@@ -10013,9 +10013,9 @@
             }
         },
         "semver": {
-            "version": "7.1.3",
-            "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz",
-            "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==",
+            "version": "7.2.1",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.2.1.tgz",
+            "integrity": "sha512-aHhm1pD02jXXkyIpq25qBZjr3CQgg8KST8uX0OWXch3xE6jw+1bfbWnCjzMwojsTquroUmKFHNzU6x26mEiRxw==",
             "dev": true
         },
         "semver-compare": {
@@ -11048,33 +11048,71 @@
             }
         },
         "wait-on": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-4.0.1.tgz",
-            "integrity": "sha512-x83fmTH2X0KL7vXoGt9aV5x4SMCvO8A/NbwWpaYYh4NJ16d3KSgbHwBy9dVdHj0B30cEhOFRvDob4fnpUmZxvA==",
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-4.0.2.tgz",
+            "integrity": "sha512-Qpmgm3Hw/sXm7xK68FBsYy5r+Uid94/QymwnEjn9GTpfiWTUVYm0bccivVwY/BXGYO2r+5Cd8S/DzrRZqHK/9w==",
             "dev": true,
             "requires": {
-                "@hapi/joi": "^17.1.0",
+                "@hapi/joi": "^17.1.1",
                 "lodash": "^4.17.15",
-                "minimist": "^1.2.0",
-                "request": "^2.88.0",
+                "minimist": "^1.2.5",
+                "request": "^2.88.2",
                 "request-promise-native": "^1.0.8",
-                "rxjs": "^6.5.4"
+                "rxjs": "^6.5.5"
             },
             "dependencies": {
                 "minimist": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-                    "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-                    "dev": true
+                    "version": "1.2.5",
+                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+                    "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+                    "dev": true
+                },
+                "request": {
+                    "version": "2.88.2",
+                    "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+                    "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+                    "dev": true,
+                    "requires": {
+                        "aws-sign2": "~0.7.0",
+                        "aws4": "^1.8.0",
+                        "caseless": "~0.12.0",
+                        "combined-stream": "~1.0.6",
+                        "extend": "~3.0.2",
+                        "forever-agent": "~0.6.1",
+                        "form-data": "~2.3.2",
+                        "har-validator": "~5.1.3",
+                        "http-signature": "~1.2.0",
+                        "is-typedarray": "~1.0.0",
+                        "isstream": "~0.1.2",
+                        "json-stringify-safe": "~5.0.1",
+                        "mime-types": "~2.1.19",
+                        "oauth-sign": "~0.9.0",
+                        "performance-now": "^2.1.0",
+                        "qs": "~6.5.2",
+                        "safe-buffer": "^5.1.2",
+                        "tough-cookie": "~2.5.0",
+                        "tunnel-agent": "^0.6.0",
+                        "uuid": "^3.3.2"
+                    }
                 },
                 "rxjs": {
-                    "version": "6.5.4",
-                    "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
-                    "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+                    "version": "6.5.5",
+                    "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz",
+                    "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==",
                     "dev": true,
                     "requires": {
                         "tslib": "^1.9.0"
                     }
+                },
+                "tough-cookie": {
+                    "version": "2.5.0",
+                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+                    "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+                    "dev": true,
+                    "requires": {
+                        "psl": "^1.1.28",
+                        "punycode": "^2.1.1"
+                    }
                 }
             }
         },
@@ -11231,9 +11269,9 @@
             "dev": true
         },
         "yaml": {
-            "version": "1.8.0",
-            "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.8.0.tgz",
-            "integrity": "sha512-6qI/tTx7OVtA4qNqD0OyutbM6Z9EKu4rxWm/2Y3FDEBQ4/2X2XAnyuRXMzAE2+1BPyqzksJZtrIwblOHg0IEzA==",
+            "version": "1.8.3",
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.8.3.tgz",
+            "integrity": "sha512-X/v7VDnK+sxbQ2Imq4Jt2PRUsRsP7UcpSl3Llg6+NRRqWLIvxkMFYtH1FmvwNGYRKKPa+EPA4qDBlI9WVG1UKw==",
             "dev": true,
             "requires": {
                 "@babel/runtime": "^7.8.7"
diff --git a/package.json b/package.json
index 8a411b1..c84c1d6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "sfdx-quiz",
-    "version": "1.0.2",
+    "version": "1.1.0",
     "private": true,
     "description": "Lightning Web Component Quiz App on a Salesforce Org",
     "engines": {
@@ -21,11 +21,11 @@
         "@salesforce/eslint-config-lwc": "^0.5.0",
         "@salesforce/sfdx-lwc-jest": "^0.7.1",
         "eslint": "^6.8.0",
-        "husky": "^4.2.3",
-        "lint-staged": "^10.0.8",
-        "prettier": "^1.19.1",
-        "prettier-plugin-apex": "^1.2.0",
-        "semver": "^7.1.3",
+        "husky": "^4.2.5",
+        "lint-staged": "^10.1.3",
+        "prettier": "^2.0.4",
+        "prettier-plugin-apex": "^1.3.0",
+        "semver": "^7.2.1",
         "replace-in-file": "^5.0.2"
     },
     "husky": {
diff --git a/src/main/default/classes/QuizController.cls b/src/main/default/classes/QuizController.cls
index 5a8acf6..cb89dbe 100644
--- a/src/main/default/classes/QuizController.cls
+++ b/src/main/default/classes/QuizController.cls
@@ -44,7 +44,15 @@ public with sharing class QuizController {
         if (sessionId == null) {
             throw new AuraHandledException('Missing session Id.');
         }
-        return sessionService.getCurrentQuestion(sessionId);
+        Quiz_Question__c question = sessionService.getCurrentQuestion(
+            sessionId
+        );
+        if (question == null) {
+            throw new AuraHandledException(
+                'Missing current question for session.'
+            );
+        }
+        return question;
     }
 
     @AuraEnabled
diff --git a/src/main/default/classes/QuizEditorController.cls b/src/main/default/classes/QuizEditorController.cls
new file mode 100755
index 0000000..63028c2
--- /dev/null
+++ b/src/main/default/classes/QuizEditorController.cls
@@ -0,0 +1,28 @@
+public with sharing class QuizEditorController {
+    private static final QuizQuestionService questionService = new QuizQuestionService();
+    private static final QuizSessionService sessionService = new QuizSessionService();
+
+    @AuraEnabled(cacheable=true)
+    public static List<Quiz_Question__c> getAllQuestions() {
+        return questionService.getQuestions();
+    }
+
+    @AuraEnabled
+    public static void setSessionQuestions(Id sessionId, List<Id> questionIds) {
+        if (sessionId == null) {
+            throw new AuraHandledException('Missing session Id.');
+        }
+        if (questionIds == null) {
+            throw new AuraHandledException('Missing questions Ids.');
+        }
+        sessionService.setSessionQuestions(sessionId, questionIds);
+    }
+
+    @AuraEnabled(cacheable=true)
+    public static List<Quiz_Question__c> getSessionQuestions(Id sessionId) {
+        if (sessionId == null) {
+            throw new AuraHandledException('Missing session Id.');
+        }
+        return sessionService.getSessionQuestions(sessionId);
+    }
+}
diff --git a/src/main/default/classes/QuizEditorController.cls-meta.xml b/src/main/default/classes/QuizEditorController.cls-meta.xml
new file mode 100755
index 0000000..db9bf8c
--- /dev/null
+++ b/src/main/default/classes/QuizEditorController.cls-meta.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
+    <apiVersion>48.0</apiVersion>
+    <status>Active</status>
+</ApexClass>
diff --git a/src/main/default/classes/QuizQuestionService.cls b/src/main/default/classes/QuizQuestionService.cls
new file mode 100755
index 0000000..853d9e3
--- /dev/null
+++ b/src/main/default/classes/QuizQuestionService.cls
@@ -0,0 +1,5 @@
+public class QuizQuestionService extends QuizAbstractDataService {
+    public List<Quiz_Question__c> getQuestions() {
+        return [SELECT Id, Label__c FROM Quiz_Question__c];
+    }
+}
diff --git a/src/main/default/classes/QuizQuestionService.cls-meta.xml b/src/main/default/classes/QuizQuestionService.cls-meta.xml
new file mode 100755
index 0000000..db9bf8c
--- /dev/null
+++ b/src/main/default/classes/QuizQuestionService.cls-meta.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
+    <apiVersion>48.0</apiVersion>
+    <status>Active</status>
+</ApexClass>
diff --git a/src/main/default/classes/QuizSessionService.cls b/src/main/default/classes/QuizSessionService.cls
index 7be620b..0539d5f 100644
--- a/src/main/default/classes/QuizSessionService.cls
+++ b/src/main/default/classes/QuizSessionService.cls
@@ -54,4 +54,33 @@ public class QuizSessionService extends QuizAbstractDataService {
         }
         return questions;
     }
+
+    public void setSessionQuestions(Id sessionId, List<Id> questionIds) {
+        // Get session
+        Quiz_Session__c session = [
+            SELECT Current_Question__c
+            FROM Quiz_Session__c
+            WHERE Id = :sessionId
+        ];
+        // Overwrite session questions
+        delete [
+            SELECT Id
+            FROM Quiz_Session_Question__c
+            WHERE Session__c = :sessionId
+        ];
+        List<Quiz_Session_Question__c> questions = new List<Quiz_Session_Question__c>();
+        for (Integer i = 0; i < questionIds.size(); i++) {
+            questions.add(
+                new Quiz_Session_Question__c(
+                    Question__c = questionIds[i],
+                    Question_Index__c = i,
+                    Session__c = sessionId
+                )
+            );
+        }
+        insert questions;
+        // Replace current question
+        session.Current_Question__c = questionIds[0];
+        update session;
+    }
 }
diff --git a/src/main/default/flexipages/Quiz_Session_Record_Page.flexipage-meta.xml b/src/main/default/flexipages/Quiz_Session_Record_Page.flexipage-meta.xml
new file mode 100644
index 0000000..4898d8a
--- /dev/null
+++ b/src/main/default/flexipages/Quiz_Session_Record_Page.flexipage-meta.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<FlexiPage xmlns="http://soap.sforce.com/2006/04/metadata">
+    <flexiPageRegions>
+        <componentInstances>
+            <componentInstanceProperties>
+                <name>collapsed</name>
+                <value>false</value>
+            </componentInstanceProperties>
+            <componentInstanceProperties>
+                <name>hideChatterActions</name>
+                <value>false</value>
+            </componentInstanceProperties>
+            <componentInstanceProperties>
+                <name>numVisibleActions</name>
+                <value>3</value>
+            </componentInstanceProperties>
+            <componentName>force:highlightsPanel</componentName>
+        </componentInstances>
+        <name>header</name>
+        <type>Region</type>
+    </flexiPageRegions>
+    <flexiPageRegions>
+        <componentInstances>
+            <componentName>questionPicker</componentName>
+        </componentInstances>
+        <name>leftcol</name>
+        <type>Region</type>
+    </flexiPageRegions>
+    <flexiPageRegions>
+        <componentInstances>
+            <componentName>force:detailPanel</componentName>
+        </componentInstances>
+        <name>rightcol</name>
+        <type>Region</type>
+    </flexiPageRegions>
+    <masterLabel>Quiz Session Record Page</masterLabel>
+    <sobjectType>Quiz_Session__c</sobjectType>
+    <template>
+        <name>flexipage:recordHomeTwoColEqualHeaderTemplateDesktop</name>
+    </template>
+    <type>RecordPage</type>
+</FlexiPage>
diff --git a/src/main/default/layouts/Quiz_Session__c-Quiz Session Layout.layout-meta.xml b/src/main/default/layouts/Quiz_Session__c-Quiz Session Layout.layout-meta.xml
index 2d24b02..be3addb 100644
--- a/src/main/default/layouts/Quiz_Session__c-Quiz Session Layout.layout-meta.xml	
+++ b/src/main/default/layouts/Quiz_Session__c-Quiz Session Layout.layout-meta.xml	
@@ -14,7 +14,7 @@
         </layoutColumns>
         <layoutColumns>
             <layoutItems>
-                <behavior>Required</behavior>
+                <behavior>Edit</behavior>
                 <field>Current_Question__c</field>
             </layoutItems>
         </layoutColumns>
@@ -57,6 +57,24 @@
         <layoutColumns/>
         <style>CustomLinks</style>
     </layoutSections>
+    <platformActionList>
+        <actionListContext>Record</actionListContext>
+        <platformActionListItems>
+            <actionName>Delete</actionName>
+            <actionType>StandardButton</actionType>
+            <sortOrder>0</sortOrder>
+        </platformActionListItems>
+        <platformActionListItems>
+            <actionName>Edit</actionName>
+            <actionType>StandardButton</actionType>
+            <sortOrder>1</sortOrder>
+        </platformActionListItems>
+        <platformActionListItems>
+            <actionName>Clone</actionName>
+            <actionType>StandardButton</actionType>
+            <sortOrder>2</sortOrder>
+        </platformActionListItems>
+    </platformActionList>
     <relatedLists>
         <fields>NAME</fields>
         <fields>Quiz_Question__c.NAME</fields>
@@ -72,7 +90,7 @@
     <showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox>
     <showSubmitAndAttachButton>false</showSubmitAndAttachButton>
     <summaryLayout>
-        <masterLabel>00h1g000000MR8k</masterLabel>
+        <masterLabel>00h1j000000sXjK</masterLabel>
         <sizeX>4</sizeX>
         <sizeY>0</sizeY>
         <summaryLayoutStyle>Default</summaryLayoutStyle>
diff --git a/src/main/default/lwc/answerBarChart/answerBarChart.js b/src/main/default/lwc/answerBarChart/answerBarChart.js
index afbcc4d..885bd88 100644
--- a/src/main/default/lwc/answerBarChart/answerBarChart.js
+++ b/src/main/default/lwc/answerBarChart/answerBarChart.js
@@ -16,11 +16,11 @@ export default class AnswerBarChart extends LightningElement {
 
     connectedCallback() {
         getAnswerStats()
-            .then(data => {
-                this.answerStats = ANSWER_LABELS.map(label => data[label]);
+            .then((data) => {
+                this.answerStats = ANSWER_LABELS.map((label) => data[label]);
                 this.error = undefined;
             })
-            .catch(error => {
+            .catch((error) => {
                 this.error = reduceErrors(error);
                 this.answerStats = undefined;
             });
@@ -99,7 +99,7 @@ export default class AnswerBarChart extends LightningElement {
                 };
                 this.chart = new window.Chart(ctx, config);
             })
-            .catch(error => {
+            .catch((error) => {
                 this.answerStats = undefined;
                 this.error = error;
             });
diff --git a/src/main/default/lwc/errorUtils/errorUtils.js b/src/main/default/lwc/errorUtils/errorUtils.js
index 9bd1b67..e07fdae 100644
--- a/src/main/default/lwc/errorUtils/errorUtils.js
+++ b/src/main/default/lwc/errorUtils/errorUtils.js
@@ -14,12 +14,12 @@ export function reduceErrors(errors) {
     return (
         errors
             // Remove null/undefined items
-            .filter(error => !!error)
+            .filter((error) => !!error)
             // Extract an error message
-            .map(error => {
+            .map((error) => {
                 // UI API read errors
                 if (Array.isArray(error.body)) {
-                    return error.body.map(e => e.message);
+                    return error.body.map((e) => e.message);
                 }
                 // UI API DML, Apex and network errors
                 else if (error.body && typeof error.body.message === 'string') {
@@ -35,6 +35,6 @@ export function reduceErrors(errors) {
             // Flatten
             .reduce((prev, curr) => prev.concat(curr), [])
             // Remove empty strings
-            .filter(message => !!message)
+            .filter((message) => !!message)
     );
 }
diff --git a/src/main/default/lwc/gameApp/gameApp.js b/src/main/default/lwc/gameApp/gameApp.js
index a35460a..e8ba06c 100644
--- a/src/main/default/lwc/gameApp/gameApp.js
+++ b/src/main/default/lwc/gameApp/gameApp.js
@@ -14,7 +14,7 @@ export default class GameApp extends LightningElement {
     @track isNextButtonDisabled = true;
     @track currentQuestion;
 
-    HOST_APP_VERSION = '1.0.2';
+    HOST_APP_VERSION = '1.1.0';
 
     @wire(getQuizSettings)
     wiredQuizSettings({ error, data }) {
@@ -27,16 +27,16 @@ export default class GameApp extends LightningElement {
     }
 
     connectedCallback() {
-        checkSettings().catch(error => {
+        checkSettings().catch((error) => {
             this.error = reduceErrors(error);
             this.isNextButtonDisabled = true;
         });
         getQuizSession()
-            .then(quizSession => {
+            .then((quizSession) => {
                 this.quizSession = quizSession;
                 this.refreshCurrentQuestion();
             })
-            .catch(error => {
+            .catch((error) => {
                 this.error = reduceErrors(error);
                 this.quizSession = undefined;
             });
@@ -44,7 +44,7 @@ export default class GameApp extends LightningElement {
 
     refreshCurrentQuestion() {
         getCurrentQuestion({ sessionId: this.quizSession.Id })
-            .then(currentQuestion => {
+            .then((currentQuestion) => {
                 this.currentQuestion = currentQuestion;
                 // Double phase change click prevention
                 // eslint-disable-next-line @lwc/lwc/no-async-operation
@@ -54,7 +54,7 @@ export default class GameApp extends LightningElement {
                     }
                 }, 2000);
             })
-            .catch(error => {
+            .catch((error) => {
                 this.error = reduceErrors(error);
                 this.currentQuestion = undefined;
                 this.isNextButtonDisabled = true;
@@ -65,12 +65,12 @@ export default class GameApp extends LightningElement {
         this.isNextButtonDisabled = true;
         this.answerCount = undefined;
         triggerNextPhase({ sessionId: this.quizSession.Id })
-            .then(updatedSession => {
+            .then((updatedSession) => {
                 this.quizSession = updatedSession;
                 this.error = undefined;
                 this.refreshCurrentQuestion();
             })
-            .catch(error => {
+            .catch((error) => {
                 this.error = reduceErrors(error);
                 this.quizSession = undefined;
             });
diff --git a/src/main/default/lwc/leaderBoard/leaderBoard.js b/src/main/default/lwc/leaderBoard/leaderBoard.js
index f10ce93..26c4a08 100644
--- a/src/main/default/lwc/leaderBoard/leaderBoard.js
+++ b/src/main/default/lwc/leaderBoard/leaderBoard.js
@@ -8,11 +8,11 @@ export default class LeaderBoard extends LightningElement {
 
     connectedCallback() {
         getPlayersSortedByScore({ maxFetchCount: 10 })
-            .then(players => {
+            .then((players) => {
                 this.error = undefined;
                 this.displayPlayers(players);
             })
-            .catch(error => {
+            .catch((error) => {
                 this.error = reduceErrors(error);
                 this.players = undefined;
             });
diff --git a/src/main/default/lwc/playerList/playerList.js b/src/main/default/lwc/playerList/playerList.js
index 8173fc1..063193e 100755
--- a/src/main/default/lwc/playerList/playerList.js
+++ b/src/main/default/lwc/playerList/playerList.js
@@ -11,24 +11,24 @@ export default class playerList extends LightningElement {
 
     connectedCallback() {
         getPlayersSortedByScore()
-            .then(players => {
-                this.playerNames = players.map(player => player.Name);
+            .then((players) => {
+                this.playerNames = players.map((player) => player.Name);
                 this.error = undefined;
                 this.initEmpApi();
             })
-            .catch(error => {
+            .catch((error) => {
                 this.error = reduceErrors(error);
                 this.playerNames = undefined;
             });
     }
 
     initEmpApi() {
-        empApi.onError(error => {
+        empApi.onError((error) => {
             // eslint-disable-next-line no-console
             console.error('Streaming API error: ' + JSON.stringify(error));
         });
         empApi
-            .subscribe('/data/Quiz_Player__ChangeEvent', -1, cdcEvent => {
+            .subscribe('/data/Quiz_Player__ChangeEvent', -1, (cdcEvent) => {
                 if (
                     cdcEvent.data.payload.ChangeEventHeader.changeType ===
                     'CREATE'
@@ -36,7 +36,7 @@ export default class playerList extends LightningElement {
                     this.handlePlayerCreationEvent(cdcEvent);
                 }
             })
-            .then(response => {
+            .then((response) => {
                 this.subscription = response;
             });
     }
diff --git a/src/main/default/lwc/questionPicker/questionPicker.html b/src/main/default/lwc/questionPicker/questionPicker.html
new file mode 100755
index 0000000..1fd468d
--- /dev/null
+++ b/src/main/default/lwc/questionPicker/questionPicker.html
@@ -0,0 +1,34 @@
+<template>
+    <lightning-card title={cardTitle}>
+        <lightning-button
+            slot="actions"
+            label="Select random questions"
+            onclick={handleRandomizeClick}
+        ></lightning-button>
+
+        <div class="slds-var-p-horizontal_small">
+            <lightning-spinner
+                if:true={isLoading}
+                variant="brand"
+                alternative-text="Loading questions"
+            ></lightning-spinner>
+
+            <lightning-dual-listbox
+                label="Select and sort questions for this session"
+                source-label="Available"
+                selected-label={selectedLabel}
+                options={allQuestions}
+                value={selectedQuestionIds}
+                onchange={handleQuestionChange}
+            ></lightning-dual-listbox>
+            <div class="slds-var-m-top_small slds-align_absolute-center">
+                <lightning-button
+                    label="Save"
+                    variant="brand"
+                    onclick={handleSaveClick}
+                    disabled={isLoading}
+                ></lightning-button>
+            </div>
+        </div>
+    </lightning-card>
+</template>
diff --git a/src/main/default/lwc/questionPicker/questionPicker.js b/src/main/default/lwc/questionPicker/questionPicker.js
new file mode 100755
index 0000000..b4f3010
--- /dev/null
+++ b/src/main/default/lwc/questionPicker/questionPicker.js
@@ -0,0 +1,131 @@
+import { LightningElement, api, wire } from 'lwc';
+import { ShowToastEvent } from 'lightning/platformShowToastEvent';
+import setSessionQuestions from '@salesforce/apex/QuizEditorController.setSessionQuestions';
+import getSessionQuestions from '@salesforce/apex/QuizEditorController.getSessionQuestions';
+import getAllQuestions from '@salesforce/apex/QuizEditorController.getAllQuestions';
+
+export default class QuestionPicker extends LightningElement {
+    @api recordId;
+
+    allQuestions = [];
+    selectedQuestionIds = [];
+    isSaving = false;
+    isDirty = false;
+
+    @wire(getAllQuestions)
+    getAllQuestions({ data, error }) {
+        if (data) {
+            this.allQuestions = data.map((question) => ({
+                value: question.Id,
+                label: question.Label__c
+            }));
+        } else if (error) {
+            console.error(error);
+            this.showToast('Failed to load all questions', undefined, 'error');
+        }
+    }
+
+    @wire(getSessionQuestions, { sessionId: '$recordId' })
+    getSessionQuestions({ data, error }) {
+        if (data) {
+            this.selectedQuestionIds = data.map((question) => question.Id);
+        } else if (error) {
+            console.error(error);
+            this.showToast(
+                'Failed to load session questions',
+                undefined,
+                'error'
+            );
+        }
+    }
+
+    handleQuestionChange(event) {
+        this.selectedQuestionIds = event.detail.value;
+        this.isDirty = true;
+    }
+
+    handleSaveClick() {
+        this.isSaving = true;
+        setSessionQuestions({
+            sessionId: this.recordId,
+            questionIds: this.selectedQuestionIds
+        })
+            .then(() => {
+                this.isSaving = false;
+                this.isDirty = false;
+                this.showToast('Success', 'Session questions saved', 'success');
+            })
+            .catch((error) => {
+                console.error(error);
+                this.showToast(
+                    'Failed to save session questions',
+                    undefined,
+                    'error'
+                );
+            });
+    }
+
+    handleRandomizeClick() {
+        // Prompt number of questions
+        // eslint-disable-next-line no-alert
+        const input = prompt(
+            'Enter the number of questions you would like to select',
+            '5'
+        );
+        const totalQuestionCount = this.allQuestions.length;
+        const selectionCount = parseInt(input, 10);
+        if (
+            isNaN(selectionCount) ||
+            selectionCount < 1 ||
+            selectionCount > totalQuestionCount
+        ) {
+            this.showToast(
+                'Operation aborted',
+                'Invalid question count',
+                'info'
+            );
+            return;
+        }
+        // Select random questions
+        const selectedIds = [];
+        const allQuestions = JSON.parse(JSON.stringify(this.allQuestions));
+        for (let i = 0; i < selectionCount; i++) {
+            const index = Math.floor(Math.random() * allQuestions.length);
+            const question = allQuestions.splice(index, 1)[0];
+            selectedIds.push(question.value);
+        }
+        // Save selection
+        this.selectedQuestionIds = selectedIds;
+        this.isDirty = true;
+        // Notify user
+        this.showToast(
+            'Success',
+            `${selectionCount} random questions selected. Don't forget to save them.`,
+            'success'
+        );
+    }
+
+    showToast(title, message, variant) {
+        this.dispatchEvent(
+            new ShowToastEvent({
+                title,
+                message,
+                variant
+            })
+        );
+    }
+
+    get isLoading() {
+        return (
+            this.isSaving || !(this.allQuestions && this.selectedQuestionIds)
+        );
+    }
+
+    get cardTitle() {
+        return `Session Questions ${this.isDirty ? '(modified)' : ''}`;
+    }
+
+    get selectedLabel() {
+        return `Selected (${this.selectedQuestionIds.length})`;
+    }
+}
diff --git a/src/main/default/lwc/questionPicker/questionPicker.js-meta.xml b/src/main/default/lwc/questionPicker/questionPicker.js-meta.xml
new file mode 100755
index 0000000..16a698f
--- /dev/null
+++ b/src/main/default/lwc/questionPicker/questionPicker.js-meta.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
+    <apiVersion>48.0</apiVersion>
+    <isExposed>true</isExposed>
+    <masterLabel>Question Picker</masterLabel>
+    <targets>
+        <target>lightning__RecordPage</target>
+    </targets>
+    <targetConfigs>
+        <targetConfig targets="lightning__RecordPage">
+            <objects>
+                <object>Quiz_Session__c</object>
+            </objects>
+        </targetConfig>
+    </targetConfigs>
+</LightningComponentBundle>
\ No newline at end of file
diff --git a/src/main/default/objects/Quiz_Session__c/Quiz_Session__c.object-meta.xml b/src/main/default/objects/Quiz_Session__c/Quiz_Session__c.object-meta.xml
index b8fb5bb..75d6da9 100644
--- a/src/main/default/objects/Quiz_Session__c/Quiz_Session__c.object-meta.xml
+++ b/src/main/default/objects/Quiz_Session__c/Quiz_Session__c.object-meta.xml
@@ -4,38 +4,144 @@
         <actionName>Accept</actionName>
         <type>Default</type>
     </actionOverrides>
+    <actionOverrides>
+        <actionName>Accept</actionName>
+        <formFactor>Large</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>Accept</actionName>
+        <formFactor>Small</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>CancelEdit</actionName>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>CancelEdit</actionName>
+        <formFactor>Large</formFactor>
+        <type>Default</type>
+    </actionOverrides>
     <actionOverrides>
         <actionName>CancelEdit</actionName>
+        <formFactor>Small</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>Clone</actionName>
         <type>Default</type>
     </actionOverrides>
     <actionOverrides>
         <actionName>Clone</actionName>
+        <formFactor>Large</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>Clone</actionName>
+        <formFactor>Small</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>Delete</actionName>
         <type>Default</type>
     </actionOverrides>
     <actionOverrides>
         <actionName>Delete</actionName>
+        <formFactor>Large</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>Delete</actionName>
+        <formFactor>Small</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>Edit</actionName>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>Edit</actionName>
+        <formFactor>Large</formFactor>
         <type>Default</type>
     </actionOverrides>
     <actionOverrides>
         <actionName>Edit</actionName>
+        <formFactor>Small</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>List</actionName>
         <type>Default</type>
     </actionOverrides>
     <actionOverrides>
         <actionName>List</actionName>
+        <formFactor>Large</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>List</actionName>
+        <formFactor>Small</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>New</actionName>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>New</actionName>
+        <formFactor>Large</formFactor>
         <type>Default</type>
     </actionOverrides>
     <actionOverrides>
         <actionName>New</actionName>
+        <formFactor>Small</formFactor>
         <type>Default</type>
     </actionOverrides>
     <actionOverrides>
         <actionName>SaveEdit</actionName>
         <type>Default</type>
     </actionOverrides>
+    <actionOverrides>
+        <actionName>SaveEdit</actionName>
+        <formFactor>Large</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>SaveEdit</actionName>
+        <formFactor>Small</formFactor>
+        <type>Default</type>
+    </actionOverrides>
     <actionOverrides>
         <actionName>Tab</actionName>
         <type>Default</type>
     </actionOverrides>
+    <actionOverrides>
+        <actionName>Tab</actionName>
+        <formFactor>Large</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>Tab</actionName>
+        <formFactor>Small</formFactor>
+        <type>Default</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>View</actionName>
+        <comment>Action override created by Lightning App Builder during activation.</comment>
+        <content>Quiz_Session_Record_Page</content>
+        <formFactor>Large</formFactor>
+        <skipRecordTypeSelect>false</skipRecordTypeSelect>
+        <type>Flexipage</type>
+    </actionOverrides>
+    <actionOverrides>
+        <actionName>View</actionName>
+        <comment>Action override created by Lightning App Builder during activation.</comment>
+        <content>Quiz_Session_Record_Page</content>
+        <formFactor>Small</formFactor>
+        <skipRecordTypeSelect>false</skipRecordTypeSelect>
+        <type>Flexipage</type>
+    </actionOverrides>
     <actionOverrides>
         <actionName>View</actionName>
         <type>Default</type>
@@ -52,6 +158,7 @@
     <enableSearch>true</enableSearch>
     <enableSharing>true</enableSharing>
     <enableStreamingApi>true</enableStreamingApi>
+    <externalSharingModel>Private</externalSharingModel>
     <label>Quiz Session</label>
     <nameField>
         <displayFormat>QS-{0000}</displayFormat>
diff --git a/src/main/default/objects/Quiz_Session__c/fields/Current_Question__c.field-meta.xml b/src/main/default/objects/Quiz_Session__c/fields/Current_Question__c.field-meta.xml
index 0cee65e..8ea0a30 100644
--- a/src/main/default/objects/Quiz_Session__c/fields/Current_Question__c.field-meta.xml
+++ b/src/main/default/objects/Quiz_Session__c/fields/Current_Question__c.field-meta.xml
@@ -1,13 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
     <fullName>Current_Question__c</fullName>
-    <deleteConstraint>Restrict</deleteConstraint>
+    <deleteConstraint>SetNull</deleteConstraint>
     <externalId>false</externalId>
     <label>Current Question</label>
     <referenceTo>Quiz_Question__c</referenceTo>
     <relationshipLabel>Sessions</relationshipLabel>
     <relationshipName>Quiz_Sessions</relationshipName>
-    <required>true</required>
+    <required>false</required>
     <trackTrending>false</trackTrending>
     <type>Lookup</type>
 </CustomField>