feat: Create Vue.js assessment app with file upload capability

Torey Heinz committed Jul 12, 2025
commit 640c4ab2053d9c5c1ecb767ea6e46ae03f40dc2b
Showing 21 changed files with 2807 additions and 0 deletions
.gitignore +24 -0
@@ @@ -0,0 +1,24 @@
+ # Logs
+ logs
+ *.log
+ npm-debug.log*
+ yarn-debug.log*
+ yarn-error.log*
+ pnpm-debug.log*
+ lerna-debug.log*
+
+ node_modules
+ dist
+ dist-ssr
+ *.local
+
+ # Editor directories and files
+ .vscode/*
+ !.vscode/extensions.json
+ .idea
+ .DS_Store
+ *.suo
+ *.ntvs*
+ *.njsproj
+ *.sln
+ *.sw?
.vscode/extensions.json +3 -0
@@ @@ -0,0 +1,3 @@
+ {
+ "recommendations": ["Vue.volar"]
+ }
DEPLOYMENT.md +53 -0
@@ @@ -0,0 +1,53 @@
+ # Cloudflare Pages Deployment Guide
+
+ ## Prerequisites
+ - Cloudflare account
+ - GitHub repository with this code
+
+ ## Deployment Steps
+
+ 1. **Build the project locally** (optional, to test):
+ ```bash
+ npm install
+ npm run build
+ ```
+
+ 2. **Push to GitHub**:
+ ```bash
+ git init
+ git add .
+ git commit -m "Initial commit"
+ git remote add origin YOUR_GITHUB_REPO_URL
+ git push -u origin main
+ ```
+
+ 3. **Deploy to Cloudflare Pages**:
+ - Go to [Cloudflare Pages](https://pages.cloudflare.com/)
+ - Click "Create a project"
+ - Connect to Git and select your repository
+ - Configure build settings:
+ - Build command: `npm run build`
+ - Build output directory: `dist`
+ - Root directory: `/assessment-app` (if in subdirectory)
+ - Click "Save and Deploy"
+
+ 4. **Custom Domain** (optional):
+ - After deployment, go to Custom domains
+ - Add your domain (e.g., `assessment-app.example.com`)
+ - Update DNS records as instructed
+
+ ## Adding New Assessments
+
+ 1. Create a new JSON file in `public/assessments/` (e.g., `11th-grade-assessment.json`)
+ 2. Follow the same structure as existing assessment files
+ 3. Add the assessment to the home page in `src/views/Home.vue`
+ 4. Commit and push - Cloudflare will automatically rebuild
+
+ ## Local Development
+
+ ```bash
+ npm install
+ npm run dev
+ ```
+
+ Visit http://localhost:5173 to see the app.
\ No newline at end of file
README.md +5 -0
@@ @@ -0,0 +1,5 @@
+ # Vue 3 + Vite
+
+ This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+
+ Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
index.html +13 -0
@@ @@ -0,0 +1,13 @@
+ <!doctype html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Vite + Vue</title>
+ </head>
+ <body>
+ <div id="app"></div>
+ <script type="module" src="/src/main.js"></script>
+ </body>
+ </html>
package-lock.json +1278 -0
@@ @@ -0,0 +1,1278 @@
+ {
+ "name": "assessment-app",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "assessment-app",
+ "version": "0.0.0",
+ "dependencies": {
+ "vue": "^3.5.17",
+ "vue-router": "^4.5.1"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^6.0.0",
+ "vite": "^7.0.4"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
+ "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz",
+ "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz",
+ "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz",
+ "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz",
+ "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz",
+ "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz",
+ "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz",
+ "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz",
+ "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz",
+ "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz",
+ "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz",
+ "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz",
+ "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz",
+ "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz",
+ "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz",
+ "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz",
+ "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz",
+ "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz",
+ "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz",
+ "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz",
+ "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz",
+ "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz",
+ "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz",
+ "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz",
+ "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
+ "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
+ "license": "MIT"
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.19",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz",
+ "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz",
+ "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz",
+ "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz",
+ "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz",
+ "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz",
+ "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz",
+ "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz",
+ "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz",
+ "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz",
+ "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz",
+ "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz",
+ "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz",
+ "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz",
+ "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz",
+ "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz",
+ "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz",
+ "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz",
+ "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz",
+ "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz",
+ "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz",
+ "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz",
+ "integrity": "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-beta.19"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
+ "vue": "^3.2.25"
+ }
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
+ "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.27.5",
+ "@vue/shared": "3.5.17",
+ "entities": "^4.5.0",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz",
+ "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.17",
+ "@vue/shared": "3.5.17"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz",
+ "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.27.5",
+ "@vue/compiler-core": "3.5.17",
+ "@vue/compiler-dom": "3.5.17",
+ "@vue/compiler-ssr": "3.5.17",
+ "@vue/shared": "3.5.17",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.17",
+ "postcss": "^8.5.6",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz",
+ "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.17",
+ "@vue/shared": "3.5.17"
+ }
+ },
+ "node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz",
+ "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.17"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz",
+ "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.17",
+ "@vue/shared": "3.5.17"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz",
+ "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.17",
+ "@vue/runtime-core": "3.5.17",
+ "@vue/shared": "3.5.17",
+ "csstype": "^3.1.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz",
+ "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.17",
+ "@vue/shared": "3.5.17"
+ },
+ "peerDependencies": {
+ "vue": "3.5.17"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz",
+ "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==",
+ "license": "MIT"
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz",
+ "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.6",
+ "@esbuild/android-arm": "0.25.6",
+ "@esbuild/android-arm64": "0.25.6",
+ "@esbuild/android-x64": "0.25.6",
+ "@esbuild/darwin-arm64": "0.25.6",
+ "@esbuild/darwin-x64": "0.25.6",
+ "@esbuild/freebsd-arm64": "0.25.6",
+ "@esbuild/freebsd-x64": "0.25.6",
+ "@esbuild/linux-arm": "0.25.6",
+ "@esbuild/linux-arm64": "0.25.6",
+ "@esbuild/linux-ia32": "0.25.6",
+ "@esbuild/linux-loong64": "0.25.6",
+ "@esbuild/linux-mips64el": "0.25.6",
+ "@esbuild/linux-ppc64": "0.25.6",
+ "@esbuild/linux-riscv64": "0.25.6",
+ "@esbuild/linux-s390x": "0.25.6",
+ "@esbuild/linux-x64": "0.25.6",
+ "@esbuild/netbsd-arm64": "0.25.6",
+ "@esbuild/netbsd-x64": "0.25.6",
+ "@esbuild/openbsd-arm64": "0.25.6",
+ "@esbuild/openbsd-x64": "0.25.6",
+ "@esbuild/openharmony-arm64": "0.25.6",
+ "@esbuild/sunos-x64": "0.25.6",
+ "@esbuild/win32-arm64": "0.25.6",
+ "@esbuild/win32-ia32": "0.25.6",
+ "@esbuild/win32-x64": "0.25.6"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz",
+ "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.44.2",
+ "@rollup/rollup-android-arm64": "4.44.2",
+ "@rollup/rollup-darwin-arm64": "4.44.2",
+ "@rollup/rollup-darwin-x64": "4.44.2",
+ "@rollup/rollup-freebsd-arm64": "4.44.2",
+ "@rollup/rollup-freebsd-x64": "4.44.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.44.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.44.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.44.2",
+ "@rollup/rollup-linux-arm64-musl": "4.44.2",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.44.2",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.44.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.44.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.44.2",
+ "@rollup/rollup-linux-x64-gnu": "4.44.2",
+ "@rollup/rollup-linux-x64-musl": "4.44.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.44.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.44.2",
+ "@rollup/rollup-win32-x64-msvc": "4.44.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz",
+ "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.6",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.6",
+ "rollup": "^4.40.0",
+ "tinyglobby": "^0.2.14"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz",
+ "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.17",
+ "@vue/compiler-sfc": "3.5.17",
+ "@vue/runtime-dom": "3.5.17",
+ "@vue/server-renderer": "3.5.17",
+ "@vue/shared": "3.5.17"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-router": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
+ "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.6.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ }
+ }
+ }
package.json +19 -0
@@ @@ -0,0 +1,19 @@
+ {
+ "name": "assessment-app",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "vue": "^3.5.17",
+ "vue-router": "^4.5.1"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^6.0.0",
+ "vite": "^7.0.4"
+ }
+ }
public/_redirects +1 -0
@@ @@ -0,0 +1 @@
+ /* /index.html 200
\ No newline at end of file
public/assessments/10th-grade-assessment.json +23 -0
@@ @@ -0,0 +1,23 @@
+ {
+ "title": "10th Grade Readiness Assessment",
+ "description": "Sample assessment for 10th grade students",
+ "subjects": [
+ {
+ "name": "Sample Questions",
+ "questions": [
+ {
+ "id": 1,
+ "question": "This is a sample question for 10th grade assessment",
+ "options": ["Option A", "Option B", "Option C", "Option D"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 2,
+ "question": "Another sample question",
+ "options": ["Choice 1", "Choice 2", "Choice 3", "Choice 4"],
+ "correctAnswer": 1
+ }
+ ]
+ }
+ ]
+ }
\ No newline at end of file
public/assessments/9th-grade-assessment-chatgpt.json +296 -0
@@ @@ -0,0 +1,296 @@
+ {
+ "title": "9th Grade Readiness Assessment (ChatGPT)",
+ "description": "Test your knowledge in History, Geography, Science, Math, and Language Arts",
+ "subjects": [
+ {
+ "name": "History",
+ "questions": [
+ {
+ "id": 1,
+ "question": "Who wrote the Declaration of Independence?",
+ "options": ["George Washington", "Thomas Jefferson", "Abraham Lincoln", "John Adams"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 2,
+ "question": "What was a main cause of the American Civil War?",
+ "options": ["The War of 1812", "The Gold Rush", "Disagreements over slavery", "Industrialization"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 3,
+ "question": "What ancient civilization built the pyramids of Egypt?",
+ "options": ["Romans", "Mesopotamians", "Egyptians", "Greeks"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 4,
+ "question": "What is the purpose of the U.S. Constitution?",
+ "options": ["To declare independence", "To organize the government", "To end the Civil War", "To buy land from France"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 5,
+ "question": "Which two men were key figures in the American Revolution?",
+ "options": ["Jefferson and Franklin", "Lincoln and Davis", "Edison and Ford", "Washington and King George"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 6,
+ "question": "Which war occurred from 1939 to 1945?",
+ "options": ["Korean War", "World War I", "Vietnam War", "World War II"],
+ "correctAnswer": 3
+ },
+ {
+ "id": 7,
+ "question": "When did the Great Depression take place?",
+ "options": ["1910s", "1920s", "1930s", "1950s"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 8,
+ "question": "What was the goal of the Civil Rights Movement?",
+ "options": ["Equal rights for African Americans", "Independence from Britain", "End World War II", "Expand U.S. territory"],
+ "correctAnswer": 0
+ }
+ ]
+ },
+ {
+ "name": "Geography",
+ "questions": [
+ {
+ "id": 9,
+ "question": "Which of the following is NOT a continent?",
+ "options": ["Africa", "Europe", "Greenland", "Australia"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 10,
+ "question": "How many oceans are there?",
+ "options": ["3", "4", "5", "6"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 11,
+ "question": "What is the longest river in the United States?",
+ "options": ["Mississippi", "Missouri", "Colorado", "Ohio"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 12,
+ "question": "Which hemisphere is the U.S. in?",
+ "options": ["Southern", "Eastern", "Western", "Central"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 13,
+ "question": "What is the capital of Canada?",
+ "options": ["Toronto", "Montreal", "Vancouver", "Ottawa"],
+ "correctAnswer": 3
+ },
+ {
+ "id": 14,
+ "question": "Which mountain range is in the western U.S.?",
+ "options": ["Appalachians", "Andes", "Rockies", "Alps"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 15,
+ "question": "Which country borders the U.S. to the south?",
+ "options": ["Canada", "Cuba", "Mexico", "Brazil"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 16,
+ "question": "Lines of latitude measure distance from what?",
+ "options": ["The Equator", "The poles", "The International Date Line", "Prime Meridian"],
+ "correctAnswer": 0
+ }
+ ]
+ },
+ {
+ "name": "Science",
+ "questions": [
+ {
+ "id": 17,
+ "question": "Which is NOT a state of matter?",
+ "options": ["Solid", "Plasma", "Light", "Gas"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 18,
+ "question": "What is the basic unit of life?",
+ "options": ["Organ", "Tissue", "Cell", "Gene"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 19,
+ "question": "Which planet is closest to the sun?",
+ "options": ["Venus", "Mars", "Earth", "Mercury"],
+ "correctAnswer": 3
+ },
+ {
+ "id": 20,
+ "question": "What gas do humans need to breathe?",
+ "options": ["Oxygen", "Nitrogen", "Hydrogen", "Carbon Dioxide"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 21,
+ "question": "What process do plants use to make food?",
+ "options": ["Germination", "Fermentation", "Photosynthesis", "Respiration"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 22,
+ "question": "What is Newton's First Law also called?",
+ "options": ["Law of Gravity", "Law of Energy", "Law of Inertia", "Law of Attraction"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 23,
+ "question": "Which part of an atom is positively charged?",
+ "options": ["Electron", "Neutron", "Proton", "Nucleus"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 24,
+ "question": "Which organ pumps blood?",
+ "options": ["Brain", "Lungs", "Heart", "Liver"],
+ "correctAnswer": 2
+ }
+ ]
+ },
+ {
+ "name": "Mathematics",
+ "questions": [
+ {
+ "id": 25,
+ "question": "Simplify: 4x + 3x - 2 =",
+ "options": ["7x - 2", "12x", "x + 2", "4x - 1"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 26,
+ "question": "Area of a rectangle with length 5 and width 3 is:",
+ "options": ["8", "15", "18", "10"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 27,
+ "question": "Solve for x: 2x + 5 = 11",
+ "options": ["6", "3", "8", "4"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 28,
+ "question": "Value of 3² + 4² is:",
+ "options": ["12", "25", "49", "19"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 29,
+ "question": "Convert 0.75 to a fraction:",
+ "options": ["1/4", "2/5", "3/4", "4/5"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 30,
+ "question": "Slope between (0, 0) and (2, 4) is:",
+ "options": ["1", "2", "4", "0.5"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 31,
+ "question": "Volume of a cube with side length 3 is:",
+ "options": ["6", "9", "27", "18"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 32,
+ "question": "Which is a prime number?",
+ "options": ["15", "11", "12", "21"],
+ "correctAnswer": 1
+ }
+ ]
+ },
+ {
+ "name": "Language Arts",
+ "questions": [
+ {
+ "id": 33,
+ "question": "Subject and predicate: 'The dog barked loudly.'",
+ "options": [
+ "Subject: loudly, Predicate: barked",
+ "Subject: The dog, Predicate: barked loudly",
+ "Subject: The, Predicate: dog barked",
+ "Subject: barked, Predicate: dog loudly"
+ ],
+ "correctAnswer": 1
+ },
+ {
+ "id": 34,
+ "question": "A metaphor is:",
+ "options": [
+ "A comparison using 'like' or 'as'",
+ "An exaggeration",
+ "A word that imitates sound",
+ "A direct comparison without 'like' or 'as'"
+ ],
+ "correctAnswer": 3
+ },
+ {
+ "id": 35,
+ "question": "Which is a run-on sentence?",
+ "options": [
+ "I like ice cream it is my favorite dessert.",
+ "I like ice cream.",
+ "My favorite dessert is ice cream.",
+ "Ice cream is good."
+ ],
+ "correctAnswer": 0
+ },
+ {
+ "id": 36,
+ "question": "Correct this sentence: 'Their going to the mall later.'",
+ "options": [
+ "They're going to the mall later.",
+ "There going to the mall later.",
+ "Theres going to the mall later.",
+ "They're mall going later."
+ ],
+ "correctAnswer": 0
+ },
+ {
+ "id": 37,
+ "question": "The theme of a story is:",
+ "options": ["The main character", "The plot", "The central message or lesson", "The setting"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 38,
+ "question": "Who wrote Romeo and Juliet?",
+ "options": ["Charles Dickens", "William Shakespeare", "Mark Twain", "J.K. Rowling"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 39,
+ "question": "Simile vs. Metaphor:",
+ "options": [
+ "Simile is more poetic",
+ "Simile uses 'like' or 'as,' metaphor does not",
+ "Similes compare animals only",
+ "Metaphors are always longer"
+ ],
+ "correctAnswer": 1
+ },
+ {
+ "id": 40,
+ "question": "'She had walked to school before the bell rang.' Verb tense?",
+ "options": ["Simple past", "Present perfect", "Past perfect", "Future perfect"],
+ "correctAnswer": 2
+ }
+ ]
+ }
+ ]
+ }
public/assessments/9th-grade-assessment-grok.json +271 -0
@@ @@ -0,0 +1,271 @@
+ {
+ "title": "9th Grade Readiness Assessment (Grok)",
+ "description": "Test your knowledge in History, Geography, Science, Math, and Language Arts",
+ "subjects": [
+ {
+ "name": "History",
+ "questions": [
+ {
+ "id": 1,
+ "question": "Who was the first President of the United States?",
+ "options": ["Abraham Lincoln", "George Washington", "Thomas Jefferson", "John Adams"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 2,
+ "question": "What was the main cause of the American Civil War?",
+ "options": ["Taxation", "Slavery", "Trade disputes", "Religious differences"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 3,
+ "question": "In which year did the Declaration of Independence get adopted?",
+ "options": ["1776", "1789", "1800", "1750"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 4,
+ "question": "Which country was NOT part of the Axis powers in WWII?",
+ "options": ["Germany", "Japan", "Italy", "France"],
+ "correctAnswer": 3
+ },
+ {
+ "id": 5,
+ "question": "What was the purpose of the Lewis and Clark expedition?",
+ "options": ["To explore the Louisiana Purchase", "To fight Native Americans", "To establish a new government", "To mine gold"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 6,
+ "question": "Who wrote the Emancipation Proclamation?",
+ "options": ["Abraham Lincoln", "Ulysses S. Grant", "Frederick Douglass", "Harriet Tubman"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 7,
+ "question": "What was the primary goal of the Women's Suffrage Movement?",
+ "options": ["Equal pay", "Voting rights", "Property ownership", "Education reform"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 8,
+ "question": "Which event marked the beginning of the Great Depression?",
+ "options": ["Stock Market Crash of 1929", "World War I", "Dust Bowl", "New Deal"],
+ "correctAnswer": 0
+ }
+ ]
+ },
+ {
+ "name": "Geography",
+ "questions": [
+ {
+ "id": 9,
+ "question": "What is the capital of the United States?",
+ "options": ["New York", "Washington, D.C.", "Florida", "California"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 10,
+ "question": "Which continent is home to the Amazon Rainforest?",
+ "options": ["Africa", "South America", "Asia", "Australia"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 11,
+ "question": "What is the longest river in the world?",
+ "options": ["Nile", "Amazon", "Mississippi", "Yangtze"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 12,
+ "question": "Which country has the largest population?",
+ "options": ["United States", "India", "Russia", "Brazil"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 13,
+ "question": "What is the name of the largest ocean on Earth?",
+ "options": ["Atlantic", "Indian", "Pacific", "Arctic"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 14,
+ "question": "Which US state is known as the \"Sunshine State\"?",
+ "options": ["California", "Florida", "Texas", "Arizona"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 15,
+ "question": "What type of map shows elevation and landforms?",
+ "options": ["Political", "Physical", "Thematic", "Road"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 16,
+ "question": "Which country is both a continent and an island?",
+ "options": ["Australia", "Greenland", "Antarctica", "Iceland"],
+ "correctAnswer": 0
+ }
+ ]
+ },
+ {
+ "name": "Science",
+ "questions": [
+ {
+ "id": 17,
+ "question": "What is the chemical symbol for water?",
+ "options": ["H2O", "CO2", "O2", "NaCl"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 18,
+ "question": "What planet is known as the Red Planet?",
+ "options": ["Jupiter", "Mars", "Venus", "Mercury"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 19,
+ "question": "What is the primary source of energy for Earth's climate system?",
+ "options": ["Wind", "Sun", "Moon", "Geothermal"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 20,
+ "question": "What is the process by which plants make food?",
+ "options": ["Respiration", "Photosynthesis", "Decomposition", "Fermentation"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 21,
+ "question": "What is the basic unit of life?",
+ "options": ["Atom", "Cell", "Molecule", "Organ"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 22,
+ "question": "Which force keeps planets in orbit around the Sun?",
+ "options": ["Magnetism", "Gravity", "Friction", "Electricity"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 23,
+ "question": "What is the freezing point of water in Celsius?",
+ "options": ["0", "100", "32", "-10"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 24,
+ "question": "What type of rock is formed from cooled lava?",
+ "options": ["Sedimentary", "Igneous", "Metamorphic", "Fossilized"],
+ "correctAnswer": 1
+ }
+ ]
+ },
+ {
+ "name": "Mathematics",
+ "questions": [
+ {
+ "id": 25,
+ "question": "What is 5 squared?",
+ "options": ["10", "15", "25", "50"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 26,
+ "question": "Solve: 2x + 4 = 10",
+ "options": ["x = 2", "x = 3", "x = 4", "x = 5"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 27,
+ "question": "What is the perimeter of a rectangle with length 6 and width 4?",
+ "options": ["16", "20", "24", "28"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 28,
+ "question": "What is 3/4 as a decimal?",
+ "options": ["0.25", "0.5", "0.75", "1.0"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 29,
+ "question": "What is the value of π (pi) to two decimal places?",
+ "options": ["3.12", "3.14", "3.16", "3.18"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 30,
+ "question": "If a shirt costs $20 and is on sale for 25% off, what is the sale price?",
+ "options": ["$12", "$15", "$16", "$18"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 31,
+ "question": "What is the slope of the line y = 2x + 3?",
+ "options": ["2", "3", "-2", "-3"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 32,
+ "question": "What is the area of a circle with radius 5? (Use π = 3.14)",
+ "options": ["15.7", "31.4", "78.5", "157"],
+ "correctAnswer": 2
+ }
+ ]
+ },
+ {
+ "name": "Language Arts",
+ "questions": [
+ {
+ "id": 33,
+ "question": "What is a synonym for \"happy\"?",
+ "options": ["Sad", "Joyful", "Angry", "Tired"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 34,
+ "question": "What is the main idea of a story called?",
+ "options": ["Theme", "Plot", "Setting", "Character"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 35,
+ "question": "Which punctuation mark is used to show possession?",
+ "options": ["Comma", "Apostrophe", "Period", "Exclamation point"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 36,
+ "question": "What is the past tense of \"run\"?",
+ "options": ["Runned", "Ran", "Running", "Runs"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 37,
+ "question": "What is a person, place, or thing called in grammar?",
+ "options": ["Verb", "Adjective", "Noun", "Adverb"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 38,
+ "question": "Who wrote \"The Outsiders\"?",
+ "options": ["J.K. Rowling", "S.E. Hinton", "Mark Twain", "Harper Lee"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 39,
+ "question": "What is it called when a story is told from the \"I\" perspective?",
+ "options": ["First person", "Second person", "Third person", "Omniscient"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 40,
+ "question": "What is an example of a metaphor?",
+ "options": ["The wind howled like a wolf.", "Her smile was a ray of sunshine.", "The stars twinkled brightly.", "He ran as fast as he could."],
+ "correctAnswer": 1
+ }
+ ]
+ }
+ ]
+ }
public/vite.svg +1 -0
@@ @@ -0,0 +1 @@
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
src/App.vue +26 -0
@@ @@ -0,0 +1,26 @@
+ <script setup>
+ </script>
+
+ <template>
+ <div id="app">
+ <router-view />
+ </div>
+ </template>
+
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ background-color: #f5f5f5;
+ color: #333;
+ }
+
+ #app {
+ min-height: 100vh;
+ }
+ </style>
\ No newline at end of file
src/assets/vue.svg +1 -0
@@ @@ -0,0 +1 @@
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
\ No newline at end of file
src/components/HelloWorld.vue +43 -0
@@ @@ -0,0 +1,43 @@
+ <script setup>
+ import { ref } from 'vue'
+
+ defineProps({
+ msg: String,
+ })
+
+ const count = ref(0)
+ </script>
+
+ <template>
+ <h1>{{ msg }}</h1>
+
+ <div class="card">
+ <button type="button" @click="count++">count is {{ count }}</button>
+ <p>
+ Edit
+ <code>components/HelloWorld.vue</code> to test HMR
+ </p>
+ </div>
+
+ <p>
+ Check out
+ <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
+ >create-vue</a
+ >, the official Vue + Vite starter
+ </p>
+ <p>
+ Learn more about IDE Support for Vue in the
+ <a
+ href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
+ target="_blank"
+ >Vue Docs Scaling up Guide</a
+ >.
+ </p>
+ <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
+ </template>
+
+ <style scoped>
+ .read-the-docs {
+ color: #888;
+ }
+ </style>
src/main.js +6 -0
@@ @@ -0,0 +1,6 @@
+ import { createApp } from 'vue'
+ import './style.css'
+ import App from './App.vue'
+ import router from './router'
+
+ createApp(App).use(router).mount('#app')
src/router/index.js +21 -0
@@ @@ -0,0 +1,21 @@
+ import { createRouter, createWebHistory } from 'vue-router'
+ import Home from '../views/Home.vue'
+ import Assessment from '../views/Assessment.vue'
+
+ const router = createRouter({
+ history: createWebHistory(),
+ routes: [
+ {
+ path: '/',
+ name: 'home',
+ component: Home
+ },
+ {
+ path: '/:assessment',
+ name: 'assessment',
+ component: Assessment
+ }
+ ]
+ })
+
+ export default router
\ No newline at end of file
src/style.css +29 -0
@@ @@ -0,0 +1,29 @@
+ :root {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color: #333;
+ background-color: #f5f5f5;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ }
+
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ margin: 0;
+ min-width: 320px;
+ min-height: 100vh;
+ }
+
+ #app {
+ min-height: 100vh;
+ }
\ No newline at end of file
src/views/Assessment.vue +400 -0
@@ @@ -0,0 +1,400 @@
+ <template>
+ <div class="assessment-container" v-if="assessmentData">
+ <div class="assessment-header">
+ <h1>{{ assessmentData.title }}</h1>
+ <div class="progress-bar">
+ <div
+ class="progress-fill"
+ :style="{ width: `${(currentQuestionIndex / totalQuestions) * 100}%` }"
+ ></div>
+ </div>
+ <p class="progress-text">
+ Question {{ currentQuestionIndex + 1 }} of {{ totalQuestions }}
+ </p>
+ </div>
+
+ <div v-if="!showResults" class="quiz-content">
+ <div class="question-group">
+ <h2>{{ currentSubject.name }}</h2>
+ <div
+ v-for="(question, index) in currentPageQuestions"
+ :key="question.id"
+ class="question"
+ >
+ <h3>{{ getQuestionNumber(index) }}. {{ question.question }}</h3>
+ <div class="options">
+ <label
+ v-for="(option, optionIndex) in question.options"
+ :key="optionIndex"
+ class="option"
+ >
+ <input
+ type="radio"
+ :name="`q${question.id}`"
+ :value="optionIndex"
+ v-model="answers[question.id]"
+ />
+ <span>{{ option }}</span>
+ </label>
+ </div>
+ </div>
+ </div>
+
+ <div class="navigation">
+ <button
+ @click="previousPage"
+ :disabled="currentPage === 0"
+ class="nav-button"
+ >
+ Previous
+ </button>
+ <button
+ v-if="!isLastPage"
+ @click="nextPage"
+ class="nav-button primary"
+ :disabled="!currentPageAnswered"
+ >
+ Next
+ </button>
+ <button
+ v-else
+ @click="submitQuiz"
+ class="nav-button primary"
+ :disabled="!allQuestionsAnswered"
+ >
+ Submit Quiz
+ </button>
+ </div>
+ </div>
+
+ <div v-else class="results">
+ <h2>Quiz Results</h2>
+ <div class="score-card">
+ <h3>Total Score: {{ score }} / {{ totalQuestions }}</h3>
+ <p>{{ ((score / totalQuestions) * 100).toFixed(1) }}%</p>
+ </div>
+
+ <div class="subject-scores">
+ <h3>Score by Subject:</h3>
+ <div v-for="subject in subjectScores" :key="subject.name" class="subject-score">
+ <span>{{ subject.name }}:</span>
+ <span>{{ subject.score }} / {{ subject.total }} ({{ subject.percentage }}%)</span>
+ </div>
+ </div>
+
+ <router-link to="/" class="nav-button primary">
+ Back to Home
+ </router-link>
+ </div>
+ </div>
+
+ <div v-else class="loading">
+ Loading assessment...
+ </div>
+ </template>
+
+ <script setup>
+ import { ref, computed, onMounted } from 'vue'
+ import { useRoute, useRouter } from 'vue-router'
+
+ const route = useRoute()
+ const router = useRouter()
+
+ const assessmentData = ref(null)
+ const answers = ref({})
+ const currentPage = ref(0)
+ const showResults = ref(false)
+ const score = ref(0)
+ const subjectScores = ref([])
+
+ const questionsPerPage = 5
+
+ const allQuestions = computed(() => {
+ if (!assessmentData.value) return []
+ return assessmentData.value.subjects.flatMap(subject =>
+ subject.questions.map(q => ({ ...q, subject: subject.name }))
+ )
+ })
+
+ const totalQuestions = computed(() => allQuestions.value.length)
+
+ const currentPageQuestions = computed(() => {
+ const start = currentPage.value * questionsPerPage
+ const end = start + questionsPerPage
+ return allQuestions.value.slice(start, end)
+ })
+
+ const currentQuestionIndex = computed(() => currentPage.value * questionsPerPage)
+
+ const currentSubject = computed(() => {
+ if (currentPageQuestions.value.length === 0) return { name: '' }
+ return { name: currentPageQuestions.value[0].subject }
+ })
+
+ const isLastPage = computed(() => {
+ return (currentPage.value + 1) * questionsPerPage >= totalQuestions.value
+ })
+
+ const currentPageAnswered = computed(() => {
+ return currentPageQuestions.value.every(q => answers.value[q.id] !== undefined)
+ })
+
+ const allQuestionsAnswered = computed(() => {
+ return allQuestions.value.every(q => answers.value[q.id] !== undefined)
+ })
+
+ const getQuestionNumber = (index) => {
+ return currentPage.value * questionsPerPage + index + 1
+ }
+
+ const previousPage = () => {
+ if (currentPage.value > 0) {
+ currentPage.value--
+ }
+ }
+
+ const nextPage = () => {
+ if (!isLastPage.value) {
+ currentPage.value++
+ }
+ }
+
+ const submitQuiz = () => {
+ calculateScore()
+ showResults.value = true
+ }
+
+ const calculateScore = () => {
+ let totalScore = 0
+ const subjectData = {}
+
+ assessmentData.value.subjects.forEach(subject => {
+ subjectData[subject.name] = { correct: 0, total: 0 }
+
+ subject.questions.forEach(question => {
+ subjectData[subject.name].total++
+ if (answers.value[question.id] === question.correctAnswer) {
+ totalScore++
+ subjectData[subject.name].correct++
+ }
+ })
+ })
+
+ score.value = totalScore
+
+ subjectScores.value = Object.entries(subjectData).map(([name, data]) => ({
+ name,
+ score: data.correct,
+ total: data.total,
+ percentage: ((data.correct / data.total) * 100).toFixed(1)
+ }))
+ }
+
+ onMounted(async () => {
+ const assessmentId = route.params.assessment
+
+ try {
+ // Check if it's an uploaded assessment first
+ if (assessmentId.startsWith('uploaded-')) {
+ const storedData = localStorage.getItem(`assessment-${assessmentId}`)
+ if (storedData) {
+ assessmentData.value = JSON.parse(storedData)
+ return
+ }
+ }
+
+ // Otherwise, try to fetch from public folder
+ const response = await fetch(`/assessments/${assessmentId}.json`)
+ if (!response.ok) {
+ throw new Error('Assessment not found')
+ }
+ assessmentData.value = await response.json()
+ } catch (error) {
+ console.error('Error loading assessment:', error)
+ router.push('/')
+ }
+ })
+ </script>
+
+ <style scoped>
+ .assessment-container {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 2rem;
+ }
+
+ .assessment-header {
+ text-align: center;
+ margin-bottom: 2rem;
+ }
+
+ .assessment-header h1 {
+ color: #2c3e50;
+ margin-bottom: 1rem;
+ }
+
+ .progress-bar {
+ width: 100%;
+ height: 8px;
+ background-color: #e0e0e0;
+ border-radius: 4px;
+ overflow: hidden;
+ margin-bottom: 0.5rem;
+ }
+
+ .progress-fill {
+ height: 100%;
+ background-color: #42b883;
+ transition: width 0.3s ease;
+ }
+
+ .progress-text {
+ color: #666;
+ font-size: 0.9rem;
+ }
+
+ .quiz-content {
+ background: white;
+ padding: 2rem;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ }
+
+ .question-group h2 {
+ color: #2c3e50;
+ margin-bottom: 1.5rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 2px solid #e0e0e0;
+ }
+
+ .question {
+ margin-bottom: 2rem;
+ }
+
+ .question:last-child {
+ margin-bottom: 0;
+ }
+
+ .question h3 {
+ color: #333;
+ margin-bottom: 1rem;
+ font-size: 1.1rem;
+ }
+
+ .options {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ }
+
+ .option {
+ display: flex;
+ align-items: center;
+ padding: 0.75rem 1rem;
+ background: #f8f9fa;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ }
+
+ .option:hover {
+ background: #e9ecef;
+ }
+
+ .option input[type="radio"] {
+ margin-right: 0.75rem;
+ }
+
+ .navigation {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 2rem;
+ padding-top: 2rem;
+ border-top: 1px solid #e0e0e0;
+ }
+
+ .nav-button {
+ padding: 0.75rem 1.5rem;
+ border: none;
+ border-radius: 4px;
+ font-size: 1rem;
+ cursor: pointer;
+ transition: all 0.2s;
+ background: #e0e0e0;
+ color: #333;
+ }
+
+ .nav-button:hover:not(:disabled) {
+ background: #d0d0d0;
+ }
+
+ .nav-button.primary {
+ background: #42b883;
+ color: white;
+ }
+
+ .nav-button.primary:hover:not(:disabled) {
+ background: #35a372;
+ }
+
+ .nav-button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ .results {
+ background: white;
+ padding: 2rem;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ text-align: center;
+ }
+
+ .results h2 {
+ color: #2c3e50;
+ margin-bottom: 2rem;
+ }
+
+ .score-card {
+ background: #f0f9ff;
+ padding: 2rem;
+ border-radius: 8px;
+ margin-bottom: 2rem;
+ }
+
+ .score-card h3 {
+ color: #2c3e50;
+ margin-bottom: 0.5rem;
+ font-size: 1.5rem;
+ }
+
+ .score-card p {
+ color: #42b883;
+ font-size: 2rem;
+ font-weight: bold;
+ margin: 0;
+ }
+
+ .subject-scores {
+ text-align: left;
+ margin-bottom: 2rem;
+ }
+
+ .subject-scores h3 {
+ color: #2c3e50;
+ margin-bottom: 1rem;
+ }
+
+ .subject-score {
+ display: flex;
+ justify-content: space-between;
+ padding: 0.5rem 0;
+ border-bottom: 1px solid #e0e0e0;
+ }
+
+ .loading {
+ text-align: center;
+ padding: 4rem;
+ color: #666;
+ }
+ </style>
\ No newline at end of file
src/views/Home.vue +282 -0
@@ @@ -0,0 +1,282 @@
+ <template>
+ <div class="home">
+ <h1>Student Assessment Portal</h1>
+ <p>Select an assessment to begin:</p>
+
+ <div class="assessment-list">
+ <router-link
+ v-for="assessment in allAssessments"
+ :key="assessment.id"
+ :to="`/${assessment.id}`"
+ class="assessment-card"
+ >
+ <h3>{{ assessment.title }}</h3>
+ <p>{{ assessment.description }}</p>
+ <span v-if="assessment.isUploaded" class="uploaded-badge">Uploaded</span>
+ </router-link>
+ </div>
+
+ <div class="upload-section">
+ <h2>Upload Custom Assessment</h2>
+ <p>Upload a JSON file to create a custom assessment</p>
+ <div class="upload-container">
+ <input
+ type="file"
+ ref="fileInput"
+ @change="handleFileUpload"
+ accept=".json"
+ id="file-upload"
+ class="file-input"
+ />
+ <label for="file-upload" class="file-label">
+ Choose JSON File
+ </label>
+ </div>
+ <div v-if="uploadError" class="error-message">
+ {{ uploadError }}
+ </div>
+ </div>
+ </div>
+ </template>
+
+ <script setup>
+ import { ref, computed, onMounted } from 'vue'
+
+ const availableAssessments = ref([
+ {
+ id: '9th-grade-assessment-chatgpt',
+ title: '9th Grade Readiness Assessment (ChatGPT)',
+ description: 'Test your knowledge in History, Geography, Science, Math, and Language Arts'
+ },
+ {
+ id: '9th-grade-assessment-grok',
+ title: '9th Grade Readiness Assessment (Grok)',
+ description: 'Alternative 9th grade assessment with different questions'
+ },
+ {
+ id: '10th-grade-assessment',
+ title: '10th Grade Readiness Assessment',
+ description: 'Sample assessment for 10th grade students'
+ }
+ ])
+
+ const uploadedAssessments = ref([])
+ const uploadError = ref('')
+ const fileInput = ref(null)
+
+ const allAssessments = computed(() => {
+ return [...availableAssessments.value, ...uploadedAssessments.value]
+ })
+
+ const loadUploadedAssessments = () => {
+ const stored = localStorage.getItem('uploadedAssessments')
+ if (stored) {
+ try {
+ uploadedAssessments.value = JSON.parse(stored)
+ } catch (e) {
+ console.error('Failed to load uploaded assessments:', e)
+ }
+ }
+ }
+
+ const validateAssessment = (data) => {
+ if (!data.title || !data.description || !data.subjects) {
+ throw new Error('Assessment must have title, description, and subjects')
+ }
+
+ if (!Array.isArray(data.subjects) || data.subjects.length === 0) {
+ throw new Error('Assessment must have at least one subject')
+ }
+
+ for (const subject of data.subjects) {
+ if (!subject.name || !subject.questions || !Array.isArray(subject.questions)) {
+ throw new Error('Each subject must have a name and questions array')
+ }
+
+ for (const question of subject.questions) {
+ if (!question.id || !question.question || !question.options || typeof question.correctAnswer !== 'number') {
+ throw new Error('Each question must have id, question text, options, and correctAnswer')
+ }
+
+ if (!Array.isArray(question.options) || question.options.length < 2) {
+ throw new Error('Each question must have at least 2 options')
+ }
+
+ if (question.correctAnswer < 0 || question.correctAnswer >= question.options.length) {
+ throw new Error('correctAnswer must be a valid index for the options array')
+ }
+ }
+ }
+
+ return true
+ }
+
+ const handleFileUpload = async (event) => {
+ uploadError.value = ''
+ const file = event.target.files[0]
+
+ if (!file) return
+
+ try {
+ const text = await file.text()
+ const data = JSON.parse(text)
+
+ validateAssessment(data)
+
+ const assessmentId = `uploaded-${Date.now()}`
+ const assessment = {
+ id: assessmentId,
+ title: data.title,
+ description: data.description,
+ isUploaded: true
+ }
+
+ // Store the full assessment data in localStorage
+ localStorage.setItem(`assessment-${assessmentId}`, JSON.stringify(data))
+
+ // Add to uploaded assessments list
+ uploadedAssessments.value.push(assessment)
+
+ // Save the list of uploaded assessments
+ localStorage.setItem('uploadedAssessments', JSON.stringify(uploadedAssessments.value))
+
+ // Clear the file input
+ if (fileInput.value) {
+ fileInput.value.value = ''
+ }
+
+ } catch (error) {
+ uploadError.value = error.message || 'Failed to parse JSON file'
+ console.error('Upload error:', error)
+ }
+ }
+
+ onMounted(() => {
+ loadUploadedAssessments()
+ })
+ </script>
+
+ <style scoped>
+ .home {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 2rem;
+ }
+
+ h1 {
+ color: #2c3e50;
+ margin-bottom: 1rem;
+ text-align: center;
+ }
+
+ p {
+ text-align: center;
+ margin-bottom: 2rem;
+ color: #666;
+ }
+
+ .assessment-list {
+ display: grid;
+ gap: 1rem;
+ }
+
+ .assessment-card {
+ display: block;
+ padding: 1.5rem;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ text-decoration: none;
+ color: inherit;
+ transition: transform 0.2s, box-shadow 0.2s;
+ }
+
+ .assessment-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+ }
+
+ .assessment-card h3 {
+ color: #2c3e50;
+ margin-bottom: 0.5rem;
+ }
+
+ .assessment-card p {
+ color: #666;
+ margin: 0;
+ text-align: left;
+ }
+
+ .assessment-card {
+ position: relative;
+ }
+
+ .uploaded-badge {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ background: #42b883;
+ color: white;
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ font-size: 0.75rem;
+ font-weight: bold;
+ }
+
+ .upload-section {
+ margin-top: 3rem;
+ padding: 2rem;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ }
+
+ .upload-section h2 {
+ color: #2c3e50;
+ margin-bottom: 0.5rem;
+ }
+
+ .upload-section p {
+ color: #666;
+ margin-bottom: 1.5rem;
+ }
+
+ .upload-container {
+ margin-bottom: 1rem;
+ }
+
+ .file-input {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0,0,0,0);
+ white-space: nowrap;
+ border: 0;
+ }
+
+ .file-label {
+ display: inline-block;
+ padding: 0.75rem 1.5rem;
+ background: #42b883;
+ color: white;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ font-weight: 500;
+ }
+
+ .file-label:hover {
+ background: #35a372;
+ }
+
+ .error-message {
+ color: #dc3545;
+ padding: 0.75rem;
+ background: #f8d7da;
+ border: 1px solid #f5c6cb;
+ border-radius: 4px;
+ }
+ </style>
vite.config.js +12 -0
@@ @@ -0,0 +1,12 @@
+ import { defineConfig } from 'vite'
+ import vue from '@vitejs/plugin-vue'
+
+ // https://vite.dev/config/
+ export default defineConfig({
+ plugins: [vue()],
+ base: '/',
+ build: {
+ outDir: 'dist',
+ assetsDir: 'assets'
+ }
+ })