From 8063f011449172b59aec1126db5263844502a47a Mon Sep 17 00:00:00 2001 From: Sai Krishna <46342902+d-saikrishna@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:45:57 +0530 Subject: [PATCH 01/19] Create npm-grunt.yml --- .github/workflows/npm-grunt.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/npm-grunt.yml diff --git a/.github/workflows/npm-grunt.yml b/.github/workflows/npm-grunt.yml new file mode 100644 index 0000000..40ecd9d --- /dev/null +++ b/.github/workflows/npm-grunt.yml @@ -0,0 +1,28 @@ +name: NodeJS with Grunt + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Build + run: | + npm install + npm start From cd39cdcc247e028b87e8765e98592a3166759f94 Mon Sep 17 00:00:00 2001 From: Sai Krishna <46342902+d-saikrishna@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:33:25 +0530 Subject: [PATCH 02/19] Create node.js.yml --- .github/workflows/node.js.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/node.js.yml diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml new file mode 100644 index 0000000..340f454 --- /dev/null +++ b/.github/workflows/node.js.yml @@ -0,0 +1,31 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs + +name: Node.js CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm ci + - run: npm install + - run: npm start From edbea09de0d2aee84c0cd79ad84c159c714dedc0 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Wed, 18 Sep 2024 10:40:07 +0530 Subject: [PATCH 03/19] REACT APP DEPLOY GITHUB PAGES --- .github/workflows/node.js.yml | 31 --- .github/workflows/npm-grunt.yml | 28 --- package-lock.json | 341 +++++++++++++++++++++++++++++++- package.json | 6 +- 4 files changed, 344 insertions(+), 62 deletions(-) delete mode 100644 .github/workflows/node.js.yml delete mode 100644 .github/workflows/npm-grunt.yml diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index 340f454..0000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - -name: Node.js CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.x, 20.x, 22.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - run: npm ci - - run: npm install - - run: npm start diff --git a/.github/workflows/npm-grunt.yml b/.github/workflows/npm-grunt.yml deleted file mode 100644 index 40ecd9d..0000000 --- a/.github/workflows/npm-grunt.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: NodeJS with Grunt - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.x, 20.x, 22.x] - - steps: - - uses: actions/checkout@v4 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - - name: Build - run: | - npm install - npm start diff --git a/package-lock.json b/package-lock.json index 49fed82..7968a8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,24 @@ { - "name": "multiple-choice-quiz", + "name": "air-quality-quiz", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "multiple-choice-quiz", + "name": "air-quality-quiz", "version": "0.1.0", "dependencies": { "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", + "moment": "^2.30.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "5.0.0", "web-vitals": "^2.1.3" + }, + "devDependencies": { + "gh-pages": "^6.1.1" } }, "node_modules/@babel/code-frame": { @@ -4169,6 +4173,16 @@ "node": ">=8" } }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -6087,6 +6101,13 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.49.tgz", "integrity": "sha512-k/0t1TRfonHIp8TJKfjBu2cKj8MqYTiEpOhci+q7CVEE5xnCQnx1pTa+V8b/sdhe4S3PR4p4iceEQWhGrKQORQ==" }, + "node_modules/email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", + "dev": true, + "license": "MIT" + }, "node_modules/emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -7182,6 +7203,34 @@ "minimatch": "^3.0.4" } }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -7609,6 +7658,91 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gh-pages": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz", + "integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.4", + "commander": "^11.0.0", + "email-addresses": "^5.0.0", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^11.1.1", + "globby": "^6.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gh-pages/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/gh-pages/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/gh-pages/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/gh-pages/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -10855,6 +10989,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -11386,6 +11529,39 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", @@ -14064,6 +14240,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", @@ -14573,6 +14762,19 @@ "node": ">=8" } }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -18744,6 +18946,12 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true + }, "array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -20170,6 +20378,12 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.49.tgz", "integrity": "sha512-k/0t1TRfonHIp8TJKfjBu2cKj8MqYTiEpOhci+q7CVEE5xnCQnx1pTa+V8b/sdhe4S3PR4p4iceEQWhGrKQORQ==" }, + "email-addresses": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", + "dev": true + }, "emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -20972,6 +21186,23 @@ "minimatch": "^3.0.4" } }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "dev": true + }, + "filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, "filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -21260,6 +21491,68 @@ "get-intrinsic": "^1.1.1" } }, + "gh-pages": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz", + "integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==", + "dev": true, + "requires": { + "async": "^3.2.4", + "commander": "^11.0.0", + "email-addresses": "^5.0.0", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^11.1.1", + "globby": "^6.1.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } + } + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -23601,6 +23894,11 @@ "minimist": "^1.2.5" } }, + "moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -23976,6 +24274,27 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, "pirates": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", @@ -25832,6 +26151,15 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", @@ -26193,6 +26521,15 @@ "punycode": "^2.1.1" } }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", diff --git a/package.json b/package.json index 433f0ee..6744c78 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { - "name": "multiple-choice-quiz", + "name": "air-quality-quiz", "version": "0.1.0", "private": true, "dependencies": { "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", + "moment": "^2.30.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "5.0.0", @@ -34,5 +35,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "gh-pages": "^6.1.1" } } From a5b1837f12386e198f1cb97d9fb4db0deeb69dcf Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Wed, 18 Sep 2024 11:59:24 +0530 Subject: [PATCH 04/19] ADD 10 questions --- package.json | 3 ++ src/App.css | 9 +++-- src/App.js | 106 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 88 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 6744c78..74ecb58 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "homepage": "https://urbanemissions-info.github.io/airqualityquiz", "name": "air-quality-quiz", "version": "0.1.0", "private": true, @@ -14,6 +15,8 @@ }, "scripts": { "start": "react-scripts start", + "predeploy": "npm run build", + "deploy": "gh-pages -d build", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" diff --git a/src/App.css b/src/App.css index ce91dd4..7adb5e6 100644 --- a/src/App.css +++ b/src/App.css @@ -11,7 +11,7 @@ height: auto; /* Visual */ - background-color: gray; + background-color: rgb(23, 81,18); padding: 16px; border-radius: 16px; color: white; @@ -25,15 +25,16 @@ ul { li { margin-top: 8px; - background-color: darkgray; + background-color: rgba(255, 255, 255, 0.86); padding: 16px; - border: 3px solid white; + border: 3px solid rgb(0, 0, 0); border-radius: 20px; font-size: 20px; + color: black; } .question-text { - color: darkblue; + color: rgb(255, 255, 255); font-size: 24px; } diff --git a/src/App.js b/src/App.js index 5360555..2d1078a 100644 --- a/src/App.js +++ b/src/App.js @@ -9,48 +9,102 @@ function App() { const questions = [ { - text: "What is the capital of America?", + text: "What is Particulate Matter (PM) 2.5?", options: [ - { id: 0, text: "New York City", isCorrect: false }, - { id: 1, text: "Boston", isCorrect: false }, - { id: 2, text: "Santa Fe", isCorrect: false }, - { id: 3, text: "Washington DC", isCorrect: true }, + { id: 0, text: "All PM with radius less than 2.5 µm", isCorrect: false }, + { id: 1, text: "All PM with radius less than 2.5 mm", isCorrect: false }, + { id: 2, text: "All PM with diameter less than 2.5 µm", isCorrect: true }, + { id: 3, text: "All PM with diameter less than 2.5 mm", isCorrect: false }, ], }, + + { + text: "What is the main ingredient of the acid rain?", + options: [ + { id: 0, text: "Nitrogen dioxide (NO2)", isCorrect: false }, + { id: 1, text: "Sulphuric acid (H2SO4)", isCorrect: false }, + { id: 2, text: "Carbon Monoxide (CO)", isCorrect: false }, + { id: 3, text: "Sulphur dioxide (SO2)", isCorrect: true }, + ], + }, + + { + text: "Which of the following is not a necessary ingredient in the production of Tropospheric Ozone?", + options: [ + { id: 0, text: "Nitrogen Oxides (NOx)", isCorrect: false }, + { id: 1, text: "Carbon dioxide (CO2)", isCorrect: true }, + { id: 2, text: "Volatile Organic Compounds (VOCs)", isCorrect: false }, + { id: 3, text: "Sunlight", isCorrect: false }, + ], + }, + + { + text: "Which of the following pollutants can exist in both gas and aerosol phases?", + options: [ + { id: 0, text: "Particulate Matter (PM 2.5)", isCorrect: false }, + { id: 1, text: "Sulphur Dioxide (SO2)", isCorrect: true }, + { id: 2, text: "Ozone (O3)", isCorrect: false }, + { id: 3, text: "Carbon Monoxide (CO)", isCorrect: false }, + ], + }, + + { + text: "What is the unit of Air Quality Index (AQI) ?", + options: [ + { id: 0, text: "Parts per million (ppm)", isCorrect: false }, + { id: 1, text: "Micrograms (µg)", isCorrect: false }, + { id: 2, text: "Micrograms per unit volumen (µg/m3)", isCorrect: false }, + { id: 3, text: "None", isCorrect: true }, + ], + }, + { - text: "What year was the Constitution of America written?", + text: "What AQI value is considered 'Good' in India?", options: [ - { id: 0, text: "1787", isCorrect: true }, - { id: 1, text: "1776", isCorrect: false }, - { id: 2, text: "1774", isCorrect: false }, - { id: 3, text: "1826", isCorrect: false }, + { id: 0, text: "0-10", isCorrect: false }, + { id: 1, text: "50-100", isCorrect: false }, + { id: 2, text: "0-25", isCorrect: false }, + { id: 3, text: "0-50", isCorrect: true }, ], }, + { - text: "Who was the second president of the US?", + text: "What is the fundamental equation in building an Emissions Inventory?", options: [ - { id: 0, text: "John Adams", isCorrect: true }, - { id: 1, text: "Paul Revere", isCorrect: false }, - { id: 2, text: "Thomas Jefferson", isCorrect: false }, - { id: 3, text: "Benjamin Franklin", isCorrect: false }, + { id: 0, text: "Activity * Emissions Factor", isCorrect: true }, + { id: 1, text: "Activity / Emissions Factor", isCorrect: false }, + { id: 2, text: "Emissions Factor / Activity", isCorrect: false }, + { id: 3, text: "Activity + Emissions Factor", isCorrect: false }, ], }, + { - text: "What is the largest state in the US?", + text: "What is the representative area of an air quality monitor?", options: [ - { id: 0, text: "California", isCorrect: false }, - { id: 1, text: "Alaska", isCorrect: true }, - { id: 2, text: "Texas", isCorrect: false }, - { id: 3, text: "Montana", isCorrect: false }, + { id: 0, text: "10 sq.km", isCorrect: false }, + { id: 1, text: "4 sq.km", isCorrect: true }, + { id: 2, text: "2 sq.km", isCorrect: false }, + { id: 3, text: "20 sq.km", isCorrect: false }, ], }, + + { + text: "What does source apportionment study aim to determine?", + options: [ + { id: 0, text: "When the pollution occurred", isCorrect: false }, + { id: 1, text: "The geographical location of pollution", isCorrect: false }, + { id: 2, text: "What all activities contributed to the air pollution", isCorrect: true }, + { id: 3, text: "The composition of pollutants", isCorrect: false }, + ], + }, + { - text: "Which of the following countries DO NOT border the US?", + text: "What action is recommended when idling in traffic for more than 30 seconds??", options: [ - { id: 0, text: "Canada", isCorrect: false }, - { id: 1, text: "Russia", isCorrect: true }, - { id: 2, text: "Cuba", isCorrect: true }, - { id: 3, text: "Mexico", isCorrect: false }, + { id: 0, text: "Keep the engine running to avoid restart, which causes more pollution.", isCorrect: false }, + { id: 1, text: "Reduce air conditioning", isCorrect: false }, + { id: 2, text: "Turn off the engine", isCorrect: true }, + { id: 3, text: "Put the vehicle on a Neutral gear", isCorrect: false }, ], }, ]; @@ -81,7 +135,7 @@ function App() { return (
{/* 1. Header */} -

USA Quiz 🇺🇸

+

Air Quality Quiz

{/* 2. Current Score */}

Score: {score}

From f644058ba8052dd170c9306429b88a8fe8f1bf18 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Fri, 8 Nov 2024 22:01:06 +0530 Subject: [PATCH 05/19] ADD new questions --- README.md | 11 ++- src/App.js | 238 +++++++++++++++++++++++++++++++++++++++++++++------ src/quiz.csv | 3 + 3 files changed, 222 insertions(+), 30 deletions(-) create mode 100644 src/quiz.csv diff --git a/README.md b/README.md index 1360b48..1e5bb01 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ Screen Shot 2022-01-19 at 6 19 32 PM +## Deploy on GH pages +[Video tutorial](https://www.youtube.com/watch?v=7wzuievFjrk) + + +# ## Improvements ### Easy 🟢 @@ -11,8 +16,10 @@ - Store list of questions in a seperate file - `questions.txt` - Change list of questions to something else -### Medium 🟡 -- After game reset, **scramble order of questions** randomly +### Medium 🟡 + +- [x] After game reset, **scramble order of questions** randomly + - After clicking an option, pause for 2 sec, show correct answer, then move onto next one - At the end show list of wrong questions with the correct answers - Create different ReactJS components diff --git a/src/App.js b/src/App.js index 2d1078a..b910bea 100644 --- a/src/App.js +++ b/src/App.js @@ -6,9 +6,11 @@ function App() { const [showResults, setShowResults] = useState(false); const [currentQuestion, setCurrentQuestion] = useState(0); const [score, setScore] = useState(0); + const [begin_shuffle, setBeginShuffle] = useState(true); const questions = [ { + id: 1, text: "What is Particulate Matter (PM) 2.5?", options: [ { id: 0, text: "All PM with radius less than 2.5 µm", isCorrect: false }, @@ -19,17 +21,19 @@ function App() { }, { + id: 2, text: "What is the main ingredient of the acid rain?", options: [ { id: 0, text: "Nitrogen dioxide (NO2)", isCorrect: false }, - { id: 1, text: "Sulphuric acid (H2SO4)", isCorrect: false }, + { id: 1, text: "Carbon Dioxide (CO2)", isCorrect: false }, { id: 2, text: "Carbon Monoxide (CO)", isCorrect: false }, { id: 3, text: "Sulphur dioxide (SO2)", isCorrect: true }, ], }, { - text: "Which of the following is not a necessary ingredient in the production of Tropospheric Ozone?", + id: 3, + text: "Which of the following is not a necessary ingredient in the production of 'Global' Tropospheric Ozone?", options: [ { id: 0, text: "Nitrogen Oxides (NOx)", isCorrect: false }, { id: 1, text: "Carbon dioxide (CO2)", isCorrect: true }, @@ -39,7 +43,8 @@ function App() { }, { - text: "Which of the following pollutants can exist in both gas and aerosol phases?", + id: 4, + text: "Which of the following gaseous pollutants can directly contribute to aerosol concentrations?", options: [ { id: 0, text: "Particulate Matter (PM 2.5)", isCorrect: false }, { id: 1, text: "Sulphur Dioxide (SO2)", isCorrect: true }, @@ -49,26 +54,18 @@ function App() { }, { + id: 5, text: "What is the unit of Air Quality Index (AQI) ?", options: [ { id: 0, text: "Parts per million (ppm)", isCorrect: false }, { id: 1, text: "Micrograms (µg)", isCorrect: false }, - { id: 2, text: "Micrograms per unit volumen (µg/m3)", isCorrect: false }, + { id: 2, text: "Micrograms per unit volume (µg/m3)", isCorrect: false }, { id: 3, text: "None", isCorrect: true }, ], }, { - text: "What AQI value is considered 'Good' in India?", - options: [ - { id: 0, text: "0-10", isCorrect: false }, - { id: 1, text: "50-100", isCorrect: false }, - { id: 2, text: "0-25", isCorrect: false }, - { id: 3, text: "0-50", isCorrect: true }, - ], - }, - - { + id: 6, text: "What is the fundamental equation in building an Emissions Inventory?", options: [ { id: 0, text: "Activity * Emissions Factor", isCorrect: true }, @@ -79,38 +76,210 @@ function App() { }, { + id: 7, text: "What is the representative area of an air quality monitor?", options: [ - { id: 0, text: "10 sq.km", isCorrect: false }, - { id: 1, text: "4 sq.km", isCorrect: true }, - { id: 2, text: "2 sq.km", isCorrect: false }, - { id: 3, text: "20 sq.km", isCorrect: false }, + { id: 0, text: "Area with a radius of 1 km", isCorrect: false }, + { id: 1, text: "Area with a radius of 2 km", isCorrect: true }, + { id: 2, text: "Area with a radius of 4 km", isCorrect: false }, + { id: 3, text: "Area with a radius of 10 km", isCorrect: false }, ], }, { + id: 8, text: "What does source apportionment study aim to determine?", options: [ { id: 0, text: "When the pollution occurred", isCorrect: false }, { id: 1, text: "The geographical location of pollution", isCorrect: false }, { id: 2, text: "What all activities contributed to the air pollution", isCorrect: true }, - { id: 3, text: "The composition of pollutants", isCorrect: false }, + { id: 3, text: "The chemical composition of pollutants", isCorrect: false }, ], }, { - text: "What action is recommended when idling in traffic for more than 30 seconds??", + id: 9, + text: "What action is recommended when idling in traffic for more than 30 seconds?", options: [ - { id: 0, text: "Keep the engine running to avoid restart, which causes more pollution.", isCorrect: false }, + { id: 0, text: "Keep the engine running to avoid restart, which causes more pollution", isCorrect: false }, { id: 1, text: "Reduce air conditioning", isCorrect: false }, { id: 2, text: "Turn off the engine", isCorrect: true }, { id: 3, text: "Put the vehicle on a Neutral gear", isCorrect: false }, ], }, + + { + id: 10, + text: "Main pollutant of volcanic eruptions is ?", + options: [ + { id: 0, text: "Nitrogen Dioxide (NO2)", isCorrect: false }, + { id: 1, text: "Sulphur Dioxide (SO2)", isCorrect: true }, + { id: 2, text: "Ozone (O3)", isCorrect: false }, + { id: 3, text: "Volatile Organic Compounds (VOCs)", isCorrect: false }, + ], + }, + + { + id: 11, + text: "Pollutant that gets attached to blood (haemoglobin) and leads to premature mortality?", + options: [ + { id: 0, text: "Nitrogen Dioxide (NO2)", isCorrect: false }, + { id: 1, text: "Benzene (C6H6)", isCorrect: false }, + { id: 2, text: "Ozone (O3)", isCorrect: false }, + { id: 3, text: "Carbon Monoxide (CO)", isCorrect: true }, + ], + }, + + { + id: 12, + text: "Lightning produces this in large quantities?", + options: [ + { id: 0, text: "Nitrogen oxides (NOx)", isCorrect: true }, + { id: 1, text: "Halogen compounds", isCorrect: false }, + { id: 2, text: "Ozone (O3)", isCorrect: false }, + { id: 3, text: "Volatile Organic Compounds (VOCs)", isCorrect: false }, + ], + }, + + { + id: 13, + text: "Pollutant concentrations most used for calculating health impacts?", + options: [ + { id: 0, text: "Sulphur Dioxide (SO2) and Nitrogen Dioxide (NO2)", isCorrect: false }, + { id: 1, text: "Particulate Matter 2.5 (PM2.5) and Ozone (O3)", isCorrect: true }, + { id: 2, text: "Nitrogen Dioxide (NO2) and Particulate Matter 2.5 (PM2.5)", isCorrect: false }, + { id: 3, text: "NO2, Benzene (C6H6) and Ozone (O3)", isCorrect: false }, + ], + }, + + { + id: 14, + text: "Good ozone protecting us from UV radiation resides in?", + options: [ + { id: 0, text: "Troposphere", isCorrect: false }, + { id: 1, text: "Heliosphere", isCorrect: false }, + { id: 2, text: "Mesosphere", isCorrect: false }, + { id: 3, text: "Stratosphere", isCorrect: true }, + ], + }, + + { + id: 15, + text: "Chronic Obstructive Pulmonary Disorder (COPD) is associated with ?", + options: [ + { id: 0, text: "Heart", isCorrect: false }, + { id: 1, text: "Lungs", isCorrect: true }, + { id: 2, text: "Eyes", isCorrect: false }, + { id: 3, text: "Veins", isCorrect: false }, + ], + }, + + { + id: 16, + text: "Ultrafine particulates are Particulate Matter (PM) less than?", + options: [ + { id: 0, text: "10μm", isCorrect: false }, + { id: 1, text: "1μm", isCorrect: false }, + { id: 2, text: "0.1μm", isCorrect: true }, + { id: 3, text: "0.01μm", isCorrect: false }, + ], + }, + + { + id: 17, + text: "Ambient air pollution sources are?", + options: [ + { id: 0, text: "Everything that burns", isCorrect: true }, + { id: 1, text: "mostly power plants", isCorrect: false }, + { id: 2, text: "mostly transport", isCorrect: false }, + { id: 3, text: "waste burning", isCorrect: false }, + ], + }, + + { + id: 18, + text: "Which of the following statements is TRUE", + options: [ + { id: 0, text: "Air quality index provides more information than air quality data ", isCorrect: false }, + { id: 1, text: "Air quality index information is preferred for regulatory purposes", isCorrect: false }, + { id: 2, text: "Air quality index information is preferred for public awareness", isCorrect: true }, + { id: 3, text: "Air quality index value is same as PM2.5 concentration value", isCorrect: false }, + ], + }, + + { + id: 19, + text: "Which of the following statements is TRUE", + options: [ + { id: 0, text: "Discussions on pollution is same as discussions on concentrations", isCorrect: true }, + { id: 1, text: "Discussions on emissions is same as discussions on pollution", isCorrect: false }, + { id: 2, text: "Discussions on emissions is same as discussions on air quality index", isCorrect: false }, + { id: 3, text: "Discussions on pollutants is same as discussions on air quality index", isCorrect: false }, + ], + }, + + { + id: 20, + text: "Which of the following statements is TRUE", + options: [ + { id: 0, text: "Two monitoring stations is a good sample to represent a city", isCorrect: false }, + { id: 1, text: "Low-cost sensors are same as regulatory-grade monitoring systems", isCorrect: false }, + { id: 2, text: "Regulatory-grade systems do not require any maintenance", isCorrect: false }, + { id: 3, text: "All monitoring systems require regular maintenance and calibration", isCorrect: true }, + ], + }, + + { + id: 21, + text: "Which of the following statements is TRUE", + options: [ + { id: 0, text: "Satellite observations can directly replace ground measurements", isCorrect: false }, + { id: 1, text: "Satellite observations are superior to ground measurements", isCorrect: false }, + { id: 2, text: "Satellite observations are a proxy to ground measurements", isCorrect: true }, + { id: 3, text: "Satellite observations are a proxy to emission inventories", isCorrect: false }, + ], + }, + + { + id: 22, + text: "What is TRUE about Aerosol Optical Depth (AOD)?", + options: [ + { id: 0, text: "AOD is a proxy of PM2.5 or PM10 concentrations in the air", isCorrect: true }, + { id: 1, text: "AOD from satellites is same as PM2.5/PM10 from ground measurements", isCorrect: false }, + { id: 2, text: "AOD can be used for policy discussions globally", isCorrect: false }, + { id: 3, text: "AOD from a single satellite pass per day is enough to deduce pollution", isCorrect: false }, + ], + }, + + { + id: 23, + text: "Best way to reduce pollution", + options: [ + { id: 0, text: "Increase the number of monitoring stations", isCorrect: false }, + { id: 1, text: "Control emissions at all the sources", isCorrect: true }, + { id: 2, text: "Use vacuum cleaners to suck pollution in the air", isCorrect: false }, + { id: 3, text: "All the above", isCorrect: false }, + ], + }, + + { + id: 24, + text: "Which of the following statements is TRUE about source apportionment techniques", + options: [ + { id: 0, text: "Sampling-chemical-analysis approach is always preferred", isCorrect: false }, + { id: 1, text: "Sampling-chemical-analysis approach is the best", isCorrect: false }, + { id: 2, text: "Emissions-pollution-modelling approach is always preferred", isCorrect: false }, + { id: 3, text: "Emissions-pollution-modelling approach has better spatial and temporal coverage ", isCorrect: true }, + ], + }, + + ]; - // Helper Functions + +const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions); + // Helper Functions /* A possible answer was clicked */ const optionClicked = (isCorrect) => { // Increment the score @@ -118,7 +287,7 @@ function App() { setScore(score + 1); } - if (currentQuestion + 1 < questions.length) { + if (currentQuestion + 1 < questionsWithRandomIds.length) { setCurrentQuestion(currentQuestion + 1); } else { setShowResults(true); @@ -130,8 +299,20 @@ function App() { setScore(0); setCurrentQuestion(0); setShowResults(false); + // Shuffle the questions array once (using Fisher-Yates algorithm) + for (let i = questions.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questions[i], questions[j]] = [questions[j], questions[i]]; + } + setQuestionsWithRandomIds(questions); }; + if (begin_shuffle) { + restartGame(); + setBeginShuffle(false); + + } + return (
{/* 1. Header */} @@ -146,23 +327,24 @@ function App() {

Final Results

- {score} out of {questions.length} correct - ( - {(score / questions.length) * 100}%) + {score} out of {questionsWithRandomIds.length} correct - ( + {(score / questionsWithRandomIds.length) * 100}%)

) : ( /* 5. Question Card */ +
{/* Current Question */}

- Question: {currentQuestion + 1} out of {questions.length} + Question: {currentQuestion + 1} out of {questionsWithRandomIds.length}

-

{questions[currentQuestion].text}

+

{questionsWithRandomIds[currentQuestion].text}

{/* List of possible answers */}
    - {questions[currentQuestion].options.map((option) => { + {questionsWithRandomIds[currentQuestion].options.map((option) => { return (
  • Date: Fri, 8 Nov 2024 22:16:26 +0530 Subject: [PATCH 06/19] UPDATE acid rain Ques --- src/App.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index b910bea..bc5b9b0 100644 --- a/src/App.js +++ b/src/App.js @@ -22,12 +22,12 @@ function App() { { id: 2, - text: "What is the main ingredient of the acid rain?", + text: "Which gas is responsible for acid rain?", options: [ - { id: 0, text: "Nitrogen dioxide (NO2)", isCorrect: false }, + { id: 0, text: "Ozone (O3)", isCorrect: false }, { id: 1, text: "Carbon Dioxide (CO2)", isCorrect: false }, { id: 2, text: "Carbon Monoxide (CO)", isCorrect: false }, - { id: 3, text: "Sulphur dioxide (SO2)", isCorrect: true }, + { id: 3, text: "Sulphur Dioxide (SO2)", isCorrect: true }, ], }, From 3b050db93e674af2b29a0092de935e6dcd2305e2 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Thu, 6 Feb 2025 11:31:49 +0530 Subject: [PATCH 07/19] UPDATE quiz from csv --- README.md | 11 +- image.png | Bin 0 -> 79407 bytes src/App.js | 360 ++++++++++++--------------------------------------- src/quiz.csv | 26 +++- 4 files changed, 112 insertions(+), 285 deletions(-) create mode 100644 image.png diff --git a/README.md b/README.md index 1e5bb01..4db2b4d 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,19 @@ # Multiple Choice Quiz (ReactJS) -Screen Shot 2022-01-19 at 6 19 13 PM +Answer the quiz on: [AirQualityQuiz](https://urbanemissions-info.github.io/airqualityquiz/) -Screen Shot 2022-01-19 at 6 19 32 PM +![alt text](image.png) ## Deploy on GH pages [Video tutorial](https://www.youtube.com/watch?v=7wzuievFjrk) -# ## Improvements ### Easy 🟢 -- **Visual updates**: Animations, Sounds, Font Styling, Background, Colors, etc. -- Store list of questions in a seperate file - `questions.txt` -- Change list of questions to something else +- [ ]**Visual updates**: Animations, Sounds, Font Styling, Background, Colors, etc. +- [x] Store list of questions in a seperate file - `questions.csv` +- [ ] Place `questions.csv` on Google Sheets and serve it from there. ### Medium 🟡 diff --git a/image.png b/image.png new file mode 100644 index 0000000000000000000000000000000000000000..a970ab1f8336b5965816198f143ce58ea5b4efd8 GIT binary patch literal 79407 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 literal 0 HcmV?d00001 diff --git a/src/App.js b/src/App.js index bc5b9b0..f22d87b 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,6 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import "./App.css"; +import csv from "./quiz.csv"; function App() { // Properties @@ -8,276 +9,79 @@ function App() { const [score, setScore] = useState(0); const [begin_shuffle, setBeginShuffle] = useState(true); - const questions = [ - { - id: 1, - text: "What is Particulate Matter (PM) 2.5?", - options: [ - { id: 0, text: "All PM with radius less than 2.5 µm", isCorrect: false }, - { id: 1, text: "All PM with radius less than 2.5 mm", isCorrect: false }, - { id: 2, text: "All PM with diameter less than 2.5 µm", isCorrect: true }, - { id: 3, text: "All PM with diameter less than 2.5 mm", isCorrect: false }, - ], - }, - - { - id: 2, - text: "Which gas is responsible for acid rain?", - options: [ - { id: 0, text: "Ozone (O3)", isCorrect: false }, - { id: 1, text: "Carbon Dioxide (CO2)", isCorrect: false }, - { id: 2, text: "Carbon Monoxide (CO)", isCorrect: false }, - { id: 3, text: "Sulphur Dioxide (SO2)", isCorrect: true }, - ], - }, - - { - id: 3, - text: "Which of the following is not a necessary ingredient in the production of 'Global' Tropospheric Ozone?", - options: [ - { id: 0, text: "Nitrogen Oxides (NOx)", isCorrect: false }, - { id: 1, text: "Carbon dioxide (CO2)", isCorrect: true }, - { id: 2, text: "Volatile Organic Compounds (VOCs)", isCorrect: false }, - { id: 3, text: "Sunlight", isCorrect: false }, - ], - }, - - { - id: 4, - text: "Which of the following gaseous pollutants can directly contribute to aerosol concentrations?", - options: [ - { id: 0, text: "Particulate Matter (PM 2.5)", isCorrect: false }, - { id: 1, text: "Sulphur Dioxide (SO2)", isCorrect: true }, - { id: 2, text: "Ozone (O3)", isCorrect: false }, - { id: 3, text: "Carbon Monoxide (CO)", isCorrect: false }, - ], - }, - - { - id: 5, - text: "What is the unit of Air Quality Index (AQI) ?", - options: [ - { id: 0, text: "Parts per million (ppm)", isCorrect: false }, - { id: 1, text: "Micrograms (µg)", isCorrect: false }, - { id: 2, text: "Micrograms per unit volume (µg/m3)", isCorrect: false }, - { id: 3, text: "None", isCorrect: true }, - ], - }, - - { - id: 6, - text: "What is the fundamental equation in building an Emissions Inventory?", - options: [ - { id: 0, text: "Activity * Emissions Factor", isCorrect: true }, - { id: 1, text: "Activity / Emissions Factor", isCorrect: false }, - { id: 2, text: "Emissions Factor / Activity", isCorrect: false }, - { id: 3, text: "Activity + Emissions Factor", isCorrect: false }, - ], - }, - - { - id: 7, - text: "What is the representative area of an air quality monitor?", - options: [ - { id: 0, text: "Area with a radius of 1 km", isCorrect: false }, - { id: 1, text: "Area with a radius of 2 km", isCorrect: true }, - { id: 2, text: "Area with a radius of 4 km", isCorrect: false }, - { id: 3, text: "Area with a radius of 10 km", isCorrect: false }, - ], - }, - - { - id: 8, - text: "What does source apportionment study aim to determine?", - options: [ - { id: 0, text: "When the pollution occurred", isCorrect: false }, - { id: 1, text: "The geographical location of pollution", isCorrect: false }, - { id: 2, text: "What all activities contributed to the air pollution", isCorrect: true }, - { id: 3, text: "The chemical composition of pollutants", isCorrect: false }, - ], - }, - - { - id: 9, - text: "What action is recommended when idling in traffic for more than 30 seconds?", - options: [ - { id: 0, text: "Keep the engine running to avoid restart, which causes more pollution", isCorrect: false }, - { id: 1, text: "Reduce air conditioning", isCorrect: false }, - { id: 2, text: "Turn off the engine", isCorrect: true }, - { id: 3, text: "Put the vehicle on a Neutral gear", isCorrect: false }, - ], - }, - - { - id: 10, - text: "Main pollutant of volcanic eruptions is ?", - options: [ - { id: 0, text: "Nitrogen Dioxide (NO2)", isCorrect: false }, - { id: 1, text: "Sulphur Dioxide (SO2)", isCorrect: true }, - { id: 2, text: "Ozone (O3)", isCorrect: false }, - { id: 3, text: "Volatile Organic Compounds (VOCs)", isCorrect: false }, - ], - }, - - { - id: 11, - text: "Pollutant that gets attached to blood (haemoglobin) and leads to premature mortality?", - options: [ - { id: 0, text: "Nitrogen Dioxide (NO2)", isCorrect: false }, - { id: 1, text: "Benzene (C6H6)", isCorrect: false }, - { id: 2, text: "Ozone (O3)", isCorrect: false }, - { id: 3, text: "Carbon Monoxide (CO)", isCorrect: true }, - ], - }, - - { - id: 12, - text: "Lightning produces this in large quantities?", - options: [ - { id: 0, text: "Nitrogen oxides (NOx)", isCorrect: true }, - { id: 1, text: "Halogen compounds", isCorrect: false }, - { id: 2, text: "Ozone (O3)", isCorrect: false }, - { id: 3, text: "Volatile Organic Compounds (VOCs)", isCorrect: false }, - ], - }, - - { - id: 13, - text: "Pollutant concentrations most used for calculating health impacts?", - options: [ - { id: 0, text: "Sulphur Dioxide (SO2) and Nitrogen Dioxide (NO2)", isCorrect: false }, - { id: 1, text: "Particulate Matter 2.5 (PM2.5) and Ozone (O3)", isCorrect: true }, - { id: 2, text: "Nitrogen Dioxide (NO2) and Particulate Matter 2.5 (PM2.5)", isCorrect: false }, - { id: 3, text: "NO2, Benzene (C6H6) and Ozone (O3)", isCorrect: false }, - ], - }, - - { - id: 14, - text: "Good ozone protecting us from UV radiation resides in?", - options: [ - { id: 0, text: "Troposphere", isCorrect: false }, - { id: 1, text: "Heliosphere", isCorrect: false }, - { id: 2, text: "Mesosphere", isCorrect: false }, - { id: 3, text: "Stratosphere", isCorrect: true }, - ], - }, - - { - id: 15, - text: "Chronic Obstructive Pulmonary Disorder (COPD) is associated with ?", - options: [ - { id: 0, text: "Heart", isCorrect: false }, - { id: 1, text: "Lungs", isCorrect: true }, - { id: 2, text: "Eyes", isCorrect: false }, - { id: 3, text: "Veins", isCorrect: false }, - ], - }, - - { - id: 16, - text: "Ultrafine particulates are Particulate Matter (PM) less than?", - options: [ - { id: 0, text: "10μm", isCorrect: false }, - { id: 1, text: "1μm", isCorrect: false }, - { id: 2, text: "0.1μm", isCorrect: true }, - { id: 3, text: "0.01μm", isCorrect: false }, - ], - }, - - { - id: 17, - text: "Ambient air pollution sources are?", - options: [ - { id: 0, text: "Everything that burns", isCorrect: true }, - { id: 1, text: "mostly power plants", isCorrect: false }, - { id: 2, text: "mostly transport", isCorrect: false }, - { id: 3, text: "waste burning", isCorrect: false }, - ], - }, - - { - id: 18, - text: "Which of the following statements is TRUE", - options: [ - { id: 0, text: "Air quality index provides more information than air quality data ", isCorrect: false }, - { id: 1, text: "Air quality index information is preferred for regulatory purposes", isCorrect: false }, - { id: 2, text: "Air quality index information is preferred for public awareness", isCorrect: true }, - { id: 3, text: "Air quality index value is same as PM2.5 concentration value", isCorrect: false }, - ], - }, - - { - id: 19, - text: "Which of the following statements is TRUE", - options: [ - { id: 0, text: "Discussions on pollution is same as discussions on concentrations", isCorrect: true }, - { id: 1, text: "Discussions on emissions is same as discussions on pollution", isCorrect: false }, - { id: 2, text: "Discussions on emissions is same as discussions on air quality index", isCorrect: false }, - { id: 3, text: "Discussions on pollutants is same as discussions on air quality index", isCorrect: false }, - ], - }, - - { - id: 20, - text: "Which of the following statements is TRUE", - options: [ - { id: 0, text: "Two monitoring stations is a good sample to represent a city", isCorrect: false }, - { id: 1, text: "Low-cost sensors are same as regulatory-grade monitoring systems", isCorrect: false }, - { id: 2, text: "Regulatory-grade systems do not require any maintenance", isCorrect: false }, - { id: 3, text: "All monitoring systems require regular maintenance and calibration", isCorrect: true }, - ], - }, - - { - id: 21, - text: "Which of the following statements is TRUE", - options: [ - { id: 0, text: "Satellite observations can directly replace ground measurements", isCorrect: false }, - { id: 1, text: "Satellite observations are superior to ground measurements", isCorrect: false }, - { id: 2, text: "Satellite observations are a proxy to ground measurements", isCorrect: true }, - { id: 3, text: "Satellite observations are a proxy to emission inventories", isCorrect: false }, - ], - }, - - { - id: 22, - text: "What is TRUE about Aerosol Optical Depth (AOD)?", - options: [ - { id: 0, text: "AOD is a proxy of PM2.5 or PM10 concentrations in the air", isCorrect: true }, - { id: 1, text: "AOD from satellites is same as PM2.5/PM10 from ground measurements", isCorrect: false }, - { id: 2, text: "AOD can be used for policy discussions globally", isCorrect: false }, - { id: 3, text: "AOD from a single satellite pass per day is enough to deduce pollution", isCorrect: false }, - ], - }, - - { - id: 23, - text: "Best way to reduce pollution", - options: [ - { id: 0, text: "Increase the number of monitoring stations", isCorrect: false }, - { id: 1, text: "Control emissions at all the sources", isCorrect: true }, - { id: 2, text: "Use vacuum cleaners to suck pollution in the air", isCorrect: false }, - { id: 3, text: "All the above", isCorrect: false }, - ], - }, + function csvToQuestions(csvString) { + const lines = csvString.trim().split("\n"); + const headers = lines[0].split(","); + const questions = []; + + for (let i = 1; i < lines.length; i++) { + const values = lines[i].split(","); + const questionId = parseInt(values[0], 10); + const questionText = values[1]; + const obj = { a: 2, b: 3, c: 4, d: 5 }; + const correctAnswerIndex = obj[values[6].replace("\r", "")]; // Assuming 'correct_ans' is at index 6 + + const options = []; + for (let j = 2; j <= 5; j++) { + options.push({ + id: j - 2, + text: values[j], + isCorrect: j === correctAnswerIndex, + }); + } + + questions.push({ + id: questionId, + text: questionText, + options: options, + }); + } - { - id: 24, - text: "Which of the following statements is TRUE about source apportionment techniques", - options: [ - { id: 0, text: "Sampling-chemical-analysis approach is always preferred", isCorrect: false }, - { id: 1, text: "Sampling-chemical-analysis approach is the best", isCorrect: false }, - { id: 2, text: "Emissions-pollution-modelling approach is always preferred", isCorrect: false }, - { id: 3, text: "Emissions-pollution-modelling approach has better spatial and temporal coverage ", isCorrect: true }, - ], - }, + // Shuffle the questions array once (using Fisher-Yates algorithm) + for (let i = questions.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questions[i], questions[j]] = [questions[j], questions[i]]; + } - - ]; + return questions; + } - -const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions); + // Example CSV input (as a string) + // const csvData = `question_id,text,option_0,option_1,option_2,option_3,correct_ans + // 1,What is Particulate Matter (PM) 2.5?,All PM with radius less than 2.5 µm,All PM with radius less than 2.5 mm,All PM with diameter less than 2.5 µm,All PM with diameter less than 2.5 mm,2 + // 2,Which gas is responsible for acid rain?,Ozone (O3),Carbon Dioxide (CO2),Carbon Monoxide (CO),Sulphur Dioxide (SO2),3`; + // Read the CSV file + + useEffect(() => { + loadquotesCSV(); + }, []); + + const [questions, setQuestions] = useState([]); + const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions); + + function loadquotesCSV() { + fetch(csv, { + headers: { "content-type": "text/csv;charset=UTF-8" }, + method: "GET", + }) + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + // console.log("CSV Data:", response.text()); + return response.text(); + }) + .then((csvData) => { + // console.log("CSV Data:", csvData); + const data = csvToQuestions(csvData); + setQuestions(data); + setQuestionsWithRandomIds(data); + // You can now process the CSV data as needed + }) + .catch((error) => { + console.error("Error fetching CSV file:", error); + }); + } // Helper Functions /* A possible answer was clicked */ @@ -300,17 +104,16 @@ const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions); setCurrentQuestion(0); setShowResults(false); // Shuffle the questions array once (using Fisher-Yates algorithm) - for (let i = questions.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [questions[i], questions[j]] = [questions[j], questions[i]]; - } + for (let i = questions.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questions[i], questions[j]] = [questions[j], questions[i]]; + } setQuestionsWithRandomIds(questions); }; if (begin_shuffle) { restartGame(); setBeginShuffle(false); - } return ( @@ -334,17 +137,20 @@ const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions);
) : ( /* 5. Question Card */ - +
{/* Current Question */}

- Question: {currentQuestion + 1} out of {questionsWithRandomIds.length} + Question: {currentQuestion + 1} out of{" "} + {questionsWithRandomIds.length}

-

{questionsWithRandomIds[currentQuestion].text}

+

+ {questionsWithRandomIds[currentQuestion]?.text} +

{/* List of possible answers */}
    - {questionsWithRandomIds[currentQuestion].options.map((option) => { + {questionsWithRandomIds[currentQuestion]?.options.map((option) => { return (
  • Date: Fri, 7 Feb 2025 10:20:49 +0530 Subject: [PATCH 08/19] UPDATE quiz -GoogleSheets serve --- README.md | 2 +- src/App.js | 92 ++++++++++++++++++++++++++---------------------------- 2 files changed, 45 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 4db2b4d..b2b9bf5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Answer the quiz on: [AirQualityQuiz](https://urbanemissions-info.github.io/airqu ### Easy 🟢 - [ ]**Visual updates**: Animations, Sounds, Font Styling, Background, Colors, etc. - [x] Store list of questions in a seperate file - `questions.csv` -- [ ] Place `questions.csv` on Google Sheets and serve it from there. +- [x] Place `questions.csv` on Google Sheets and serve it from there // add 'Loading quiz' for lag. ### Medium 🟡 diff --git a/src/App.js b/src/App.js index f22d87b..b31147b 100644 --- a/src/App.js +++ b/src/App.js @@ -7,7 +7,6 @@ function App() { const [showResults, setShowResults] = useState(false); const [currentQuestion, setCurrentQuestion] = useState(0); const [score, setScore] = useState(0); - const [begin_shuffle, setBeginShuffle] = useState(true); function csvToQuestions(csvString) { const lines = csvString.trim().split("\n"); @@ -59,8 +58,9 @@ function App() { const [questions, setQuestions] = useState([]); const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions); + const GOOGLE_SHEET_CSV_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=0&single=true&output=csv"; function loadquotesCSV() { - fetch(csv, { + fetch(GOOGLE_SHEET_CSV_URL, { headers: { "content-type": "text/csv;charset=UTF-8" }, method: "GET", }) @@ -111,57 +111,53 @@ function App() { setQuestionsWithRandomIds(questions); }; - if (begin_shuffle) { - restartGame(); - setBeginShuffle(false); - } + // if (begin_shuffle) { + // restartGame(); + // setBeginShuffle(false); + // } return (
    - {/* 1. Header */} + {/* 1. Header */}

    Air Quality Quiz

    - - {/* 2. Current Score */} -

    Score: {score}

    - - {/* 3. Show results or show the question game */} - {showResults ? ( - /* 4. Final Results */ -
    -

    Final Results

    -

    - {score} out of {questionsWithRandomIds.length} correct - ( - {(score / questionsWithRandomIds.length) * 100}%) -

    - -
    + + {/* Show loading message if questions are not yet loaded */} + {questions.length === 0 ? ( +

    Loading quiz...

    ) : ( - /* 5. Question Card */ - -
    - {/* Current Question */} -

    - Question: {currentQuestion + 1} out of{" "} - {questionsWithRandomIds.length} -

    -

    - {questionsWithRandomIds[currentQuestion]?.text} -

    - - {/* List of possible answers */} -
      - {questionsWithRandomIds[currentQuestion]?.options.map((option) => { - return ( -
    • optionClicked(option.isCorrect)} - > - {option.text} -
    • - ); - })} -
    -
    + <> + {/* 2. Current Score */} +

    Score: {score}

    + + {/* 3. Show results or show the question game */} + {showResults ? ( + /* 4. Final Results */ +
    +

    Final Results

    +

    + {score} out of {questionsWithRandomIds.length} correct - ( + {(score / questionsWithRandomIds.length) * 100}%) +

    + +
    + ) : ( + /* 5. Question Card */ +
    +

    + Question: {currentQuestion + 1} out of {questionsWithRandomIds.length} +

    +

    {questionsWithRandomIds[currentQuestion]?.text}

    + +
      + {questionsWithRandomIds[currentQuestion]?.options.map((option) => ( +
    • optionClicked(option.isCorrect)}> + {option.text} +
    • + ))} +
    +
    + )} + )}
    ); From 1b139bbe0c8321f37af6199eb389fa2ff953a5d0 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Sat, 8 Feb 2025 21:13:12 +0530 Subject: [PATCH 09/19] UPDATE -- multiple quiz URLS and tracker --- Quiz-001-Basic-for-Webversion.txt | 167 ++++++++++++++++++++++++++++ README.md | 12 +- package-lock.json | 64 +++++++++++ package.json | 1 + public/index.html | 3 +- src/App.js | 175 +++++++++++++++--------------- src/Appp.js | 0 7 files changed, 326 insertions(+), 96 deletions(-) create mode 100644 Quiz-001-Basic-for-Webversion.txt create mode 100644 src/Appp.js diff --git a/Quiz-001-Basic-for-Webversion.txt b/Quiz-001-Basic-for-Webversion.txt new file mode 100644 index 0000000..5259afd --- /dev/null +++ b/Quiz-001-Basic-for-Webversion.txt @@ -0,0 +1,167 @@ +What is particulate matter (PM) 2.5? +-- all PM under 2.5 mm radius +-- all PM under 2.5 micro-m radius +-- all PM under 2.5 mm diameter +-- all PM under 2.5 micro-m diameter + +AQI standards for +-- Air Quality Information +-- Air Quality Index +-- Air Quality Inventory +-- Air Quality Impacts + +PM2.5 is a +-- Primary pollutant +-- Secondary pollutant +-- Has both primary and secondary pollutants +-- Don't know + +Global climate Change is same as air pollution we experience in the city's +-- True +-- False +-- Sometimes +-- Don't know + +SMOG is +-- Suspended-PM and Fog +-- Smoke and Fog +-- Sulfur Dioxide and Fog +-- Soot and Fog + +Air quality is same as Air Quality Index (AQI) +-- True +-- False +-- Sometimes +-- Don't know + +Which pollutant is responsible for acid rain? +-- Ammonia +-- Carbon Monoxide +-- Ozone +-- Sulfur Dioxide + +Which of the following is not a necessary ingredient in the production of Ozone? +-- Carbon Dioxide +-- Nitrogen Oxides +-- Sunlight +-- Volatile Organic Compounds + +What is the unit of Air Quality Index (AQI)? +-- micro-gm/m3 +-- none +-- percentage (%) +-- tons/year + +What is a typical unit of emissions? +-- kg/day +-- tons/year +-- It depends +-- A & B + +What is a typical unit of pollution? +-- micro-gm/m3 +-- tons/year +-- It depends +-- A & B + +Pollutant that gets attached to blood (haemoglobin) and leads to premature mortality? +-- Carbon Dioxide +-- Carbon Monoxide +-- Nitrogen Dioxide +-- Ozone + +Good ozone protecting us from UV radiation, resides in? +-- Biosphere +-- Mesosphere +-- Stratosphere +-- Troposphere + +Bad ozone that we don't want to breathe, resides in? +-- Biosphere +-- Mesosphere +-- Stratosphere +-- Troposphere + +Chronic Obstructive Pulmonary Disorder (COPD) due to air pollution is associated with this organ +-- Blood +-- Heart +-- Lungs +-- Eyes + +Cardiac arrests due to air pollution is associated with this organ +-- Blood +-- Heart +-- Lungs +-- Eyes + +Ambient air pollution sources are? +-- Everything that burns +-- It depends on when and where +-- Mostly biomass burning +-- Mostly transport + +Air quality index (AQI) provides more information than air quality data +-- True +-- False +-- Sometimes +-- Don't know + +Air quality index information is preferred for regulatory purposes +-- True +-- False +-- Sometimes +-- Don't know + +Air quality index information is preferred for public awareness +-- True +-- False +-- Sometimes +-- Don't know + +Air quality index value is same as PM2.5 concentration value +-- True +-- False +-- Sometimes +-- Don't know + +Discussions on emissions is same as discussions on pollution +-- True +-- False +-- Sometimes +-- Don't know + +Regulatory-grade monitoring systems do not require any maintenance +-- True +-- False +-- Sometimes +-- Don't know + +All monitoring systems require regular maintenance and calibration +-- True +-- False +-- Sometimes +-- Don't know + +Satellite observations are superior to ground measurements +-- True +-- False +-- Sometimes +-- Don't know + +Best way to reduce pollution +-- Increase the number of monitoring stations +-- Control emissions at all the sources +-- Use vacuum cleaner like equipment to suck pollution in the air +-- All the above + +Air Quality Index (AQI) methodology is same for all the countries +-- True +-- False +-- Sometimes +-- Don't know + +Monitoring data is enough to know everything about air pollution +-- True +-- False +-- Sometimes +-- Don't know \ No newline at end of file diff --git a/README.md b/README.md index b2b9bf5..7df1e22 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,16 @@ Answer the quiz on: [AirQualityQuiz](https://urbanemissions-info.github.io/airqu - [ ]**Visual updates**: Animations, Sounds, Font Styling, Background, Colors, etc. - [x] Store list of questions in a seperate file - `questions.csv` - [x] Place `questions.csv` on Google Sheets and serve it from there // add 'Loading quiz' for lag. +- [x] Add multiple quiz urls served by multiple GoogleSheets +- [x] Create a #users tracker ### Medium 🟡 - [x] After game reset, **scramble order of questions** randomly - -- After clicking an option, pause for 2 sec, show correct answer, then move onto next one -- At the end show list of wrong questions with the correct answers -- Create different ReactJS components +- [ ] Collect scores +- [ ] After clicking an option, pause for 2 sec, show correct answer, then move onto next one +- [ ] At the end show list of wrong questions with the correct answers +- [ ] Create different ReactJS components ### Hard 🔴 -- Create a **live leaderboard** of users using a backend (Firebase, AWS, etc.) +- [] Create a **live leaderboard** of users using a backend (Firebase, AWS, etc.) diff --git a/package-lock.json b/package-lock.json index 7968a8e..c7e3f5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "moment": "^2.30.1", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-router-dom": "^6.29.0", "react-scripts": "5.0.0", "web-vitals": "^2.1.3" }, @@ -2673,6 +2674,15 @@ "node": ">= 8" } }, + "node_modules/@remix-run/router": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz", + "integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", @@ -13130,6 +13140,38 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.29.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.29.0.tgz", + "integrity": "sha512-DXZJoE0q+KyeVw75Ck6GkPxFak63C4fGqZGNijnWgzB/HzSP1ZfTlBj5COaGWwhrMQ/R8bXiq5Ooy4KG+ReyjQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.22.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.29.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.29.0.tgz", + "integrity": "sha512-pkEbJPATRJ2iotK+wUwHfy0xs2T59YPEN8BQxVCPeBZvK7kfPESRc/nyxzdcxR17hXgUPYx2whMwl+eo9cUdnQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.22.0", + "react-router": "6.29.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz", @@ -17827,6 +17869,11 @@ } } }, + "@remix-run/router": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz", + "integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw==" + }, "@rollup/plugin-babel": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", @@ -25321,6 +25368,23 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" }, + "react-router": { + "version": "6.29.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.29.0.tgz", + "integrity": "sha512-DXZJoE0q+KyeVw75Ck6GkPxFak63C4fGqZGNijnWgzB/HzSP1ZfTlBj5COaGWwhrMQ/R8bXiq5Ooy4KG+ReyjQ==", + "requires": { + "@remix-run/router": "1.22.0" + } + }, + "react-router-dom": { + "version": "6.29.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.29.0.tgz", + "integrity": "sha512-pkEbJPATRJ2iotK+wUwHfy0xs2T59YPEN8BQxVCPeBZvK7kfPESRc/nyxzdcxR17hXgUPYx2whMwl+eo9cUdnQ==", + "requires": { + "@remix-run/router": "1.22.0", + "react-router": "6.29.0" + } + }, "react-scripts": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz", diff --git a/package.json b/package.json index 74ecb58..1a1fd3a 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "moment": "^2.30.1", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-router-dom": "^6.29.0", "react-scripts": "5.0.0", "web-vitals": "^2.1.3" }, diff --git a/public/index.html b/public/index.html index bbc4f82..070fa59 100644 --- a/public/index.html +++ b/public/index.html @@ -6,10 +6,11 @@ Multiple Choice Quiz + diff --git a/src/App.js b/src/App.js index b31147b..8bfd65f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,66 +1,60 @@ import React, { useEffect, useState } from "react"; import "./App.css"; -import csv from "./quiz.csv"; - -function App() { - // Properties - const [showResults, setShowResults] = useState(false); - const [currentQuestion, setCurrentQuestion] = useState(0); - const [score, setScore] = useState(0); - - function csvToQuestions(csvString) { - const lines = csvString.trim().split("\n"); - const headers = lines[0].split(","); - const questions = []; - - for (let i = 1; i < lines.length; i++) { - const values = lines[i].split(","); - const questionId = parseInt(values[0], 10); - const questionText = values[1]; - const obj = { a: 2, b: 3, c: 4, d: 5 }; - const correctAnswerIndex = obj[values[6].replace("\r", "")]; // Assuming 'correct_ans' is at index 6 - - const options = []; - for (let j = 2; j <= 5; j++) { - options.push({ - id: j - 2, - text: values[j], - isCorrect: j === correctAnswerIndex, - }); - } - - questions.push({ - id: questionId, - text: questionText, - options: options, +import { BrowserRouter as Router, Routes, Route, Link} from 'react-router-dom'; // Import necessary components + +// Helper function to convert CSV to questions (shared by both quizzes) +function csvToQuestions(csvString) { + const lines = csvString.trim().split("\n"); + const headers = lines[0].split(","); + const questions = []; + + for (let i = 1; i < lines.length; i++) { + const values = lines[i].split(","); + const questionId = parseInt(values[0], 10); + const questionText = values[1]; + const obj = { a: 2, b: 3, c: 4, d: 5 }; + const correctAnswerIndex = obj[values[6].replace("\r", "")]; // Assuming 'correct_ans' is at index 6 + + const options = []; + for (let j = 2; j <= 5; j++) { + options.push({ + id: j - 2, + text: values[j], + isCorrect: j === correctAnswerIndex, }); } - // Shuffle the questions array once (using Fisher-Yates algorithm) - for (let i = questions.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [questions[i], questions[j]] = [questions[j], questions[i]]; - } - - return questions; + questions.push({ + id: questionId, + text: questionText, + options: options, + }); } - // Example CSV input (as a string) - // const csvData = `question_id,text,option_0,option_1,option_2,option_3,correct_ans - // 1,What is Particulate Matter (PM) 2.5?,All PM with radius less than 2.5 µm,All PM with radius less than 2.5 mm,All PM with diameter less than 2.5 µm,All PM with diameter less than 2.5 mm,2 - // 2,Which gas is responsible for acid rain?,Ozone (O3),Carbon Dioxide (CO2),Carbon Monoxide (CO),Sulphur Dioxide (SO2),3`; - // Read the CSV file + // Shuffle the questions array once (using Fisher-Yates algorithm) + for (let i = questions.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questions[i], questions[j]] = [questions[j], questions[i]]; + } - useEffect(() => { - loadquotesCSV(); - }, []); + return questions.slice(0,10); +} +// Quiz Component (reusable) +function Quiz({ googleSheetURL, quizTitle }) { const [questions, setQuestions] = useState([]); - const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions); + const [currentQuestion, setCurrentQuestion] = useState(0); + const [score, setScore] = useState(0); + const [showResults, setShowResults] = useState(false); + // const [loading, setLoading] = useState(true); // Add loading state - const GOOGLE_SHEET_CSV_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=0&single=true&output=csv"; - function loadquotesCSV() { - fetch(GOOGLE_SHEET_CSV_URL, { + useEffect(() => { + loadQuestions(); + }, [googleSheetURL]); // Add googleSheetURL as a dependency + + const loadQuestions = () => { + // setLoading(true); // Set loading to true before fetching + fetch(googleSheetURL, { headers: { "content-type": "text/csv;charset=UTF-8" }, method: "GET", }) @@ -68,88 +62,63 @@ function App() { if (!response.ok) { throw new Error("Network response was not ok"); } - // console.log("CSV Data:", response.text()); return response.text(); }) .then((csvData) => { - // console.log("CSV Data:", csvData); const data = csvToQuestions(csvData); setQuestions(data); - setQuestionsWithRandomIds(data); - // You can now process the CSV data as needed + // setLoading(false); // Set loading to false after successful fetch }) .catch((error) => { console.error("Error fetching CSV file:", error); + // setLoading(false); // Set loading to false even if there's an error }); - } + }; - // Helper Functions - /* A possible answer was clicked */ const optionClicked = (isCorrect) => { - // Increment the score if (isCorrect) { setScore(score + 1); } - if (currentQuestion + 1 < questionsWithRandomIds.length) { + if (currentQuestion + 1 < questions.length) { setCurrentQuestion(currentQuestion + 1); } else { setShowResults(true); } }; - /* Resets the game back to default */ const restartGame = () => { setScore(0); setCurrentQuestion(0); setShowResults(false); - // Shuffle the questions array once (using Fisher-Yates algorithm) - for (let i = questions.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [questions[i], questions[j]] = [questions[j], questions[i]]; - } - setQuestionsWithRandomIds(questions); + loadQuestions(); // Reload questions to shuffle }; - // if (begin_shuffle) { - // restartGame(); - // setBeginShuffle(false); - // } - return ( -
    - {/* 1. Header */} -

    Air Quality Quiz

    - - {/* Show loading message if questions are not yet loaded */} +
    {/* Add a class for styling */} +

    {quizTitle}

    {/* Display the quiz title */} {questions.length === 0 ? (

    Loading quiz...

    ) : ( <> - {/* 2. Current Score */}

    Score: {score}

    - - {/* 3. Show results or show the question game */} {showResults ? ( - /* 4. Final Results */

    Final Results

    - {score} out of {questionsWithRandomIds.length} correct - ( - {(score / questionsWithRandomIds.length) * 100}%) + {score} out of {questions.length} correct - ( + {(score / questions.length) * 100}%)

    ) : ( - /* 5. Question Card */

    - Question: {currentQuestion + 1} out of {questionsWithRandomIds.length} + Question: {currentQuestion + 1} out of {questions.length}

    -

    {questionsWithRandomIds[currentQuestion]?.text}

    - +

    {questions[currentQuestion]?.text}

      - {questionsWithRandomIds[currentQuestion]?.options.map((option) => ( + {questions[currentQuestion]?.options.map((option) => (
    • optionClicked(option.isCorrect)}> {option.text}
    • @@ -163,4 +132,30 @@ function App() { ); } -export default App; + +function App() { + const GOOGLE_SHEET_CSV_URL_1 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=0&single=true&output=csv"; // URL for Quiz 1 + const GOOGLE_SHEET_CSV_URL_2 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=329704009&single=true&output=csv"; // URL for Quiz 2 + + return ( + +
      + + +

      Air Quality Quizzes

      + Air Quality Basic Quiz
      {/* Link to Basic Quiz */} + Air Quality Advanced Quiz {/* Link to Advanced Quiz */} +
      + } /> + } /> + } /> + {/* Add more routes as needed */} + +
    + + ); + +} + +export default App; \ No newline at end of file diff --git a/src/Appp.js b/src/Appp.js new file mode 100644 index 0000000..e69de29 From 592b8ccb273d202572d172995755f04355cb6b75 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Mon, 10 Feb 2025 10:26:58 +0530 Subject: [PATCH 10/19] UPDATE Hashrouter for GH route refresh --- public/index.html | 5 +- src/App.js | 29 +++++--- src/Appp.js | 166 ++++++++++++++++++++++++++++++++++++++++++++++ src/index.js | 2 + 4 files changed, 191 insertions(+), 11 deletions(-) diff --git a/public/index.html b/public/index.html index 070fa59..69d15db 100644 --- a/public/index.html +++ b/public/index.html @@ -6,14 +6,15 @@ - Multiple Choice Quiz + Air Quality Quiz +
    diff --git a/src/App.js b/src/App.js index 8bfd65f..e50c7e2 100644 --- a/src/App.js +++ b/src/App.js @@ -7,7 +7,6 @@ function csvToQuestions(csvString) { const lines = csvString.trim().split("\n"); const headers = lines[0].split(","); const questions = []; - for (let i = 1; i < lines.length; i++) { const values = lines[i].split(","); const questionId = parseInt(values[0], 10); @@ -37,7 +36,12 @@ function csvToQuestions(csvString) { [questions[i], questions[j]] = [questions[j], questions[i]]; } - return questions.slice(0,10); + const selected_questions = questions.slice(0,10); + const set = selected_questions.map(question => question.id); + + console.log(set); + + return selected_questions; } // Quiz Component (reusable) @@ -46,14 +50,16 @@ function Quiz({ googleSheetURL, quizTitle }) { const [currentQuestion, setCurrentQuestion] = useState(0); const [score, setScore] = useState(0); const [showResults, setShowResults] = useState(false); - // const [loading, setLoading] = useState(true); // Add loading state + const [loading, setLoading] = useState(true); // ✅ New state to track loading + useEffect(() => { loadQuestions(); }, [googleSheetURL]); // Add googleSheetURL as a dependency const loadQuestions = () => { - // setLoading(true); // Set loading to true before fetching + setLoading(true); // ✅ Start loading + fetch(googleSheetURL, { headers: { "content-type": "text/csv;charset=UTF-8" }, method: "GET", @@ -67,12 +73,14 @@ function Quiz({ googleSheetURL, quizTitle }) { .then((csvData) => { const data = csvToQuestions(csvData); setQuestions(data); - // setLoading(false); // Set loading to false after successful fetch + setLoading(false); // ✅ Stop loading when questions are set + }) .catch((error) => { console.error("Error fetching CSV file:", error); - // setLoading(false); // Set loading to false even if there's an error - }); + setLoading(false); // ✅ Stop loading even if there is an error + + }) }; const optionClicked = (isCorrect) => { @@ -92,12 +100,15 @@ function Quiz({ googleSheetURL, quizTitle }) { setCurrentQuestion(0); setShowResults(false); loadQuestions(); // Reload questions to shuffle + }; return (
    {/* Add a class for styling */}

    {quizTitle}

    {/* Display the quiz title */} - {questions.length === 0 ? ( +

    Each quiz has 10 questions selected from a larger pool of questions. + Participants are encouraged to take the test multiple times

    + {(loading) ? (

    Loading quiz...

    ) : ( <> @@ -138,7 +149,7 @@ function App() { const GOOGLE_SHEET_CSV_URL_2 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=329704009&single=true&output=csv"; // URL for Quiz 2 return ( - +
    0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questions[i], questions[j]] = [questions[j], questions[i]]; + } + + return questions; + } + + // Example CSV input (as a string) + // const csvData = `question_id,text,option_0,option_1,option_2,option_3,correct_ans + // 1,What is Particulate Matter (PM) 2.5?,All PM with radius less than 2.5 µm,All PM with radius less than 2.5 mm,All PM with diameter less than 2.5 µm,All PM with diameter less than 2.5 mm,2 + // 2,Which gas is responsible for acid rain?,Ozone (O3),Carbon Dioxide (CO2),Carbon Monoxide (CO),Sulphur Dioxide (SO2),3`; + // Read the CSV file + + useEffect(() => { + loadquotesCSV(); + }, []); + + const [questions, setQuestions] = useState([]); + const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions); + + const GOOGLE_SHEET_CSV_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=0&single=true&output=csv"; + function loadquotesCSV() { + fetch(GOOGLE_SHEET_CSV_URL, { + headers: { "content-type": "text/csv;charset=UTF-8" }, + method: "GET", + }) + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + // console.log("CSV Data:", response.text()); + return response.text(); + }) + .then((csvData) => { + // console.log("CSV Data:", csvData); + const data = csvToQuestions(csvData); + setQuestions(data); + setQuestionsWithRandomIds(data); + // You can now process the CSV data as needed + }) + .catch((error) => { + console.error("Error fetching CSV file:", error); + }); + } + + // Helper Functions + /* A possible answer was clicked */ + const optionClicked = (isCorrect) => { + // Increment the score + if (isCorrect) { + setScore(score + 1); + } + + if (currentQuestion + 1 < questionsWithRandomIds.length) { + setCurrentQuestion(currentQuestion + 1); + } else { + setShowResults(true); + } + }; + + /* Resets the game back to default */ + const restartGame = () => { + setScore(0); + setCurrentQuestion(0); + setShowResults(false); + // Shuffle the questions array once (using Fisher-Yates algorithm) + for (let i = questions.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questions[i], questions[j]] = [questions[j], questions[i]]; + } + setQuestionsWithRandomIds(questions); + }; + + // if (begin_shuffle) { + // restartGame(); + // setBeginShuffle(false); + // } + + return ( +
    + {/* 1. Header */} +

    Air Quality Quiz

    + + {/* Show loading message if questions are not yet loaded */} + {questions.length === 0 ? ( +

    Loading quiz...

    + ) : ( + <> + {/* 2. Current Score */} +

    Score: {score}

    + + {/* 3. Show results or show the question game */} + {showResults ? ( + /* 4. Final Results */ +
    +

    Final Results

    +

    + {score} out of {questionsWithRandomIds.length} correct - ( + {(score / questionsWithRandomIds.length) * 100}%) +

    + +
    + ) : ( + /* 5. Question Card */ +
    +

    + Question: {currentQuestion + 1} out of {questionsWithRandomIds.length} +

    +

    {questionsWithRandomIds[currentQuestion]?.text}

    + +
      + {questionsWithRandomIds[currentQuestion]?.options.map((option) => ( +
    • optionClicked(option.isCorrect)}> + {option.text} +
    • + ))} +
    +
    + )} + + )} +
    + ); +} + +export default App; diff --git a/src/index.js b/src/index.js index 62adcf3..b7729e2 100644 --- a/src/index.js +++ b/src/index.js @@ -5,7 +5,9 @@ import App from './App'; ReactDOM.render( + + , document.getElementById('root') ); From 0231d1bbf000faaff7a2e8350b70977b8367ed97 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Mon, 10 Feb 2025 18:52:48 +0530 Subject: [PATCH 11/19] UPDATE Hashrouter --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index e50c7e2..398e2f4 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import "./App.css"; -import { BrowserRouter as Router, Routes, Route, Link} from 'react-router-dom'; // Import necessary components +import { HashRouter as Router, Routes, Route, Link} from 'react-router-dom'; // Import necessary components // Helper function to convert CSV to questions (shared by both quizzes) function csvToQuestions(csvString) { From 2b0a279d49fb650a64cd7930490ea122398feb0d Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Tue, 11 Feb 2025 12:18:55 +0530 Subject: [PATCH 12/19] UPDATE GH page --- package.json | 2 +- src/App.css | 4 ++-- src/App.js | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1a1fd3a..f67a76a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "homepage": "https://urbanemissions-info.github.io/airqualityquiz", + "homepage": "https://urbanemissionsinfo.github.io/airqualityquiz", "name": "air-quality-quiz", "version": "0.1.0", "private": true, diff --git a/src/App.css b/src/App.css index 7adb5e6..3f93451 100644 --- a/src/App.css +++ b/src/App.css @@ -59,7 +59,7 @@ li { } button { - background-color: red; /* Green */ + background-color: rgb(23, 81,18); border: none; color: white; padding: 16px 24px; @@ -68,6 +68,6 @@ button { display: inline-block; font-size: 16px; font-weight: bold; - border-radius: 24px; + margin-top: 8px; } diff --git a/src/App.js b/src/App.js index 398e2f4..810983f 100644 --- a/src/App.js +++ b/src/App.js @@ -120,8 +120,10 @@ function Quiz({ googleSheetURL, quizTitle }) { {score} out of {questions.length} correct - ( {(score / questions.length) * 100}%) - +

    +
    {/* Link to Home */}
    + ) : (

    From 04225f8ccd61eb8e859e14c50686125de1360c5f Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Tue, 11 Feb 2025 14:15:46 +0530 Subject: [PATCH 13/19] UPDATE show correct answer after each question --- README.md | 2 +- package-lock.json | 158 +++++------------------------------- package.json | 2 +- src/App.css | 19 +++++ src/App.js | 75 ++++++++++++++--- src/Appp.js | 199 +++++++++++++++++++++++++--------------------- 6 files changed, 209 insertions(+), 246 deletions(-) diff --git a/README.md b/README.md index 7df1e22..d3794db 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Answer the quiz on: [AirQualityQuiz](https://urbanemissions-info.github.io/airqu - [x] After game reset, **scramble order of questions** randomly - [ ] Collect scores -- [ ] After clicking an option, pause for 2 sec, show correct answer, then move onto next one +- [x] After clicking an option, show correct answer, then move onto next one (button or pause for 5 seconds) - [ ] At the end show list of wrong questions with the correct answers - [ ] Create different ReactJS components diff --git a/package-lock.json b/package-lock.json index c7e3f5e..4f0abe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "web-vitals": "^2.1.3" }, "devDependencies": { - "gh-pages": "^6.1.1" + "gh-pages": "^6.3.0" } }, "node_modules/@babel/code-frame": { @@ -4183,16 +4183,6 @@ "node": ">=8" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -7669,19 +7659,19 @@ } }, "node_modules/gh-pages": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz", - "integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.3.0.tgz", + "integrity": "sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==", "dev": true, "license": "MIT", "dependencies": { "async": "^3.2.4", - "commander": "^11.0.0", + "commander": "^13.0.0", "email-addresses": "^5.0.0", "filenamify": "^4.3.0", "find-cache-dir": "^3.3.1", "fs-extra": "^11.1.1", - "globby": "^6.1.0" + "globby": "^11.1.0" }, "bin": { "gh-pages": "bin/gh-pages.js", @@ -7691,19 +7681,6 @@ "node": ">=10" } }, - "node_modules/gh-pages/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/gh-pages/node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -7712,13 +7689,13 @@ "license": "MIT" }, "node_modules/gh-pages/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/gh-pages/node_modules/fs-extra": { @@ -7736,23 +7713,6 @@ "node": ">=14.14" } }, - "node_modules/gh-pages/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -11539,39 +11499,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pirates": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", @@ -18993,12 +18920,6 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true - }, "array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -21539,29 +21460,20 @@ } }, "gh-pages": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.1.1.tgz", - "integrity": "sha512-upnohfjBwN5hBP9w2dPE7HO5JJTHzSGMV1JrLrHvNuqmjoYHg6TBrCcnEoorjG/e0ejbuvnwyKMdTyM40PEByw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.3.0.tgz", + "integrity": "sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==", "dev": true, "requires": { "async": "^3.2.4", - "commander": "^11.0.0", + "commander": "^13.0.0", "email-addresses": "^5.0.0", "filenamify": "^4.3.0", "find-cache-dir": "^3.3.1", "fs-extra": "^11.1.1", - "globby": "^6.1.0" + "globby": "^11.1.0" }, "dependencies": { - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, "async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -21569,9 +21481,9 @@ "dev": true }, "commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true }, "fs-extra": { @@ -21584,19 +21496,6 @@ "jsonfile": "^6.0.1", "universalify": "^2.0.0" } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } } } }, @@ -24321,27 +24220,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, "pirates": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", diff --git a/package.json b/package.json index f67a76a..ce3035d 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,6 @@ ] }, "devDependencies": { - "gh-pages": "^6.1.1" + "gh-pages": "^6.3.0" } } diff --git a/src/App.css b/src/App.css index 3f93451..cbbf85d 100644 --- a/src/App.css +++ b/src/App.css @@ -71,3 +71,22 @@ button { border-radius: 24px; margin-top: 8px; } +.disabled { + pointer-events: none; /* Disable clicks */ + opacity: 0.6; /* Make it look disabled */ + cursor: default; /* Change cursor */ +} +.feedback { + /* Center the div */ + margin: 0 auto; + /* Dimensions */ + width: 70%; /* Can be in percentage also. */ + height: auto; + + /* Visual */ + background-color: rgb(212, 220, 154); + padding: 16px; + border-radius: 16px; + color: rgb(0, 0, 0); + margin-top: 10px; +} diff --git a/src/App.js b/src/App.js index 810983f..63fc774 100644 --- a/src/App.js +++ b/src/App.js @@ -27,6 +27,7 @@ function csvToQuestions(csvString) { id: questionId, text: questionText, options: options, + answer_explanation: values[7] }); } @@ -38,9 +39,7 @@ function csvToQuestions(csvString) { const selected_questions = questions.slice(0,10); const set = selected_questions.map(question => question.id); - - console.log(set); - + // console.log(set); return selected_questions; } @@ -51,7 +50,9 @@ function Quiz({ googleSheetURL, quizTitle }) { const [score, setScore] = useState(0); const [showResults, setShowResults] = useState(false); const [loading, setLoading] = useState(true); // ✅ New state to track loading - + const [selectedOption, setSelectedOption] = useState(null); + const [feedback, setFeedback] = useState(null); + const [showNextButton, setShowNextButton] = useState(false); // New state for button visibility useEffect(() => { loadQuestions(); @@ -83,17 +84,52 @@ function Quiz({ googleSheetURL, quizTitle }) { }) }; - const optionClicked = (isCorrect) => { - if (isCorrect) { - setScore(score + 1); - } + const optionClicked = (isCorrect, option, correctAnswerText, answerExplanationText) => { + + if (isCorrect) { + if (!selectedOption) { // Only allow click if no option is selected yet + setScore(score + 1); + } + setFeedback("Correct :) !"); + } else {setFeedback("Incorrect :( ");} + setSelectedOption(option); + // console.log(option); + setShowNextButton(true); // Show the "Next Question" button + + + // setTimeout(() => { // Use setTimeout for the delay + // if (currentQuestion + 1 < questions.length) { + // setCurrentQuestion(currentQuestion + 1); + // setSelectedOption(null); // Reset selected option for next question + // setFeedback(null); // Reset feedback + // } else { + // setShowResults(true); + // } + + // }, 5000); // 5000 milliseconds = 5 seconds + + // if (currentQuestion + 1 < questions.length) { + // setCurrentQuestion(currentQuestion + 1); + // } else { + // setShowResults(true); + // } + + // setSelectedOption(option); + }; + const goToNextQuestion = () => { if (currentQuestion + 1 < questions.length) { - setCurrentQuestion(currentQuestion + 1); + setCurrentQuestion(currentQuestion + 1); + setSelectedOption(null); + setFeedback(null); + setShowNextButton(false); // Hide the button } else { - setShowResults(true); + setShowResults(true); + setFeedback(null); + setShowNextButton(false); } - }; +}; + const restartGame = () => { setScore(0); @@ -132,13 +168,28 @@ function Quiz({ googleSheetURL, quizTitle }) {

    {questions[currentQuestion]?.text}

      {questions[currentQuestion]?.options.map((option) => ( -
    • optionClicked(option.isCorrect)}> +
    • (optionClicked(option.isCorrect, + option, + questions[currentQuestion]?.options.find(opt => opt.isCorrect)?.text, + questions[currentQuestion]?.answer_explanation))}> {option.text}
    • ))}

    )} + {/* Feedback */} + {feedback && ( +
    +

    {feedback}

    +

    Correct Answer: {questions[currentQuestion]?.options.find(opt => opt.isCorrect)?.text}

    +

    {questions[currentQuestion]?.answer_explanation}

    +
    + )} + {/* Next Question Button */} + {showNextButton && ( // Show the button only when showNextButton is true + + )} )}
    diff --git a/src/Appp.js b/src/Appp.js index b31147b..823a702 100644 --- a/src/Appp.js +++ b/src/Appp.js @@ -1,66 +1,71 @@ import React, { useEffect, useState } from "react"; import "./App.css"; -import csv from "./quiz.csv"; - -function App() { - // Properties - const [showResults, setShowResults] = useState(false); - const [currentQuestion, setCurrentQuestion] = useState(0); - const [score, setScore] = useState(0); - - function csvToQuestions(csvString) { - const lines = csvString.trim().split("\n"); - const headers = lines[0].split(","); - const questions = []; - - for (let i = 1; i < lines.length; i++) { - const values = lines[i].split(","); - const questionId = parseInt(values[0], 10); - const questionText = values[1]; - const obj = { a: 2, b: 3, c: 4, d: 5 }; - const correctAnswerIndex = obj[values[6].replace("\r", "")]; // Assuming 'correct_ans' is at index 6 - - const options = []; - for (let j = 2; j <= 5; j++) { - options.push({ - id: j - 2, - text: values[j], - isCorrect: j === correctAnswerIndex, - }); - } - - questions.push({ - id: questionId, - text: questionText, - options: options, +import { HashRouter as Router, Routes, Route, Link} from 'react-router-dom'; // Import necessary components + +// Helper function to convert CSV to questions (shared by both quizzes) +function csvToQuestions(csvString) { + const lines = csvString.trim().split("\n"); + const headers = lines[0].split(","); + const questions = []; + for (let i = 1; i < lines.length; i++) { + const values = lines[i].split(","); + const questionId = parseInt(values[0], 10); + const questionText = values[1]; + const obj = { a: 2, b: 3, c: 4, d: 5 }; + const correctAnswerIndex = obj[values[6].replace("\r", "")]; // Assuming 'correct_ans' is at index 6 + + const options = []; + for (let j = 2; j <= 5; j++) { + options.push({ + id: j - 2, + text: values[j], + isCorrect: j === correctAnswerIndex, }); } - // Shuffle the questions array once (using Fisher-Yates algorithm) - for (let i = questions.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [questions[i], questions[j]] = [questions[j], questions[i]]; - } + questions.push({ + id: questionId, + text: questionText, + options: options, + }); + } - return questions; + // Shuffle the questions array once (using Fisher-Yates algorithm) + for (let i = questions.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questions[i], questions[j]] = [questions[j], questions[i]]; } - // Example CSV input (as a string) - // const csvData = `question_id,text,option_0,option_1,option_2,option_3,correct_ans - // 1,What is Particulate Matter (PM) 2.5?,All PM with radius less than 2.5 µm,All PM with radius less than 2.5 mm,All PM with diameter less than 2.5 µm,All PM with diameter less than 2.5 mm,2 - // 2,Which gas is responsible for acid rain?,Ozone (O3),Carbon Dioxide (CO2),Carbon Monoxide (CO),Sulphur Dioxide (SO2),3`; - // Read the CSV file + const selected_questions = questions.slice(0,10); + const set = selected_questions.map(question => question.id); - useEffect(() => { - loadquotesCSV(); - }, []); + console.log(set); + return selected_questions; +} + +// Quiz Component (reusable) +function Quiz({ googleSheetURL, quizTitle }) { const [questions, setQuestions] = useState([]); - const [questionsWithRandomIds, setQuestionsWithRandomIds] = useState(questions); + const [currentQuestion, setCurrentQuestion] = useState(0); + const [score, setScore] = useState(0); + const [showResults, setShowResults] = useState(false); + const [loading, setLoading] = useState(true); // ✅ New state to track loading + const [selectedOption, setSelectedOption] = useState(null); + const [feedback, setFeedback] = useState(null); + const [answerExplanation, setAnswerExplanation] = useState(null); // New state for explanation + + + + + useEffect(() => { + loadQuestions(); + }, [googleSheetURL]); // Add googleSheetURL as a dependency - const GOOGLE_SHEET_CSV_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=0&single=true&output=csv"; - function loadquotesCSV() { - fetch(GOOGLE_SHEET_CSV_URL, { + const loadQuestions = () => { + setLoading(true); // ✅ Start loading + + fetch(googleSheetURL, { headers: { "content-type": "text/csv;charset=UTF-8" }, method: "GET", }) @@ -68,94 +73,78 @@ function App() { if (!response.ok) { throw new Error("Network response was not ok"); } - // console.log("CSV Data:", response.text()); return response.text(); }) .then((csvData) => { - // console.log("CSV Data:", csvData); const data = csvToQuestions(csvData); setQuestions(data); - setQuestionsWithRandomIds(data); - // You can now process the CSV data as needed + setLoading(false); // ✅ Stop loading when questions are set + }) .catch((error) => { console.error("Error fetching CSV file:", error); - }); - } + setLoading(false); // ✅ Stop loading even if there is an error + + }) + }; - // Helper Functions - /* A possible answer was clicked */ const optionClicked = (isCorrect) => { - // Increment the score if (isCorrect) { setScore(score + 1); } - if (currentQuestion + 1 < questionsWithRandomIds.length) { + if (currentQuestion + 1 < questions.length) { setCurrentQuestion(currentQuestion + 1); } else { setShowResults(true); } }; - /* Resets the game back to default */ const restartGame = () => { setScore(0); setCurrentQuestion(0); setShowResults(false); - // Shuffle the questions array once (using Fisher-Yates algorithm) - for (let i = questions.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [questions[i], questions[j]] = [questions[j], questions[i]]; - } - setQuestionsWithRandomIds(questions); + loadQuestions(); // Reload questions to shuffle + }; - // if (begin_shuffle) { - // restartGame(); - // setBeginShuffle(false); - // } - return ( -
    - {/* 1. Header */} -

    Air Quality Quiz

    - - {/* Show loading message if questions are not yet loaded */} - {questions.length === 0 ? ( +
    {/* Add a class for styling */} +

    {quizTitle}

    {/* Display the quiz title */} +

    Each quiz has 10 questions selected from a larger pool of questions. + Participants are encouraged to take the test multiple times

    + {(loading) ? (

    Loading quiz...

    ) : ( <> - {/* 2. Current Score */}

    Score: {score}

    - - {/* 3. Show results or show the question game */} {showResults ? ( - /* 4. Final Results */

    Final Results

    - {score} out of {questionsWithRandomIds.length} correct - ( - {(score / questionsWithRandomIds.length) * 100}%) + {score} out of {questions.length} correct - ( + {(score / questions.length) * 100}%)

    - +

    +
    {/* Link to Home */}
    + ) : ( - /* 5. Question Card */

    - Question: {currentQuestion + 1} out of {questionsWithRandomIds.length} + Question: {currentQuestion + 1} out of {questions.length}

    -

    {questionsWithRandomIds[currentQuestion]?.text}

    - +

    {questions[currentQuestion]?.text}

      - {questionsWithRandomIds[currentQuestion]?.options.map((option) => ( + {questions[currentQuestion]?.options.map((option) => (
    • optionClicked(option.isCorrect)}> {option.text}
    • ))}
    + + )} )} @@ -163,4 +152,30 @@ function App() { ); } -export default App; + +function App() { + const GOOGLE_SHEET_CSV_URL_1 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=0&single=true&output=csv"; // URL for Quiz 1 + const GOOGLE_SHEET_CSV_URL_2 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=329704009&single=true&output=csv"; // URL for Quiz 2 + + return ( + +
    + + +

    Air Quality Quizzes

    + Air Quality Basic Quiz
    {/* Link to Basic Quiz */} + Air Quality Advanced Quiz {/* Link to Advanced Quiz */} +
    + } /> + } /> + } /> + {/* Add more routes as needed */} + +
    + + ); + +} + +export default App; \ No newline at end of file From b6a07a2def64a96b5067725738bce9cac321d844 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Wed, 12 Feb 2025 15:21:43 +0530 Subject: [PATCH 14/19] ADD backedn integration --- README.md | 8 ++-- package-lock.json | 114 +++++++++++++++++++++++++++++++--------------- package.json | 5 +- src/App.js | 62 +++++++++++++++++++++---- 4 files changed, 137 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index d3794db..e09a3ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Multiple Choice Quiz (ReactJS) -Answer the quiz on: [AirQualityQuiz](https://urbanemissions-info.github.io/airqualityquiz/) +Answer the quiz on: [AirQualityQuiz](https://urbanemissionsinfo.github.io/airqualityquiz/) ![alt text](image.png) @@ -20,10 +20,10 @@ Answer the quiz on: [AirQualityQuiz](https://urbanemissions-info.github.io/airqu ### Medium 🟡 - [x] After game reset, **scramble order of questions** randomly -- [ ] Collect scores - [x] After clicking an option, show correct answer, then move onto next one (button or pause for 5 seconds) - [ ] At the end show list of wrong questions with the correct answers - [ ] Create different ReactJS components -### Hard 🔴 -- [] Create a **live leaderboard** of users using a backend (Firebase, AWS, etc.) +### Hard 🔴 - requires backend. +- [x] Collect scores - total and questionwise. +- [ ] Create a **live leaderboard** of users diff --git a/package-lock.json b/package-lock.json index 4f0abe1..0ddfb43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,15 +11,14 @@ "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.9", + "gh-pages": "^6.3.0", "moment": "^2.30.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-dom": "^6.29.0", "react-scripts": "5.0.0", "web-vitals": "^2.1.3" - }, - "devDependencies": { - "gh-pages": "^6.3.0" } }, "node_modules/@babel/code-frame": { @@ -4291,6 +4290,31 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -6105,7 +6129,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", - "dev": true, "license": "MIT" }, "node_modules/emittery": { @@ -7207,7 +7230,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -7217,7 +7239,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", - "dev": true, "license": "MIT", "dependencies": { "filename-reserved-regex": "^2.0.0", @@ -7329,15 +7350,16 @@ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==" }, "node_modules/follow-redirects": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", - "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -7662,7 +7684,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.3.0.tgz", "integrity": "sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==", - "dev": true, "license": "MIT", "dependencies": { "async": "^3.2.4", @@ -7685,24 +7706,21 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, "license": "MIT" }, "node_modules/gh-pages/node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/gh-pages/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -12771,6 +12789,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -14213,7 +14237,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dev": true, "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.2" @@ -14735,7 +14758,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", - "dev": true, "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.2" @@ -18991,6 +19013,28 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz", "integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==" }, + "axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -20349,8 +20393,7 @@ "email-addresses": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", - "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", - "dev": true + "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==" }, "emittery": { "version": "0.8.1", @@ -21157,14 +21200,12 @@ "filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", - "dev": true + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==" }, "filenamify": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", - "dev": true, "requires": { "filename-reserved-regex": "^2.0.0", "strip-outer": "^1.0.1", @@ -21247,9 +21288,9 @@ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==" }, "follow-redirects": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", - "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" }, "fork-ts-checker-webpack-plugin": { "version": "6.5.0", @@ -21463,7 +21504,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.3.0.tgz", "integrity": "sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==", - "dev": true, "requires": { "async": "^3.2.4", "commander": "^13.0.0", @@ -21477,20 +21517,17 @@ "async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, "commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==" }, "fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -25036,6 +25073,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -26097,7 +26139,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dev": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -26467,7 +26508,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", - "dev": true, "requires": { "escape-string-regexp": "^1.0.2" } diff --git a/package.json b/package.json index ce3035d..935c20e 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.9", + "gh-pages": "^6.3.0", "moment": "^2.30.1", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -39,8 +41,5 @@ "last 1 firefox version", "last 1 safari version" ] - }, - "devDependencies": { - "gh-pages": "^6.3.0" } } diff --git a/src/App.js b/src/App.js index 63fc774..7cc4cf9 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import "./App.css"; import { HashRouter as Router, Routes, Route, Link} from 'react-router-dom'; // Import necessary components +import axios from 'axios'; // Helper function to convert CSV to questions (shared by both quizzes) function csvToQuestions(csvString) { @@ -9,7 +10,7 @@ function csvToQuestions(csvString) { const questions = []; for (let i = 1; i < lines.length; i++) { const values = lines[i].split(","); - const questionId = parseInt(values[0], 10); + const questionId = values[0]; const questionText = values[1]; const obj = { a: 2, b: 3, c: 4, d: 5 }; const correctAnswerIndex = obj[values[6].replace("\r", "")]; // Assuming 'correct_ans' is at index 6 @@ -38,8 +39,6 @@ function csvToQuestions(csvString) { } const selected_questions = questions.slice(0,10); - const set = selected_questions.map(question => question.id); - // console.log(set); return selected_questions; } @@ -53,6 +52,9 @@ function Quiz({ googleSheetURL, quizTitle }) { const [selectedOption, setSelectedOption] = useState(null); const [feedback, setFeedback] = useState(null); const [showNextButton, setShowNextButton] = useState(false); // New state for button visibility + const [responses, setResponses] = useState([]); // New state to store responses + const [qids, setQids] = useState([]); // New state to store responses + useEffect(() => { loadQuestions(); @@ -75,7 +77,8 @@ function Quiz({ googleSheetURL, quizTitle }) { const data = csvToQuestions(csvData); setQuestions(data); setLoading(false); // ✅ Stop loading when questions are set - + const question_ids = data.map(question => question.id); + setQids(() => question_ids); // Correct Way to Update State based on previous value. }) .catch((error) => { console.error("Error fetching CSV file:", error); @@ -84,8 +87,13 @@ function Quiz({ googleSheetURL, quizTitle }) { }) }; + useEffect(() => { + console.log("qids in useEffect:", qids); // Correct: Shows the updated array +}, [qids]); // This useEffect runs whenever qids changes + + const optionClicked = (isCorrect, option, correctAnswerText, answerExplanationText) => { - + responses.push(option.id); if (isCorrect) { if (!selectedOption) { // Only allow click if no option is selected yet setScore(score + 1); @@ -93,7 +101,6 @@ function Quiz({ googleSheetURL, quizTitle }) { setFeedback("Correct :) !"); } else {setFeedback("Incorrect :( ");} setSelectedOption(option); - // console.log(option); setShowNextButton(true); // Show the "Next Question" button @@ -123,15 +130,54 @@ function Quiz({ googleSheetURL, quizTitle }) { setSelectedOption(null); setFeedback(null); setShowNextButton(false); // Hide the button + // console.log(responses); } else { + + saveScore(); setShowResults(true); setFeedback(null); setShowNextButton(false); } }; +const saveScore = async () => { + console.log(qids); + try { + const scoreData = { + quizName: quizTitle, // The quiz title + score: score, // The user's score + date: new Date().toISOString(), // Current date and time in ISO 8601 format (important!) + question_ids: qids, + responses: responses, + }; + + const response = await axios.post('http://127.0.0.1:8000/api/saveScore', scoreData); // Or your full URL if deployed + + console.log('Score saved successfully:', response.data); // Log the response from the server + // Optionally, you can show a success message to the user here + } catch (error) { + console.error('Error saving score:', error); + // Display an error message to the user + if (error.response) { + // The request was made and the server responded with a status code + // that falls out of the range of 2xx + console.error("Response data:", error.response.data); + console.error("Response status:", error.response.status); + console.error("Response headers:", error.response.headers); + } else if (error.request) { + // The request was made but no response was received + // `error.request` is an instance of XMLHttpRequest in the browser and an instance of + // http.ClientRequest in node.js + console.error("Request:", error.request); + } else { + // Something happened in setting up the request that triggered an Error + console.error("Error message:", error.message); + } + + } +}; - const restartGame = () => { +const restartGame = () => { setScore(0); setCurrentQuestion(0); setShowResults(false); @@ -149,7 +195,7 @@ function Quiz({ googleSheetURL, quizTitle }) { ) : ( <>

    Score: {score}

    - {showResults ? ( + {showResults ? (

    Final Results

    From 76cac5a45d56e1594790bc61627f7f3a657102ab Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Thu, 13 Feb 2025 15:08:00 +0530 Subject: [PATCH 15/19] ADD Backend url - render --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 7cc4cf9..bffcc3a 100644 --- a/src/App.js +++ b/src/App.js @@ -151,7 +151,7 @@ const saveScore = async () => { responses: responses, }; - const response = await axios.post('http://127.0.0.1:8000/api/saveScore', scoreData); // Or your full URL if deployed + const response = await axios.post('https://airqualityquiz-backend.onrender.com/api/saveScore', scoreData); // Or your full URL if deployed console.log('Score saved successfully:', response.data); // Log the response from the server // Optionally, you can show a success message to the user here From 639db0fdf7616bbff5016f1e1f4a439a08655b85 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Sat, 15 Feb 2025 09:30:24 +0530 Subject: [PATCH 16/19] UPDATE - block advanced quiz --- src/App.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index bffcc3a..d6133f7 100644 --- a/src/App.js +++ b/src/App.js @@ -255,11 +255,11 @@ function App() {

    Air Quality Quizzes

    Air Quality Basic Quiz
    {/* Link to Basic Quiz */} - Air Quality Advanced Quiz {/* Link to Advanced Quiz */} + Air Quality Advanced Quiz (Coming soon...) {/* Link to Advanced Quiz */}
    } /> } /> - } /> + {/* } /> */} {/* Add more routes as needed */}

    From ae566e321973c94d9f5b0eeed19197de0ac74f6c Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Sun, 2 Mar 2025 12:19:11 +0530 Subject: [PATCH 17/19] UPDATE titles --- src/App.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index d6133f7..1a3daa0 100644 --- a/src/App.js +++ b/src/App.js @@ -188,7 +188,7 @@ const restartGame = () => { return (
    {/* Add a class for styling */}

    {quizTitle}

    {/* Display the quiz title */} -

    Each quiz has 10 questions selected from a larger pool of questions. +

    Each quiz has 10 questions selected from a pool of questions. Participants are encouraged to take the test multiple times

    {(loading) ? (

    Loading quiz...

    @@ -254,11 +254,11 @@ function App() {

    Air Quality Quizzes

    - Air Quality Basic Quiz
    {/* Link to Basic Quiz */} + Air Quality Basics Quiz
    {/* Link to Basic Quiz */} Air Quality Advanced Quiz (Coming soon...) {/* Link to Advanced Quiz */}
    } /> - } /> + } /> {/* } /> */} {/* Add more routes as needed */} From ab685695cc36176c2e18b35205232349caeefb45 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Sun, 2 Mar 2025 18:40:18 +0530 Subject: [PATCH 18/19] UPDATE question responses error fixed --- src/App.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index 1a3daa0..7497a56 100644 --- a/src/App.js +++ b/src/App.js @@ -53,7 +53,7 @@ function Quiz({ googleSheetURL, quizTitle }) { const [feedback, setFeedback] = useState(null); const [showNextButton, setShowNextButton] = useState(false); // New state for button visibility const [responses, setResponses] = useState([]); // New state to store responses - const [qids, setQids] = useState([]); // New state to store responses + const [qids, setQids] = useState([]); // New state to store question IDs useEffect(() => { @@ -93,14 +93,17 @@ function Quiz({ googleSheetURL, quizTitle }) { const optionClicked = (isCorrect, option, correctAnswerText, answerExplanationText) => { + if (!selectedOption) { responses.push(option.id); + } if (isCorrect) { if (!selectedOption) { // Only allow click if no option is selected yet setScore(score + 1); + setFeedback("Correct :) !"); } - setFeedback("Correct :) !"); } else {setFeedback("Incorrect :( ");} setSelectedOption(option); + console.log(responses); setShowNextButton(true); // Show the "Next Question" button From 83f6bf62a78ed4483f6b1c22139e7ea812bb28f4 Mon Sep 17 00:00:00 2001 From: d-saikrishna Date: Wed, 28 May 2025 16:34:49 +0530 Subject: [PATCH 19/19] UPDATE - document --- README.md | 2 ++ src/App.js | 91 +++++++++++++++++------------------------------------- 2 files changed, 31 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index e09a3ee..fafe254 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Answer the quiz on: [AirQualityQuiz](https://urbanemissionsinfo.github.io/airqualityquiz/) +The slide doc to this app can be found here: [Slides](https://docs.google.com/presentation/d/1Q-Ula2IBLRKel3bROX5Ly3UaF7uEDVBFx6Wfe9IGZ7U/edit?usp=sharing) + ![alt text](image.png) ## Deploy on GH pages diff --git a/src/App.js b/src/App.js index 7497a56..83728e3 100644 --- a/src/App.js +++ b/src/App.js @@ -8,13 +8,13 @@ function csvToQuestions(csvString) { const lines = csvString.trim().split("\n"); const headers = lines[0].split(","); const questions = []; + for (let i = 1; i < lines.length; i++) { const values = lines[i].split(","); const questionId = values[0]; const questionText = values[1]; const obj = { a: 2, b: 3, c: 4, d: 5 }; const correctAnswerIndex = obj[values[6].replace("\r", "")]; // Assuming 'correct_ans' is at index 6 - const options = []; for (let j = 2; j <= 5; j++) { options.push({ @@ -23,7 +23,7 @@ function csvToQuestions(csvString) { isCorrect: j === correctAnswerIndex, }); } - + questions.push({ id: questionId, text: questionText, @@ -31,19 +31,17 @@ function csvToQuestions(csvString) { answer_explanation: values[7] }); } - // Shuffle the questions array once (using Fisher-Yates algorithm) for (let i = questions.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [questions[i], questions[j]] = [questions[j], questions[i]]; - } - + const j = Math.floor(Math.random() * (i + 1)); + [questions[i], questions[j]] = [questions[j], questions[i]];} const selected_questions = questions.slice(0,10); return selected_questions; } -// Quiz Component (reusable) +// QUIZ COMPONENT (reusable) function Quiz({ googleSheetURL, quizTitle }) { + // State Variables const [questions, setQuestions] = useState([]); const [currentQuestion, setCurrentQuestion] = useState(0); const [score, setScore] = useState(0); @@ -91,40 +89,23 @@ function Quiz({ googleSheetURL, quizTitle }) { console.log("qids in useEffect:", qids); // Correct: Shows the updated array }, [qids]); // This useEffect runs whenever qids changes - - const optionClicked = (isCorrect, option, correctAnswerText, answerExplanationText) => { + // Function to change score and show feedback after option click + const optionClicked = (isCorrect, option) => { + // Save first option clicked if (!selectedOption) { responses.push(option.id); } - if (isCorrect) { + + if (isCorrect) { if (!selectedOption) { // Only allow click if no option is selected yet setScore(score + 1); setFeedback("Correct :) !"); } } else {setFeedback("Incorrect :( ");} - setSelectedOption(option); + + setSelectedOption(option); // This ensures that there is no change in responses, score and feedback when user clicks other options console.log(responses); setShowNextButton(true); // Show the "Next Question" button - - - // setTimeout(() => { // Use setTimeout for the delay - // if (currentQuestion + 1 < questions.length) { - // setCurrentQuestion(currentQuestion + 1); - // setSelectedOption(null); // Reset selected option for next question - // setFeedback(null); // Reset feedback - // } else { - // setShowResults(true); - // } - - // }, 5000); // 5000 milliseconds = 5 seconds - - // if (currentQuestion + 1 < questions.length) { - // setCurrentQuestion(currentQuestion + 1); - // } else { - // setShowResults(true); - // } - - // setSelectedOption(option); }; const goToNextQuestion = () => { @@ -133,9 +114,7 @@ function Quiz({ googleSheetURL, quizTitle }) { setSelectedOption(null); setFeedback(null); setShowNextButton(false); // Hide the button - // console.log(responses); } else { - saveScore(); setShowResults(true); setFeedback(null); @@ -153,17 +132,12 @@ const saveScore = async () => { question_ids: qids, responses: responses, }; - const response = await axios.post('https://airqualityquiz-backend.onrender.com/api/saveScore', scoreData); // Or your full URL if deployed - console.log('Score saved successfully:', response.data); // Log the response from the server - // Optionally, you can show a success message to the user here } catch (error) { - console.error('Error saving score:', error); - // Display an error message to the user + console.error('Error saving score:', error); // Display an error message to the user if (error.response) { - // The request was made and the server responded with a status code - // that falls out of the range of 2xx + // The request was made and the server responded with a status code that falls out of the range of 2xx console.error("Response data:", error.response.data); console.error("Response status:", error.response.status); console.error("Response headers:", error.response.headers); @@ -176,7 +150,6 @@ const saveScore = async () => { // Something happened in setting up the request that triggered an Error console.error("Error message:", error.message); } - } }; @@ -191,12 +164,9 @@ const restartGame = () => { return (
    {/* Add a class for styling */}

    {quizTitle}

    {/* Display the quiz title */} -

    Each quiz has 10 questions selected from a pool of questions. - Participants are encouraged to take the test multiple times

    - {(loading) ? ( -

    Loading quiz...

    - ) : ( - <> +

    Each quiz has 10 questions selected from a pool of questions. Participants are encouraged to take the test multiple times

    + {(loading) ? (

    Loading quiz...

    ) : ( + <> {/* React fragment start */}

    Score: {score}

    {showResults ? (
    @@ -207,8 +177,7 @@ const restartGame = () => {


    {/* Link to Home */} -
    - +
    ) : (

    @@ -217,10 +186,7 @@ const restartGame = () => {

    {questions[currentQuestion]?.text}

      {questions[currentQuestion]?.options.map((option) => ( -
    • (optionClicked(option.isCorrect, - option, - questions[currentQuestion]?.options.find(opt => opt.isCorrect)?.text, - questions[currentQuestion]?.answer_explanation))}> +
    • (optionClicked(option.isCorrect, option))}> {option.text}
    • ))} @@ -245,30 +211,31 @@ const restartGame = () => { ); } - +// APP COMPONENT function App() { - const GOOGLE_SHEET_CSV_URL_1 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=0&single=true&output=csv"; // URL for Quiz 1 - const GOOGLE_SHEET_CSV_URL_2 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=329704009&single=true&output=csv"; // URL for Quiz 2 + const GOOGLE_SHEET_CSV_URL_1 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=329704009&single=true&output=csv"; // URL for Quiz 1 + const GOOGLE_SHEET_CSV_URL_2 = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSzzNg3HDQK3vUKpEnIwOREwa-SeRIcfYoECkL1qwivnChSUy5xrI7vE8Gpipuo_TxX6YDerL97rfGG/pub?gid=0&single=true&output=csv"; // URL for Quiz 2 return (
      -

      Air Quality Quizzes

      Air Quality Basics Quiz
      {/* Link to Basic Quiz */} Air Quality Advanced Quiz (Coming soon...) {/* Link to Advanced Quiz */}
      - } /> - } /> - {/* } /> */} + } + /> + + } /> + {/* } /> */} {/* Add more routes as needed */}
    ); - } export default App; \ No newline at end of file