docs: Update CLAUDE.md with comprehensive project guidance

Torey Heinz committed Jul 12, 2025
commit ac56d19955d1ea9ca847f0e3eb6d7b2826263228
Showing 12 changed files with 2033 additions and 9 deletions
.gitignore +1 -0
@@ @@ -22,3 +22,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?
+ .claude
CLAUDE.md +52 -9
@@ @@ -4,22 +4,26 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
- Quiz Craft is a Vue.js application for creating and taking AI-generated assessments. It features pre-built 9th-grade assessments and supports custom quiz creation through file upload or AI generation.
+ Quiz Craft is a Vue.js application for creating and taking AI-generated assessments. It features pre-built 9th and 10th-grade assessments and supports custom quiz creation through file upload, JSON paste, or AI generation.
## Architecture
- **Framework**: Vue 3 with Composition API (`<script setup>`)
- - **Build Tool**: Vite
- - **Router**: Vue Router 4
+ - **Build Tool**: Vite 7.0.4
+ - **Router**: Vue Router 4.5.1
- **Storage**: LocalStorage for uploaded assessments
- **Deployment**: Configured for Cloudflare Pages
+ - **Testing**: No testing framework currently installed
+ - **Linting**: No ESLint/Prettier configuration
## Key Files
- `src/views/Home.vue` - Landing page with assessment list and upload functionality
- `src/views/Assessment.vue` - Quiz interface with pagination and scoring
- `src/router/index.js` - Route definitions
+ - `src/App.vue` - Root component with header/branding
- `public/assessments/*.json` - Pre-built assessment files
+ - `public/_redirects` - SPA routing configuration for Cloudflare
## Development Commands
@@ @@ -27,10 +31,10 @@ Quiz Craft is a Vue.js application for creating and taking AI-generated assessme
# Install dependencies
npm install
- # Run development server
+ # Run development server (http://localhost:5173)
npm run dev
- # Build for production
+ # Build for production (outputs to dist/)
npm run build
# Preview production build
@@ @@ -50,17 +54,30 @@ npm run preview
"id": 1,
"question": "Question?",
"options": ["A", "B", "C", "D"],
- "correctAnswer": 0
+ "correctAnswer": 0,
+ "correctAnswerText": "Optional: The correct answer explanation"
}]
}]
}
```
+ **Note**: `correctAnswer` uses 0-based indexing (0 = first option)
- ### Adding New Features
+ ### Component Patterns
- Components use Vue 3 Composition API with `<script setup>`
- - Styling is scoped to components
+ - Styling is scoped to components using `<style scoped>`
- Navigation state managed with Vue Router
- Uploaded assessments stored in localStorage with `assessment-${id}` keys
+ - Router views use Suspense for loading states
+
+ ### Code Organization
+ ```
+ src/
+ ├── views/ # Page components
+ ├── components/ # Reusable components (empty, components are in views)
+ ├── router/ # Route configuration
+ ├── assets/ # Static assets
+ └── style.css # Global styles
+ ```
## Deployment
@@ @@ -68,10 +85,36 @@ Configured for Cloudflare Pages:
- Build command: `npm run build`
- Output directory: `dist`
- SPA routing handled by `public/_redirects`
+ - Repository: https://github.com/toreyheinz/quiz-craft
## Design System
- Primary color: #42b883 (Vue green)
+ - Secondary: #35495e (dark gray)
- Max content width: 800px
- Questions per page: 5
- - Logo: 48px height with 5px border radius
\ No newline at end of file
+ - Logo: 48px height with 5px border radius
+ - Mobile breakpoint: 600px
+ - Button hover: darken by ~10%
+
+ ## Current Features
+
+ 1. **Pre-built Assessments**: 9th/10th grade readiness tests
+ 2. **Custom Assessment Methods**:
+ - File upload (JSON)
+ - Paste JSON content
+ - AI generation guide with example prompts
+ 3. **Quiz Interface**:
+ - Paginated questions (5 per page)
+ - Skip functionality
+ - Progress tracking with visual bar
+ - Score display with subject breakdown
+ - Shows incorrect answers with correct solutions
+
+ ## Planned Enhancements
+
+ Based on `dev/plans/`, the roadmap includes:
+ 1. **User Authentication** - JWT-based with Cloudflare Workers
+ 2. **Database Integration** - Cloudflare D1 for persistent storage
+ 3. **AI Quiz Generation** - Cloudflare AI integration for premium users
+ 4. **Freemium Model** - Free manual upload, premium AI generation
\ No newline at end of file
dev/assements/history_assessment.json +151 -0
@@ @@ -0,0 +1,151 @@
+ {
+ "title": "World History Assessment",
+ "description": "This assessment covers key events, figures, and concepts in world history, suitable for high school students. It consists of 20 multiple-choice questions.",
+ "subjects": [
+ {
+ "name": "World History",
+ "questions": [
+ {
+ "id": 1,
+ "question": "Who painted the ceiling of the Sistine Chapel?",
+ "options": ["Leonardo da Vinci", "Michelangelo", "Raphael", "Donatello"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 2,
+ "question": "Julius Caesar was assassinated in which year?",
+ "options": ["44 BCE", "27 BCE", "14 CE", "70 CE"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 3,
+ "question": "Who was the Greek historian known as the 'Father of History'?",
+ "options": ["Plato", "Aristotle", "Herodotus", "Thucydides"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 4,
+ "question": "Martin Luther's Ninety-Five Theses were primarily concerned with:",
+ "options": ["Corruption in the Church", "The sale of indulgences", "Papal infallibility", "Clerical celibacy"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 5,
+ "question": "Which document, signed in 1215, limited the powers of the English king?",
+ "options": ["The Magna Carta", "The English Bill of Rights", "The Petition of Right", "The Domesday Book"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 6,
+ "question": "The Roman Republic was founded in which year?",
+ "options": ["509 BCE", "753 BCE", "476 CE", "27 BCE"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 7,
+ "question": "Which civilization is credited with the invention of writing (cuneiform)?",
+ "options": ["Babylonian", "Sumerian", "Assyrian", "Persian"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 8,
+ "question": "Who was the Macedonian king who created a vast empire in the 4th century BCE?",
+ "options": ["Philip II", "Alexander the Great", "Pericles", "Leonidas"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 9,
+ "question": "What was the Peace of Augsburg (1555)?",
+ "options": [
+ "A treaty ending the Hundred Years' War",
+ "A settlement allowing princes to choose Lutheranism or Catholicism",
+ "An agreement uniting the German states",
+ "A papal bull condemning Protestantism"
+ ],
+ "correctAnswer": 1
+ },
+ {
+ "id": 10,
+ "question": "Charlemagne was crowned emperor in what year?",
+ "options": ["476 CE", "732 CE", "800 CE", "843 CE"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 11,
+ "question": "Which emperor made Christianity the favored religion of the Roman Empire?",
+ "options": ["Constantine", "Nero", "Augustus", "Diocletian"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 12,
+ "question": "What was the significance of the Edict of Nantes (1598)?",
+ "options": [
+ "It ended the Thirty Years' War",
+ "It legalized Calvinism in the Netherlands",
+ "It granted religious freedom to French Protestants",
+ "It established Anglicanism in England"
+ ],
+ "correctAnswer": 2
+ },
+ {
+ "id": 13,
+ "question": "Which war ended with the Treaty of Westphalia in 1648?",
+ "options": [
+ "The Thirty Years' War",
+ "The English Civil War",
+ "The War of the Roses",
+ "The Franco-Spanish War"
+ ],
+ "correctAnswer": 0
+ },
+ {
+ "id": 14,
+ "question": "Which explorer first circumnavigated the globe?",
+ "options": ["Christopher Columbus", "Vasco da Gama", "Ferdinand Magellan", "John Cabot"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 15,
+ "question": "Who wrote 'The City of God'?",
+ "options": ["Boethius", "St. Augustine", "Jerome", "Origen"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 16,
+ "question": "What was the primary effect of the Black Death on European society?",
+ "options": ["Strengthened the power of the Church", "Decreased wages", "Increased population", "Weakened feudalism"],
+ "correctAnswer": 3
+ },
+ {
+ "id": 17,
+ "question": "Which invention greatly contributed to the spread of Renaissance ideas?",
+ "options": ["The compass", "The printing press", "Gunpowder", "The telescope"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 18,
+ "question": "The primary language of the Byzantine Empire was:",
+ "options": ["Latin", "Greek", "Coptic", "Aramaic"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 19,
+ "question": "Which war was fought between Athens and Sparta in the 5th century BCE?",
+ "options": ["Trojan War", "Peloponnesian War", "Punic War", "Greco-Persian War"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 20,
+ "question": "What event sparked the First Crusade?",
+ "options": [
+ "The fall of Constantinople",
+ "The Council of Clermont",
+ "The capture of Jerusalem",
+ "The excommunication of the Byzantine emperor"
+ ],
+ "correctAnswer": 1
+ }
+ ]
+ }
+ ]
+ }
\ No newline at end of file
dev/assements/spanish_level2_assessment.json +146 -0
@@ @@ -0,0 +1,146 @@
+ {
+ "title": "Spanish Language Level 2 Assessment",
+ "description": "An intermediate-level assessment for Spanish learners focusing on vocabulary, grammar, reading comprehension, and cultural knowledge, with an emphasis on present and past tenses and Hispanic culture.",
+ "subjects": [
+ {
+ "name": "Vocabulary",
+ "questions": [
+ {
+ "id": 1,
+ "question": "What is the Spanish word for 'book'?",
+ "options": ["Libro", "Coche", "Mesa", "Sol"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 2,
+ "question": "How do you say 'to travel' in Spanish?",
+ "options": ["Comer", "Viajar", "Dormir", "Estudiar"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 3,
+ "question": "What does 'rápido' mean in English?",
+ "options": ["Slow", "Fast", "Big", "Small"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 4,
+ "question": "Which word means 'family'?",
+ "options": ["Amigo", "Casa", "Familia", "Trabajo"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 5,
+ "question": "What is the Spanish word for 'yesterday'?",
+ "options": ["Hoy", "Mañana", "Ayer", "Ahora"],
+ "correctAnswer": 2
+ }
+ ]
+ },
+ {
+ "name": "Grammar",
+ "questions": [
+ {
+ "id": 6,
+ "question": "What is the correct present tense conjugation of 'hablar' for 'yo'?",
+ "options": ["Hablas", "Hablo", "Habla", "Hablamos"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 7,
+ "question": "Which is the correct past tense (preterite) of 'comer' for 'tú'?",
+ "options": ["Comiste", "Comió", "Comí", "Comimos"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 8,
+ "question": "Choose the correct form of 'ser' in the sentence: 'Ella ___ muy amable.'",
+ "options": ["Es", "Eres", "Son", "Somos"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 9,
+ "question": "What is the correct past tense (imperfect) of 'vivir' for 'nosotros'?",
+ "options": ["Vivimos", "Vivíamos", "Vivisteis", "Vivieron"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 10,
+ "question": "Which pronoun replaces 'María' in 'Doy el libro a María'?",
+ "options": ["Lo", "La", "Le", "Les"],
+ "correctAnswer": 2
+ }
+ ]
+ },
+ {
+ "name": "Reading Comprehension",
+ "questions": [
+ {
+ "id": 11,
+ "question": "Read: 'Ayer, Juan fue al mercado y compró frutas.' What did Juan buy?",
+ "options": ["Clothes", "Fruits", "Books", "Toys"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 12,
+ "question": "Read: 'María siempre lee un libro antes de dormir.' When does María read?",
+ "options": ["In the morning", "At noon", "Before sleeping", "After eating"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 13,
+ "question": "Read: 'El año pasado viajamos a México.' Where did they travel?",
+ "options": ["Spain", "Argentina", "Mexico", "Peru"],
+ "correctAnswer": 2
+ },
+ {
+ "id": 14,
+ "question": "Read: 'La fiesta comienza a las ocho.' What time does the party start?",
+ "options": ["Seven", "Eight", "Nine", "Ten"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 15,
+ "question": "Read: 'Pedro estudia español porque quiere hablar con sus amigos.' Why does Pedro study Spanish?",
+ "options": ["To travel", "To work", "To talk with friends", "To read books"],
+ "correctAnswer": 2
+ }
+ ]
+ },
+ {
+ "name": "Cultural Knowledge",
+ "questions": [
+ {
+ "id": 16,
+ "question": "What is a famous Mexican holiday celebrating the dead?",
+ "options": ["Día de los Muertos", "Cinco de Mayo", "Día de la Independencia", "Navidad"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 17,
+ "question": "Which dance is associated with Argentina?",
+ "options": ["Salsa", "Tango", "Flamenco", "Merengue"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 18,
+ "question": "What is a traditional Spanish dish made with rice and seafood?",
+ "options": ["Tacos", "Paella", "Empanadas", "Ceviche"],
+ "correctAnswer": 1
+ },
+ {
+ "id": 19,
+ "question": "Who is a famous Colombian singer known for 'Hips Don’t Lie'?",
+ "options": ["Shakira", "Jennifer Lopez", "Gloria Estefan", "Selena"],
+ "correctAnswer": 0
+ },
+ {
+ "id": 20,
+ "question": "What is the capital city of Peru?",
+ "options": ["Buenos Aires", "Lima", "Santiago", "Bogotá"],
+ "correctAnswer": 1
+ }
+ ]
+ }
+ ]
+ }
\ No newline at end of file
dev/design/quiz-craft-logo.jpg +0 -0
dev/notes/CLAUDE.md +47 -0
@@ @@ -0,0 +1,47 @@
+ # CLAUDE.md
+
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+ ## Project Overview
+
+ SelfQuiz is a collection of standalone HTML files for 9th-grade readiness assessments. Each file is a self-contained quiz application with embedded CSS and JavaScript.
+
+ ## Architecture
+
+ The project contains three quiz implementations:
+ - `index.html` - Compact implementation with 40 questions
+ - `chatgpt.html` - Identical to index.html
+ - `grok.html` - Expanded implementation with traditional form structure
+
+ All files follow the same pattern:
+ 1. Single HTML file with inline CSS and JavaScript
+ 2. Client-side scoring (answers visible in source)
+ 3. No external dependencies or build process
+ 4. Questions stored in JavaScript data structures
+
+ ## Development Commands
+
+ - **Run**: Open any HTML file directly in a web browser
+ - **Test**: Manual browser testing only
+ - **Deploy**: Copy HTML files to web server
+
+ ## Key Implementation Details
+
+ ### Question Format (index.html/chatgpt.html)
+ Questions use object structure:
+ ```javascript
+ { q: "question text", a: correctAnswerIndex, options: ["opt1", "opt2", "opt3", "opt4"] }
+ ```
+
+ ### Question Format (grok.html)
+ Questions use traditional HTML forms with answer key object:
+ ```javascript
+ correctAnswers = { q1: 'b', q2: 'a', ... }
+ ```
+
+ ### Subject Coverage
+ - Questions 1-8: History
+ - Questions 9-16: Geography
+ - Questions 17-24: Science
+ - Questions 25-32: Mathematics
+ - Questions 33-40: English Language Arts
\ No newline at end of file
dev/notes/chatgpt.html +95 -0
@@ @@ -0,0 +1,95 @@
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>9th Grade Readiness Quiz</title>
+ <style>
+ body { font-family: Arial, sans-serif; margin: 2rem; }
+ h1, h2 { color: #2c3e50; }
+ .question { margin-bottom: 1.5rem; }
+ .question h3 { margin-bottom: 0.5rem; }
+ .result { margin-top: 2rem; font-size: 1.2rem; font-weight: bold; color: green; }
+ button { margin-top: 2rem; padding: 10px 20px; font-size: 1rem; }
+ </style>
+ </head>
+ <body>
+ <h1>9th Grade Readiness Quiz</h1>
+ <form id="quizForm">
+ <div id="questions"></div>
+ <button type="submit">Submit Quiz</button>
+ </form>
+ <div class="result" id="result"></div>
+
+ <script>
+ const questions = [
+ { q: "Who wrote the Declaration of Independence?", a: 1, options: ["George Washington", "Thomas Jefferson", "Abraham Lincoln", "John Adams"] },
+ { q: "What was a main cause of the American Civil War?", a: 2, options: ["The War of 1812", "The Gold Rush", "Disagreements over slavery", "Industrialization"] },
+ { q: "What ancient civilization built the pyramids of Egypt?", a: 2, options: ["Romans", "Mesopotamians", "Egyptians", "Greeks"] },
+ { q: "What is the purpose of the U.S. Constitution?", a: 1, options: ["To declare independence", "To organize the government", "To end the Civil War", "To buy land from France"] },
+ { q: "Which two men were key figures in the American Revolution?", a: 3, options: ["Jefferson and Franklin", "Lincoln and Davis", "Edison and Ford", "Washington and King George"] },
+ { q: "Which war occurred from 1939 to 1945?", a: 3, options: ["Korean War", "World War I", "Vietnam War", "World War II"] },
+ { q: "When did the Great Depression take place?", a: 2, options: ["1910s", "1920s", "1930s", "1950s"] },
+ { q: "What was the goal of the Civil Rights Movement?", a: 1, options: ["Equal rights for African Americans", "Independence from Britain", "End World War II", "Expand U.S. territory"] },
+
+ { q: "Which of the following is NOT a continent?", a: 2, options: ["Africa", "Europe", "Greenland", "Australia"] },
+ { q: "How many oceans are there?", a: 2, options: ["3", "4", "5", "6"] },
+ { q: "What is the longest river in the United States?", a: 1, options: ["Mississippi", "Missouri", "Colorado", "Ohio"] },
+ { q: "Which hemisphere is the U.S. in?", a: 2, options: ["Southern", "Eastern", "Western", "Central"] },
+ { q: "What is the capital of Canada?", a: 3, options: ["Toronto", "Montreal", "Vancouver", "Ottawa"] },
+ { q: "Which mountain range is in the western U.S.?", a: 2, options: ["Appalachians", "Andes", "Rockies", "Alps"] },
+ { q: "Which country borders the U.S. to the south?", a: 2, options: ["Canada", "Cuba", "Mexico", "Brazil"] },
+ { q: "Lines of latitude measure distance from what?", a: 1, options: ["The Equator", "The poles", "The International Date Line", "Prime Meridian"] },
+
+ { q: "Which is NOT a state of matter?", a: 2, options: ["Solid", "Plasma", "Light", "Gas"] },
+ { q: "What is the basic unit of life?", a: 2, options: ["Organ", "Tissue", "Cell", "Gene"] },
+ { q: "Which planet is closest to the sun?", a: 3, options: ["Venus", "Mars", "Earth", "Mercury"] },
+ { q: "What gas do humans need to breathe?", a: 1, options: ["Oxygen", "Nitrogen", "Hydrogen", "Carbon Dioxide"] },
+ { q: "What process do plants use to make food?", a: 2, options: ["Germination", "Fermentation", "Photosynthesis", "Respiration"] },
+ { q: "What is Newton’s First Law also called?", a: 2, options: ["Law of Gravity", "Law of Energy", "Law of Inertia", "Law of Attraction"] },
+ { q: "Which part of an atom is positively charged?", a: 2, options: ["Electron", "Neutron", "Proton", "Nucleus"] },
+ { q: "Which organ pumps blood?", a: 2, options: ["Brain", "Lungs", "Heart", "Liver"] },
+
+ { q: "Simplify: 4x + 3x - 2 =", a: 0, options: ["7x - 2", "12x", "x + 2", "4x - 1"] },
+ { q: "Area of a rectangle with length 5 and width 3 is:", a: 1, options: ["8", "15", "18", "10"] },
+ { q: "Solve for x: 2x + 5 = 11", a: 1, options: ["6", "3", "8", "4"] },
+ { q: "Value of 3² + 4² is:", a: 1, options: ["12", "25", "49", "19"] },
+ { q: "Convert 0.75 to a fraction:", a: 2, options: ["1/4", "2/5", "3/4", "4/5"] },
+ { q: "Slope between (0, 0) and (2, 4) is:", a: 1, options: ["1", "2", "4", "0.5"] },
+ { q: "Volume of a cube with side length 3 is:", a: 2, options: ["6", "9", "27", "18"] },
+ { q: "Which is a prime number?", a: 1, options: ["15", "11", "12", "21"] },
+
+ { q: "Subject and predicate: 'The dog barked loudly.'", a: 1, options: ["Subject: loudly, Predicate: barked", "Subject: dog, Predicate: barked loudly", "Subject: The, Predicate: dog barked", "Subject: barked, Predicate: dog loudly"] },
+ { q: "A metaphor is:", a: 3, options: ["A comparison using 'like' or 'as'", "An exaggeration", "A word that imitates sound", "A direct comparison without 'like' or 'as'"] },
+ { q: "Which is a run-on sentence?", a: 0, options: ["I like ice cream it is my favorite dessert.", "I like ice cream.", "My favorite dessert is ice cream.", "Ice cream is good."] },
+ { q: "Correct this sentence: 'Their going to the mall later.'", a: 0, 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."] },
+ { q: "The theme of a story is:", a: 2, options: ["The main character", "The plot", "The central message or lesson", "The setting"] },
+ { q: "Who wrote Romeo and Juliet?", a: 1, options: ["Charles Dickens", "William Shakespeare", "Mark Twain", "J.K. Rowling"] },
+ { q: "Simile vs. Metaphor:", a: 1, options: ["Simile is more poetic", "Simile uses 'like' or 'as,' metaphor does not", "Similes compare animals only", "Metaphors are always longer"] },
+ { q: "'She had walked to school before the bell rang.' Verb tense?", a: 2, options: ["Simple past", "Present perfect", "Past perfect", "Future perfect"] },
+ ];
+
+ const quizContainer = document.getElementById("questions");
+
+ questions.forEach((q, index) => {
+ const div = document.createElement("div");
+ div.className = "question";
+ div.innerHTML = `<h3>${index + 1}. ${q.q}</h3>` +
+ q.options.map((opt, i) => `
+ <label><input type="radio" name="q${index}" value="${i}" required> ${opt}</label><br>
+ `).join("");
+ quizContainer.appendChild(div);
+ });
+
+ document.getElementById("quizForm").addEventListener("submit", function (e) {
+ e.preventDefault();
+ let score = 0;
+ questions.forEach((q, i) => {
+ const selected = document.querySelector(`input[name=q${i}]:checked`);
+ if (selected && parseInt(selected.value) === q.a) score++;
+ });
+ document.getElementById("result").textContent = `You scored ${score} out of ${questions.length}`;
+ });
+ </script>
+ </body>
+ </html>
dev/notes/grok.html +347 -0
@@ @@ -0,0 +1,347 @@
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>9th Grade Readiness Assessment</title>
+ <style>
+ body { font-family: Arial, sans-serif; margin: 20px; }
+ h1 { text-align: center; }
+ .question { margin: 15px 0; }
+ .option { margin: 5px 0; }
+ #submit { display: block; margin: 20px auto; padding: 10px 20px; }
+ #results { margin-top: 20px; }
+ .correct { color: green; }
+ .incorrect { color: red; }
+ </style>
+ </head>
+ <body>
+ <h1>9th Grade Readiness Assessment</h1>
+ <form id="quizForm">
+ <!-- History Questions (8) -->
+ <div class="question">
+ <p>1. Who was the first President of the United States?</p>
+ <label class="option"><input type="radio" name="q1" value="a"> a) Abraham Lincoln</label>
+ <label class="option"><input type="radio" name="q1" value="b"> b) George Washington</label>
+ <label class="option"><input type="radio" name="q1" value="c"> c) Thomas Jefferson</label>
+ <label class="option"><input type="radio" name="q1" value="d"> d) John Adams</label>
+ </div>
+ <div class="question">
+ <p>2. What was the main cause of the American Civil War?</p>
+ <label class="option"><input type="radio" name="q2" value="a"> a) Taxation</label>
+ <label class="option"><input type="radio" name="q2" value="b"> b) Slavery</label>
+ <label class="option"><input type="radio" name="q2" value="c"> c) Trade disputes</label>
+ <label class="option"><input type="radio" name="q2" value="d"> d) Religious differences</label>
+ </div>
+ <div class="question">
+ <p>3. In which year did the Declaration of Independence get adopted?</p>
+ <label class="option"><input type="radio" name="q3" value="a"> a) 1776</label>
+ <label class="option"><input type="radio" name="q3" value="b"> b) 1789</label>
+ <label class="option"><input type="radio" name="q3" value="c"> c) 1800</label>
+ <label class="option"><input type="radio" name="q3" value="d"> d) 1750</label>
+ </div>
+ <div class="question">
+ <p>4. Which country was NOT part of the Axis powers in WWII?</p>
+ <label class="option"><input type="radio" name="q4" value="a"> a) Germany</label>
+ <label class="option"><input type="radio" name="q4" value="b"> b) Japan</label>
+ <label class="option"><input type="radio" name="q4" value="c"> c) Italy</label>
+ <label class="option"><input type="radio" name="q4" value="d"> d) France</label>
+ </div>
+ <div class="question">
+ <p>5. What was the purpose of the Lewis and Clark expedition?</p>
+ <label class="option"><input type="radio" name="q5" value="a"> a) To explore the Louisiana Purchase</label>
+ <label class="option"><input type="radio" name="q5" value="b"> b) To fight Native Americans</label>
+ <label class="option"><input type="radio" name="q5" value="c"> c) To establish a new government</label>
+ <label class="option"><input type="radio" name="q5" value="d"> d) To mine gold</label>
+ </div>
+ <div class="question">
+ <p>6. Who wrote the Emancipation Proclamation?</p>
+ <label class="option"><input type="radio" name="q6" value="a"> a) Abraham Lincoln</label>
+ <label class="option"><input type="radio" name="q6" value="b"> b) Ulysses S. Grant</label>
+ <label class="option"><input type="radio" name="q6" value="c"> c) Frederick Douglass</label>
+ <label class="option"><input type="radio" name="q6" value="d"> d) Harriet Tubman</label>
+ </div>
+ <div class="question">
+ <p>7. What was the primary goal of the Women's Suffrage Movement?</p>
+ <label class="option"><input type="radio" name="q7" value="a"> a) Equal pay</label>
+ <label class="option"><input type="radio" name="q7" value="b"> b) Voting rights</label>
+ <label class="option"><input type="radio" name="q7" value="c"> c) Property ownership</label>
+ <label class="option"><input type="radio" name="q7" value="d"> d) Education reform</label>
+ </div>
+ <div class="question">
+ <p>8. Which event marked the beginning of the Great Depression?</p>
+ <label class="option"><input type="radio" name="q8" value="a"> a) Stock Market Crash of 1929</label>
+ <label class="option"><input type="radio" name="q8" value="b"> b) World War I</label>
+ <label class="option"><input type="radio" name="q8" value="c"> c) Dust Bowl</label>
+ <label class="option"><input type="radio" name="q8" value="d"> d) New Deal</label>
+ </div>
+
+ <!-- Geography Questions (8) -->
+ <div class="question">
+ <p>9. What is the capital of the United States?</p>
+ <label class="option"><input type="radio" name="q9" value="a"> a) New York</label>
+ <label class="option"><input type="radio" name="q9" value="b"> b) Washington, D.C.</label>
+ <label class="option"><input type="radio" name="q9" value="c"> c) Florida</label>
+ <label class="option"><input type="radio" name="q9" value="d"> d) California</label>
+ </div>
+ <div class="question">
+ <p>10. Which continent is home to the Amazon Rainforest?</p>
+ <label class="option"><input type="radio" name="q10" value="a"> a) Africa</label>
+ <label class="option"><input type="radio" name="q10" value="b"> b) South America</label>
+ <label class="option"><input type="radio" name="q10" value="c"> c) Asia</label>
+ <label class="option"><input type="radio" name="q10" value="d"> d) Australia</label>
+ </div>
+ <div class="question">
+ <p>11. What is the longest river in the world?</p>
+ <label class="option"><input type="radio" name="q11" value="a"> a) Nile</label>
+ <label class="option"><input type="radio" name="q11" value="b"> b) Amazon</label>
+ <label class="option"><input type="radio" name="q11" value="c"> c) Mississippi</label>
+ <label class="option"><input type="radio" name="q11" value="d"> d) Yangtze</label>
+ </div>
+ <div class="question">
+ <p>12. Which country has the largest population?</p>
+ <label class="option"><input type="radio" name="q12" value="a"> a) United States</label>
+ <label class="option"><input type="radio" name="q12" value="b"> b) India</label>
+ <label class="option"><input type="radio" name="q12" value="c"> c) Russia</label>
+ <label class="option"><input type="radio" name="q12" value="d"> d) Brazil</label>
+ </div>
+ <div class="question">
+ <p>13. What is the name of the largest ocean on Earth?</p>
+ <label class="option"><input type="radio" name="q13" value="a"> a) Atlantic</label>
+ <label class="option"><input type="radio" name="q13" value="b"> b) Indian</label>
+ <label class="option"><input type="radio" name="q13" value="c"> c) Pacific</label>
+ <label class="option"><input type="radio" name="q13" value="d"> d) Arctic</label>
+ </div>
+ <div class="question">
+ <p>14. Which US state is known as the "Sunshine State"?</p>
+ <label class="option"><input type="radio" name="q14" value="a"> a) California</label>
+ <label class="option"><input type="radio" name="q14" value="b"> b) Florida</label>
+ <label class="option"><input type="radio" name="q14" value="c"> c) Texas</label>
+ <label class="option"><input type="radio" name="q14" value="d"> d) Arizona</label>
+ </div>
+ <div class="question">
+ <p>15. What type of map shows elevation and landforms?</p>
+ <label class="option"><input type="radio" name="q15" value="a"> a) Political</label>
+ <label class="option"><input type="radio" name="q15" value="b"> b) Physical</label>
+ <label class="option"><input type="radio" name="q15" value="c"> c) Thematic</label>
+ <label class="option"><input type="radio" name="q15" value="d"> d) Road</label>
+ </div>
+ <div class="question">
+ <p>16. Which country is both a continent and an island?</p>
+ <label class="option"><input type="radio" name="q16" value="a"> a) Australia</label>
+ <label class="option"><input type="radio" name="q16" value="b"> b) Greenland</label>
+ <label class="option"><input type="radio" name="q16" value="c"> c) Antarctica</label>
+ <label class="option"><input type="radio" name="q16" value="d"> d) Iceland</label>
+ </div>
+
+ <!-- Science Questions (8) -->
+ <div class="question">
+ <p>17. What is the chemical symbol for water?</p>
+ <label class="option"><input type="radio" name="q17" value="a"> a) H2O</label>
+ <label class="option"><input type="radio" name="q17" value="b"> b) CO2</label>
+ <label class="option"><input type="radio" name="q17" value="c"> c) O2</label>
+ <label class="option"><input type="radio" name="q17" value="d"> d) NaCl</label>
+ </div>
+ <div class="question">
+ <p>18. What planet is known as the Red Planet?</p>
+ <label class="option"><input type="radio" name="q18" value="a"> a) Jupiter</label>
+ <label class="option"><input type="radio" name="q18" value="b"> b) Mars</label>
+ <label class="option"><input type="radio" name="q18" value="c"> c) Venus</label>
+ <label class="option"><input type="radio" name="q18" value="d"> d) Mercury</label>
+ </div>
+ <div class="question">
+ <p>19. What is the primary source of energy for Earth's climate system?</p>
+ <label class="option"><input type="radio" name="q19" value="a"> a) Wind</label>
+ <label class="option"><input type="radio" name="q19" value="b"> b) Sun</label>
+ <label class="option"><input type="radio" name="q19" value="c"> c) Moon</label>
+ <label class="option"><input type="radio" name="q19" value="d"> d) Geothermal</label>
+ </div>
+ <div class="question">
+ <p>20. What is the process by which plants make food?</p>
+ <label class="option"><input type="radio" name="q20" value="a"> a) Respiration</label>
+ <label class="option"><input type="radio" name="q20" value="b"> b) Photosynthesis</label>
+ <label class="option"><input type="radio" name="q20" value="c"> c) Decomposition</label>
+ <label class="option"><input type="radio" name="q20" value="d"> d) Fermentation</label>
+ </div>
+ <div class="question">
+ <p>21. What is the basic unit of life?</p>
+ <label class="option"><input type="radio" name="q21" value="a"> a) Atom</label>
+ <label class="option"><input type="radio" name="q21" value="b"> b) Cell</label>
+ <label class="option"><input type="radio" name="q21" value="c"> c) Molecule</label>
+ <label class="option"><input type="radio" name="q21" value="d"> d) Organ</label>
+ </div>
+ <div class="question">
+ <p>22. Which force keeps planets in orbit around the Sun?</p>
+ <label class="option"><input type="radio" name="q22" value="a"> a) Magnetism</label>
+ <label class="option"><input type="radio" name="q22" value="b"> b) Gravity</label>
+ <label class="option"><input type="radio" name="q22" value="c"> c) Friction</label>
+ <label class="option"><input type="radio" name="q22" value="d"> d) Electricity</label>
+ </div>
+ <div class="question">
+ <p>23. What is the freezing point of water in Celsius?</p>
+ <label class="option"><input type="radio" name="q23" value="a"> a) 0</label>
+ <label class="option"><input type="radio" name="q23" value="b"> b) 100</label>
+ <label class="option"><input type="radio" name="q23" value="c"> c) 32</label>
+ <label class="option"><input type="radio" name="q23" value="d"> d) -10</label>
+ </div>
+ <div class="question">
+ <p>24. What type of rock is formed from cooled lava?</p>
+ <label class="option"><input type="radio" name="q24" value="a"> a) Sedimentary</label>
+ <label class="option"><input type="radio" name="q24" value="b"> b) Igneous</label>
+ <label class="option"><input type="radio" name="q24" value="c"> c) Metamorphic</label>
+ <label class="option"><input type="radio" name="q24" value="d"> d) Fossilized</label>
+ </div>
+
+ <!-- Math Questions (8) -->
+ <div class="question">
+ <p>25. What is 5 squared?</p>
+ <label class="option"><input type="radio" name="q25" value="a"> a) 10</label>
+ <label class="option"><input type="radio" name="q25" value="b"> b) 15</label>
+ <label class="option"><input type="radio" name="q25" value="c"> c) 25</label>
+ <label class="option"><input type="radio" name="q25" value="d"> d) 50</label>
+ </div>
+ <div class="question">
+ <p>26. Solve: 2x + 4 = 10</p>
+ <label class="option"><input type="radio" name="q26" value="a"> a) x = 2</label>
+ <label class="option"><input type="radio" name="q26" value="b"> b) x = 3</label>
+ <label class="option"><input type="radio" name="q26" value="c"> c) x = 4</label>
+ <label class="option"><input type="radio" name="q26" value="d"> d) x = 5</label>
+ </div>
+ <div class="question">
+ <p>27. What is the perimeter of a rectangle with length 6 and width 4?</p>
+ <label class="option"><input type="radio" name="q27" value="a"> a) 16</label>
+ <label class="option"><input type="radio" name="q27" value="b"> b) 20</label>
+ <label class="option"><input type="radio" name="q27" value="c"> c) 24</label>
+ <label class="option"><input type="radio" name="q27" value="d"> d) 28</label>
+ </div>
+ <div class="question">
+ <p>28. What is 3/4 as a decimal?</p>
+ <label class="option"><input type="radio" name="q28" value="a"> a) 0.25</label>
+ <label class="option"><input type="radio" name="q28" value="b"> b) 0.5</label>
+ <label class="option"><input type="radio" name="q28" value="c"> c) 0.75</label>
+ <label class="option"><input type="radio" name="q28" value="d"> d) 1.0</label>
+ </div>
+ <div class="question">
+ <p>29. What is the value of π (pi) to two decimal places?</p>
+ <label class="option"><input type="radio" name="q29" value="a"> a) 3.12</label>
+ <label class="option"><input type="radio" name="q29" value="b"> b) 3.14</label>
+ <label class="option"><input type="radio" name="q29" value="c"> c) 3.16</label>
+ <label class="option"><input type="radio" name="q29" value="d"> d) 3.18</label>
+ </div>
+ <div class="question">
+ <p>30. If a shirt costs $20 and is on sale for 25% off, what is the sale price?</p>
+ <label class="option"><input type="radio" name="q30" value="a"> a) $12</label>
+ <label class="option"><input type="radio" name="q30" value="b"> b) $15</label>
+ <label class="option"><input type="radio" name="q30" value="c"> c) $16</label>
+ <label class="option"><input type="radio" name="q30" value="d"> d) $18</label>
+ </div>
+ <div class="question">
+ <p>31. What is the slope of the line y = 2x + 3?</p>
+ <label class="option"><input type="radio" name="q31" value="a"> a) 2</label>
+ <label class="option"><input type="radio" name="q31" value="b"> b) 3</label>
+ <label class="option"><input type="radio" name="q31" value="c"> c) -2</label>
+ <label class="option"><input type="radio" name="q31" value="d"> d) -3</label>
+ </div>
+ <div class="question">
+ <p>32. What is the area of a circle with radius 5? (Use π = 3.14)</p>
+ <label class="option"><input type="radio" name="q32" value="a"> a) 15.7</label>
+ <label class="option"><input type="radio" name="q32" value="b"> b) 31.4</label>
+ <label class="option"><input type="radio" name="q32" value="c"> c) 78.5</label>
+ <label class="option"><input type="radio" name="q32" value="d"> d) 157</label>
+ </div>
+
+ <!-- Language Arts Questions (8) -->
+ <div class="question">
+ <p>33. What is a synonym for "happy"?</p>
+ <label class="option"><input type="radio" name="q33" value="a"> a) Sad</label>
+ <label class="option"><input type="radio" name="q33" value="b"> b) Joyful</label>
+ <label class="option"><input type="radio" name="q33" value="c"> c) Angry</label>
+ <label class="option"><input type="radio" name="q33" value="d"> d) Tired</label>
+ </div>
+ <div class="question">
+ <p>34. What is the main idea of a story called?</p>
+ <label class="option"><input type="radio" name="q34" value="a"> a) Theme</label>
+ <label class="option"><input type="radio" name="q34" value="b"> b) Plot</label>
+ <label class="option"><input type="radio" name="q34" value="c"> c) Setting</label>
+ <label class="option"><input type="radio" name="q34" value="d"> d) Character</label>
+ </div>
+ <div class="question">
+ <p>35. Which punctuation mark is used to show possession?</p>
+ <label class="option"><input type="radio" name="q35" value="a"> a) Comma</label>
+ <label class="option"><input type="radio" name="q35" value="b"> b) Apostrophe</label>
+ <label class="option"><input type="radio" name="q35" value="c"> c) Period</label>
+ <label class="option"><input type="radio" name="q35" value="d"> d) Exclamation point</label>
+ </div>
+ <div class="question">
+ <p>36. What is the past tense of "run"?</p>
+ <label class="option"><input type="radio" name="q36" value="a"> a) Runned</label>
+ <label class="option"><input type="radio" name="q36" value="b"> b) Ran</label>
+ <label class="option"><input type="radio" name="q36" value="c"> c) Running</label>
+ <label class="option"><input type="radio" name="q36" value="d"> d) Runs</label>
+ </div>
+ <div class="question">
+ <p>37. What is a person, place, or thing called in grammar?</p>
+ <label class="option"><input type="radio" name="q37" value="a"> a) Verb</label>
+ <label class="option"><input type="radio" name="q37" value="b"> b) Adjective</label>
+ <label class="option"><input type="radio" name="q37" value="c"> c) Noun</label>
+ <label class="option"><input type="radio" name="q37" value="d"> d) Adverb</label>
+ </div>
+ <div class="question">
+ <p>38. Who wrote "The Outsiders"?</p>
+ <label class="option"><input type="radio" name="q38" value="a"> a) J.K. Rowling</label>
+ <label class="option"><input type="radio" name="q38" value="b"> b) S.E. Hinton</label>
+ <label class="option"><input type="radio" name="q38" value="c"> c) Mark Twain</label>
+ <label class="option"><input type="radio" name="q38" value="d"> d) Harper Lee</label>
+ </div>
+ <div class="question">
+ <p>39. What is it called when a story is told from the "I" perspective?</p>
+ <label class="option"><input type="radio" name="q39" value="a"> a) First person</label>
+ <label class="option"><input type="radio" name="q39" value="b"> b) Second person</label>
+ <label class="option"><input type="radio" name="q39" value="c"> c) Third person</label>
+ <label class="option"><input type="radio" name="q39" value="d"> d) Omniscient</label>
+ </div>
+ <div class="question">
+ <p>40. What is an example of a metaphor?</p>
+ <label class="option"><input type="radio" name="q40" value="a"> a) The wind howled like a wolf.</label>
+ <label class="option"><input type="radio" name="q40" value="b"> b) Her smile was a ray of sunshine.</label>
+ <label class="option"><input type="radio" name="q40" value="c"> c) The stars twinkled brightly.</label>
+ <label class="option"><input type="radio" name="q40" value="d"> d) He ran as fast as he could.</label>
+ </div>
+
+ <button type="submit" id="submit">Submit Answers</button>
+ </form>
+ <div id="results"></div>
+
+ <script>
+ const correctAnswers = {
+ q1: 'b', q2: 'b', q3: 'a', q4: 'd', q5: 'a', q6: 'a', q7: 'b', q8: 'a',
+ q9: 'b', q10: 'b', q11: 'a', q12: 'b', q13: 'c', q14: 'b', q15: 'b', q16: 'a',
+ q17: 'a', q18: 'b', q19: 'b', q20: 'b', q21: 'b', q22: 'b', q23: 'a', q24: 'b',
+ q25: 'c', q26: 'b', q27: 'b', q28: 'c', q29: 'b', q30: 'b', q31: 'a', q32: 'c',
+ q33: 'b', q34: 'a', q35: 'b', q36: 'b', q37: 'c', q38: 'b', q39: 'a', q40: 'b'
+ };
+
+ document.getElementById('quizForm').addEventListener('submit', function(e) {
+ e.preventDefault();
+ let score = 0;
+ let resultsHTML = '<h2>Results</h2>';
+ const formData = new FormData(this);
+
+ for (let i = 1; i <= 40; i++) {
+ const answer = formData.get(`q${i}`);
+ const isCorrect = answer === correctAnswers[`q${i}`];
+ if (isCorrect) score++;
+ resultsHTML += `
+ <p>Question ${i}:
+ ${isCorrect ? '<span class="correct">Correct</span>' : '<span class="incorrect">Incorrect</span>'}
+ ${!isCorrect && answer ? `(You answered: ${answer}, Correct: ${correctAnswers[`q${i}`]})` : ''}
+ </p>`;
+ }
+
+ const percentage = (score / 40) * 100;
+ resultsHTML = `<p><strong>Score: ${score}/40 (${percentage.toFixed(2)}%)</strong></p>` + resultsHTML;
+ document.getElementById('results').innerHTML = resultsHTML;
+ });
+ </script>
+ </body>
+ </html>
dev/plans/2507121434-cloudflare-ai-quiz-generation.md +209 -0
@@ @@ -0,0 +1,209 @@
+ # Feature Plan: Cloudflare AI Workers Quiz Generation (Premium Feature)
+
+ **Status**: Planning
+ **Created**: 2025-07-12
+ **Author**: Claude
+ **Dependencies**: User Authentication, Cloudflare D1 Database
+
+ ## Overview
+
+ Implement a Cloudflare AI Workers-based system for interactive quiz development as the PRIMARY method for quiz creation. This feature will be exclusive to premium users, with AI generation becoming the main value proposition for paid subscriptions. Free users will retain access to manual upload/paste functionality.
+
+ ## Requirements
+ - Interactive chat interface for quiz refinement (premium only)
+ - Integration with Cloudflare AI Workers API with usage tracking
+ - Real-time JSON validation
+ - Credit-based system for premium users
+ - User authentication checks before AI access
+ - Store generated quizzes in D1 database
+ - Track AI usage for billing and analytics
+ - Clear upgrade prompts for free users
+
+ ## Technical Approach
+
+ ### Architecture Decisions
+ - Use Cloudflare Workers AI for LLM processing to leverage edge computing
+ - Implement chat interface in Vue.js as primary quiz creation method
+ - Store chat history in component state (no persistence needed initially)
+ - Validate JSON structure before allowing import
+ - Use streaming responses for better UX during generation
+ - Implement credit system to track and limit AI usage
+ - Store all generated quizzes in D1 database
+
+ ### Premium User Flow
+ 1. User clicks "Create Quiz with AI" (primary CTA)
+ 2. System checks authentication and tier:
+ - Premium users → Open chat interface
+ - Free users → Show upgrade page with benefits
+ 3. Premium users interact with AI to refine quiz
+ 4. Generated quiz is validated and saved to D1
+ 5. User can edit, share, or use quiz immediately
+
+ ### API Design
+ The Cloudflare Worker will expose authenticated endpoints:
+ - `POST /api/chat` - Send message and get AI response (requires premium)
+ - `POST /api/generate-quiz` - Generate final quiz JSON from conversation (requires premium)
+ - `POST /api/validate-quiz` - Validate quiz JSON structure (all users)
+ - `GET /api/usage` - Get user's AI credit usage (premium users)
+ - `POST /api/save-quiz` - Save generated quiz to D1 (all users)
+
+ ## Implementation Tasks
+ - [ ] Wait for auth system and D1 database implementation
+ - [ ] Research Cloudflare Workers AI models and pricing
+ - [ ] Add authentication middleware to Worker endpoints
+ - [ ] Implement premium tier checks in API
+ - [ ] Create AI chat endpoint with auth validation
+ - [ ] Implement credit tracking in D1
+ - [ ] Build JSON validation logic for quiz structure
+ - [ ] Create Vue.js chat interface component (premium UI)
+ - [ ] Create upgrade prompt component for free users
+ - [ ] Update Home.vue with AI as primary creation method
+ - [ ] Add usage dashboard for premium users
+ - [ ] Implement streaming response handling
+ - [ ] Store generated quizzes in D1
+ - [ ] Test premium vs free user flows
+ - [ ] Add comprehensive error handling
+ - [ ] Update documentation with premium features
+
+ ## Files to Modify
+ - `assessment-app/src/views/Home.vue` - Add new tab for AI chat interface
+ - `assessment-app/src/components/QuizChat.vue` - New chat component (to create)
+ - `cloudflare-worker/src/index.js` - Main worker file (to create)
+ - `cloudflare-worker/src/prompts.js` - Prompt templates (to create)
+ - `cloudflare-worker/src/validation.js` - JSON validation logic (to create)
+ - `cloudflare-worker/wrangler.toml` - Worker configuration (to create)
+ - `assessment-app/src/services/api.js` - API client for worker (to create)
+
+ ## Testing Strategy
+
+ ### Unit Tests
+ - Test JSON validation logic with various quiz structures
+ - Test prompt generation with different parameters
+ - Mock Cloudflare AI responses for chat tests
+
+ ### Integration Tests
+ - Test full flow from chat to quiz generation
+ - Verify generated quizzes load correctly in Assessment.vue
+ - Test error handling for malformed AI responses
+
+ ## Edge Cases & Error Handling
+ - AI generates invalid JSON: Show error and allow retry
+ - AI response timeout: Implement retry with exponential backoff
+ - Rate limiting: Show user-friendly message with wait time
+ - Incomplete quiz structure: Validate and prompt for missing fields
+ - Network errors: Fallback to manual JSON entry
+
+ ## Credit System & Cost Management
+
+ ### Premium User Credits
+ - Monthly credit allocation based on subscription tier
+ - 1 credit = 1 quiz generation (regardless of refinements)
+ - Track token usage for cost analysis
+ - Show remaining credits in UI
+ - Option to purchase additional credits
+
+ ### Cost Tracking
+ ```sql
+ -- Track usage in ai_generations table
+ INSERT INTO ai_generations (user_id, prompt, tokens_used, created_at)
+ VALUES (?, ?, ?, CURRENT_TIMESTAMP);
+ ```
+
+ ### Rate Limiting
+ - 10 chat messages per minute per user
+ - 5 quiz generations per hour per user
+ - 100 total AI calls per day per premium user
+
+ ## Open Questions
+ - [ ] Which Cloudflare AI model should we use? (@cf/meta/llama-3-8b-instruct seems suitable)
+ - [ ] Should chat history persist between sessions?
+ - [ ] Monthly credit allocation for premium users?
+ - [ ] Cost per additional credit package?
+ - [ ] Should we offer different premium tiers?
+
+ ## Future Considerations
+ - Add quiz templates library
+ - Implement quiz sharing functionality
+ - Add AI-powered quiz difficulty adjustment
+ - Support for different question types beyond multiple choice
+ - Analytics on quiz generation patterns
+
+ ## Technical Implementation Details
+
+ ### Cloudflare Workers AI Integration with Auth
+ ```javascript
+ // Example worker code with premium checks
+ export default {
+ async fetch(request, env) {
+ const ai = env.AI;
+ const db = env.DB;
+
+ // Chat endpoint with auth
+ if (url.pathname === '/api/chat') {
+ // Verify JWT and get user
+ const user = await verifyAuth(request, env);
+ if (!user || user.tier !== 'premium') {
+ return Response.json({
+ error: 'Premium subscription required',
+ upgrade_url: '/premium'
+ }, { status: 403 });
+ }
+
+ // Check credits
+ const credits = await getUserCredits(user.id, db);
+ if (credits <= 0) {
+ return Response.json({
+ error: 'No credits remaining',
+ purchase_url: '/credits'
+ }, { status: 402 });
+ }
+
+ // Process AI request
+ const messages = await request.json();
+ const response = await ai.run('@cf/meta/llama-3-8b-instruct', {
+ messages: [
+ { role: 'system', content: systemPrompt },
+ ...messages
+ ]
+ });
+
+ // Track usage
+ await trackUsage(user.id, response.usage, db);
+
+ return Response.json({ response });
+ }
+ }
+ }
+ ```
+
+ ### Vue.js Chat Component Structure
+ ```vue
+ <template>
+ <div class="quiz-chat">
+ <div class="chat-messages">
+ <!-- Message history -->
+ </div>
+ <div class="chat-input">
+ <!-- User input -->
+ </div>
+ <button @click="generateQuiz">Generate Quiz</button>
+ </div>
+ </template>
+ ```
+
+ ### JSON Validation Schema
+ The system must validate:
+ - Required fields: title, description, subjects
+ - Each subject has name and questions array
+ - Each question has: id (unique number), question, options (exactly 4), correctAnswer (0-3)
+ - No duplicate question IDs
+ - Valid JSON syntax
+
+ ## Documentation Plan
+ After implementation, create `dev/docs/2507121434-cloudflare-ai-quiz-generation.md` with:
+ - Feature overview and purpose
+ - API endpoint documentation
+ - Configuration for Cloudflare Workers
+ - Usage guide for the chat interface
+ - Troubleshooting common issues
+ - Example conversations and generated quizzes
\ No newline at end of file
dev/plans/2507121440-implementation-roadmap.md +240 -0
@@ @@ -0,0 +1,240 @@
+ # Implementation Roadmap: Quiz Craft Premium Platform
+
+ **Status**: Planning
+ **Created**: 2025-07-12
+ **Author**: Claude
+
+ ## Overview
+
+ Transform Quiz Craft from a static quiz application into a full-featured platform with user accounts, database storage, and AI-powered quiz generation for premium users. This roadmap outlines the implementation order and dependencies.
+
+ ## Project Vision
+
+ - **Primary**: AI-powered quiz generation as the main creation method
+ - **Secondary**: Manual upload/paste functionality for all users
+ - **Business Model**: Freemium with AI generation limited to premium users
+ - **Infrastructure**: Built on Cloudflare's edge platform (Workers, D1, AI)
+
+ ## Implementation Phases
+
+ ### Phase 1: User Authentication & Accounts (Week 1-2)
+ **Goal**: Implement secure user authentication system
+
+ **Features**:
+ - User registration and login
+ - JWT-based authentication
+ - User profile management
+ - Free vs Premium tier distinction
+
+ **Technical Stack**:
+ - Cloudflare Workers for auth API
+ - Cloudflare KV for session storage
+ - Vue.js auth components
+ - Pinia for state management
+
+ **Key Files**:
+ - `cloudflare-worker/src/auth/*` - Authentication logic
+ - `assessment-app/src/stores/auth.js` - Auth state
+ - `assessment-app/src/components/Auth/*` - Login/Register components
+
+ ### Phase 2: Database Infrastructure (Week 2-3)
+ **Goal**: Implement Cloudflare D1 database for persistent storage
+
+ **Database Schema**:
+ ```sql
+ -- Users table
+ CREATE TABLE users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ email TEXT UNIQUE NOT NULL,
+ password_hash TEXT NOT NULL,
+ tier TEXT DEFAULT 'free', -- 'free' or 'premium'
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ ai_credits INTEGER DEFAULT 0
+ );
+
+ -- Quizzes table
+ CREATE TABLE quizzes (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER,
+ title TEXT NOT NULL,
+ description TEXT,
+ content JSON NOT NULL, -- Full quiz JSON
+ is_public BOOLEAN DEFAULT false,
+ source TEXT NOT NULL, -- 'ai', 'upload', 'paste'
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id)
+ );
+
+ -- Results table
+ CREATE TABLE results (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER,
+ quiz_id INTEGER,
+ score INTEGER NOT NULL,
+ total_questions INTEGER NOT NULL,
+ answers JSON NOT NULL, -- User's answers
+ completed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id),
+ FOREIGN KEY (quiz_id) REFERENCES quizzes(id)
+ );
+
+ -- AI Generation History (for tracking usage)
+ CREATE TABLE ai_generations (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ prompt TEXT NOT NULL,
+ tokens_used INTEGER,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id)
+ );
+ ```
+
+ **Features**:
+ - Store user-created quizzes
+ - Track quiz results and history
+ - Public quiz library
+ - User quiz management
+
+ ### Phase 3: AI Quiz Generation for Premium Users (Week 3-4)
+ **Goal**: Implement Cloudflare AI Workers integration with premium tier restrictions
+
+ **Features**:
+ - Interactive chat interface for quiz refinement
+ - Real-time JSON validation
+ - Credit-based system for premium users
+ - Usage tracking and limits
+
+ **Architecture Updates**:
+ ```javascript
+ // Check user tier before AI generation
+ if (user.tier !== 'premium' || user.ai_credits <= 0) {
+ return Response.json({
+ error: 'AI generation requires premium account'
+ }, { status: 403 });
+ }
+ ```
+
+ **UI Flow**:
+ 1. User clicks "Create with AI" (primary CTA)
+ 2. Free users see upgrade prompt
+ 3. Premium users enter chat interface
+ 4. Iterative refinement through conversation
+ 5. Generate and validate final quiz
+ 6. Save to database
+
+ ### Phase 4: Premium Features & Monetization (Week 4-5)
+ **Goal**: Implement premium tier benefits and payment integration
+
+ **Premium Features**:
+ - Unlimited AI quiz generation
+ - Advanced quiz analytics
+ - Export options (PDF, CSV)
+ - Private quiz library
+ - Custom branding
+
+ **Implementation**:
+ - Stripe integration for payments
+ - Subscription management
+ - Usage dashboard
+ - Billing portal
+
+ ## Technical Architecture
+
+ ### Frontend Structure
+ ```
+ assessment-app/
+ ├── src/
+ │ ├── views/
+ │ │ ├── Auth/
+ │ │ │ ├── Login.vue
+ │ │ │ └── Register.vue
+ │ │ ├── Dashboard.vue # User dashboard
+ │ │ ├── QuizBuilder.vue # AI chat interface
+ │ │ └── Premium.vue # Upgrade page
+ │ ├── stores/
+ │ │ ├── auth.js
+ │ │ └── quiz.js
+ │ └── services/
+ │ ├── api.js
+ │ └── auth.js
+ ```
+
+ ### Backend Structure
+ ```
+ cloudflare-worker/
+ ├── src/
+ │ ├── index.js # Main worker
+ │ ├── auth/
+ │ │ ├── jwt.js
+ │ │ └── middleware.js
+ │ ├── db/
+ │ │ ├── schema.sql
+ │ │ └── queries.js
+ │ ├── ai/
+ │ │ ├── chat.js
+ │ │ ├── generation.js
+ │ │ └── validation.js
+ │ └── routes/
+ │ ├── auth.js
+ │ ├── quiz.js
+ │ └── user.js
+ ```
+
+ ## Migration Strategy
+
+ 1. **Preserve Existing Functionality**: Keep current quiz-taking experience
+ 2. **Progressive Enhancement**: Add features without breaking existing ones
+ 3. **Data Migration**: Import existing JSON quizzes into D1
+ 4. **Backward Compatibility**: Support direct JSON access for legacy links
+
+ ## Security Considerations
+
+ - JWT tokens with short expiration
+ - Rate limiting on all endpoints
+ - Input validation and sanitization
+ - CORS configuration for API
+ - Secure password hashing (bcrypt)
+ - SQL injection prevention
+
+ ## Performance Optimization
+
+ - Edge caching for public quizzes
+ - Lazy loading for dashboard
+ - Optimistic UI updates
+ - Background job queue for AI generation
+ - CDN for static assets
+
+ ## Success Metrics
+
+ - User registration rate
+ - Free to premium conversion rate
+ - AI generation usage per premium user
+ - Quiz completion rates
+ - User retention (30-day)
+
+ ## Risk Mitigation
+
+ - **AI Cost Overrun**: Implement strict credit system and monitoring
+ - **Database Performance**: Use appropriate indexes and query optimization
+ - **User Adoption**: Clear value proposition for premium tier
+ - **Technical Debt**: Modular architecture for easy updates
+
+ ## Next Steps
+
+ 1. Set up Cloudflare Workers project with D1
+ 2. Implement basic authentication system
+ 3. Create database schema and migrations
+ 4. Build user dashboard MVP
+ 5. Integrate AI generation with auth checks
+ 6. Add payment processing
+ 7. Launch beta with limited users
+
+ ## Dependencies
+
+ - Cloudflare Workers account with AI access
+ - Cloudflare D1 database
+ - Stripe account for payments
+ - Domain with Cloudflare DNS
+ - SSL certificate
+
+ This roadmap provides a clear path from the current static application to a full-featured SaaS platform with AI-powered quiz generation as the primary value proposition.
\ No newline at end of file
dev/plans/2507121445-user-authentication-system.md +342 -0
@@ @@ -0,0 +1,342 @@
+ # Feature Plan: User Authentication System
+
+ **Status**: Planning
+ **Created**: 2025-07-12
+ **Author**: Claude
+ **Priority**: Phase 1 - Must be implemented first
+
+ ## Overview
+
+ Implement a secure user authentication system using Cloudflare Workers, KV storage for sessions, and Vue.js frontend. This system will support both free and premium user tiers, forming the foundation for AI quiz generation and other premium features.
+
+ ## Requirements
+
+ - User registration with email/password
+ - Secure login with JWT tokens
+ - Password reset functionality
+ - User profile management
+ - Free vs Premium tier distinction
+ - Session management with Cloudflare KV
+ - CORS support for SPA
+ - Email verification (optional for MVP)
+
+ ## Technical Approach
+
+ ### Architecture Decisions
+
+ 1. **JWT Tokens**: Use for stateless authentication
+ 2. **Cloudflare KV**: Store refresh tokens and sessions
+ 3. **D1 Database**: Store user accounts and profiles
+ 4. **bcrypt**: Password hashing (via Web Crypto API)
+ 5. **Pinia**: Frontend state management for auth
+ 6. **Vue Router Guards**: Protect premium routes
+
+ ### Security Considerations
+
+ - JWT with short expiration (15 minutes)
+ - Refresh tokens in httpOnly cookies
+ - Rate limiting on auth endpoints
+ - Input validation and sanitization
+ - CSRF protection
+ - Secure password requirements
+
+ ## Implementation Tasks
+
+ - [ ] Set up Cloudflare Workers project with TypeScript
+ - [ ] Create D1 database and users table
+ - [ ] Implement password hashing utilities
+ - [ ] Create JWT generation and validation
+ - [ ] Build registration endpoint with validation
+ - [ ] Build login endpoint with rate limiting
+ - [ ] Implement refresh token mechanism
+ - [ ] Create logout endpoint
+ - [ ] Build password reset flow
+ - [ ] Create Vue auth store with Pinia
+ - [ ] Build Login/Register components
+ - [ ] Add route guards for protected pages
+ - [ ] Create user profile page
+ - [ ] Add auth interceptor to API client
+ - [ ] Test security vulnerabilities
+ - [ ] Document API endpoints
+
+ ## API Endpoints
+
+ ### Public Endpoints
+ ```
+ POST /api/auth/register
+ Body: { email, password, name }
+ Response: { user, token, refreshToken }
+
+ POST /api/auth/login
+ Body: { email, password }
+ Response: { user, token, refreshToken }
+
+ POST /api/auth/refresh
+ Body: { refreshToken }
+ Response: { token, refreshToken }
+
+ POST /api/auth/logout
+ Headers: Authorization: Bearer <token>
+ Response: { success }
+
+ POST /api/auth/forgot-password
+ Body: { email }
+ Response: { message }
+
+ POST /api/auth/reset-password
+ Body: { token, newPassword }
+ Response: { success }
+ ```
+
+ ### Protected Endpoints
+ ```
+ GET /api/auth/me
+ Headers: Authorization: Bearer <token>
+ Response: { user }
+
+ PUT /api/auth/profile
+ Headers: Authorization: Bearer <token>
+ Body: { name, preferences }
+ Response: { user }
+
+ POST /api/auth/upgrade
+ Headers: Authorization: Bearer <token>
+ Response: { paymentUrl }
+ ```
+
+ ## Database Schema
+
+ ```sql
+ -- Users table
+ CREATE TABLE users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ email TEXT UNIQUE NOT NULL,
+ password_hash TEXT NOT NULL,
+ name TEXT NOT NULL,
+ tier TEXT DEFAULT 'free' CHECK(tier IN ('free', 'premium')),
+ email_verified BOOLEAN DEFAULT false,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ last_login DATETIME,
+ ai_credits INTEGER DEFAULT 0,
+ stripe_customer_id TEXT,
+ stripe_subscription_id TEXT
+ );
+
+ -- Create indexes
+ CREATE INDEX idx_users_email ON users(email);
+ CREATE INDEX idx_users_tier ON users(tier);
+
+ -- Password reset tokens
+ CREATE TABLE password_resets (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ token TEXT UNIQUE NOT NULL,
+ expires_at DATETIME NOT NULL,
+ used BOOLEAN DEFAULT false,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id)
+ );
+ ```
+
+ ## Frontend Implementation
+
+ ### Auth Store (Pinia)
+ ```javascript
+ // stores/auth.js
+ export const useAuthStore = defineStore('auth', {
+ state: () => ({
+ user: null,
+ token: null,
+ isAuthenticated: false,
+ isPremium: false
+ }),
+
+ actions: {
+ async login(credentials) {
+ const response = await api.post('/auth/login', credentials)
+ this.setAuth(response.data)
+ },
+
+ async register(userData) {
+ const response = await api.post('/auth/register', userData)
+ this.setAuth(response.data)
+ },
+
+ setAuth({ user, token }) {
+ this.user = user
+ this.token = token
+ this.isAuthenticated = true
+ this.isPremium = user.tier === 'premium'
+
+ // Store token
+ localStorage.setItem('token', token)
+
+ // Set axios header
+ api.defaults.headers.common['Authorization'] = `Bearer ${token}`
+ },
+
+ logout() {
+ this.user = null
+ this.token = null
+ this.isAuthenticated = false
+ this.isPremium = false
+
+ localStorage.removeItem('token')
+ delete api.defaults.headers.common['Authorization']
+
+ router.push('/login')
+ }
+ }
+ })
+ ```
+
+ ### Route Guards
+ ```javascript
+ // router/index.js
+ router.beforeEach((to, from, next) => {
+ const authStore = useAuthStore()
+
+ if (to.meta.requiresAuth && !authStore.isAuthenticated) {
+ next('/login')
+ } else if (to.meta.requiresPremium && !authStore.isPremium) {
+ next('/premium')
+ } else {
+ next()
+ }
+ })
+ ```
+
+ ## Worker Implementation
+
+ ### JWT Utilities
+ ```javascript
+ // utils/jwt.js
+ import { SignJWT, jwtVerify } from 'jose'
+
+ const secret = new TextEncoder().encode(env.JWT_SECRET)
+
+ export async function createToken(payload) {
+ return new SignJWT(payload)
+ .setProtectedHeader({ alg: 'HS256' })
+ .setExpirationTime('15m')
+ .setIssuedAt()
+ .sign(secret)
+ }
+
+ export async function verifyToken(token) {
+ const { payload } = await jwtVerify(token, secret)
+ return payload
+ }
+ ```
+
+ ### Password Hashing
+ ```javascript
+ // utils/crypto.js
+ export async function hashPassword(password) {
+ const encoder = new TextEncoder()
+ const data = encoder.encode(password + env.SALT)
+ const hash = await crypto.subtle.digest('SHA-256', data)
+ return btoa(String.fromCharCode(...new Uint8Array(hash)))
+ }
+
+ export async function verifyPassword(password, hash) {
+ const testHash = await hashPassword(password)
+ return testHash === hash
+ }
+ ```
+
+ ### Middleware
+ ```javascript
+ // middleware/auth.js
+ export async function requireAuth(request, env) {
+ const token = request.headers.get('Authorization')?.split(' ')[1]
+
+ if (!token) {
+ return Response.json({ error: 'No token provided' }, { status: 401 })
+ }
+
+ try {
+ const payload = await verifyToken(token)
+ const user = await env.DB.prepare(
+ 'SELECT * FROM users WHERE id = ?'
+ ).bind(payload.userId).first()
+
+ if (!user) {
+ return Response.json({ error: 'User not found' }, { status: 401 })
+ }
+
+ request.user = user
+ return null // Continue
+ } catch (error) {
+ return Response.json({ error: 'Invalid token' }, { status: 401 })
+ }
+ }
+
+ export async function requirePremium(request) {
+ if (request.user.tier !== 'premium') {
+ return Response.json({
+ error: 'Premium subscription required',
+ upgrade_url: '/premium'
+ }, { status: 403 })
+ }
+ return null // Continue
+ }
+ ```
+
+ ## Testing Strategy
+
+ ### Unit Tests
+ - Password hashing and verification
+ - JWT generation and validation
+ - Input validation functions
+ - Rate limiting logic
+
+ ### Integration Tests
+ - Full registration flow
+ - Login with valid/invalid credentials
+ - Token refresh mechanism
+ - Password reset flow
+ - Premium upgrade flow
+
+ ### Security Tests
+ - SQL injection attempts
+ - XSS in user inputs
+ - CSRF protection
+ - Rate limiting effectiveness
+ - Token expiration
+
+ ## Error Handling
+
+ - Registration errors: Email already exists, weak password
+ - Login errors: Invalid credentials, account locked
+ - Token errors: Expired, invalid, missing
+ - Database errors: Connection issues, constraints
+ - Rate limit errors: Too many attempts
+
+ ## Monitoring
+
+ - Track registration conversion rate
+ - Monitor failed login attempts
+ - Alert on suspicious activity
+ - Track token refresh patterns
+ - Monitor API response times
+
+ ## Future Enhancements
+
+ - Social login (Google, GitHub)
+ - Two-factor authentication
+ - Email verification
+ - Account deletion (GDPR)
+ - Login history
+ - Device management
+ - SSO for enterprise
+
+ ## Documentation Plan
+
+ After implementation, create `dev/docs/2507121445-user-authentication.md` with:
+ - API endpoint documentation
+ - Frontend integration guide
+ - Security best practices
+ - Troubleshooting guide
+ - Migration instructions
\ No newline at end of file
dev/plans/2507121448-cloudflare-d1-database.md +403 -0
@@ @@ -0,0 +1,403 @@
+ # Feature Plan: Cloudflare D1 Database Implementation
+
+ **Status**: Planning
+ **Created**: 2025-07-12
+ **Author**: Claude
+ **Priority**: Phase 2 - Implement alongside authentication
+
+ ## Overview
+
+ Implement Cloudflare D1 database to provide persistent storage for users, quizzes, results, and AI generation tracking. D1 is Cloudflare's serverless SQL database built on SQLite, providing global read replicas and seamless integration with Workers.
+
+ ## Requirements
+
+ - Store user accounts with tier information
+ - Store quiz definitions (both AI-generated and uploaded)
+ - Track quiz results and user progress
+ - Monitor AI usage for billing
+ - Support public/private quiz visibility
+ - Enable quiz sharing and collaboration
+ - Maintain data integrity with foreign keys
+ - Support efficient querying and pagination
+
+ ## Database Schema
+
+ ### Core Tables
+
+ ```sql
+ -- Users table (extended from auth plan)
+ CREATE TABLE users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ email TEXT UNIQUE NOT NULL,
+ password_hash TEXT NOT NULL,
+ name TEXT NOT NULL,
+ tier TEXT DEFAULT 'free' CHECK(tier IN ('free', 'premium', 'enterprise')),
+ email_verified BOOLEAN DEFAULT false,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ last_login DATETIME,
+ ai_credits INTEGER DEFAULT 0,
+ stripe_customer_id TEXT,
+ stripe_subscription_id TEXT,
+ preferences JSON DEFAULT '{}',
+ avatar_url TEXT
+ );
+
+ -- Quizzes table
+ CREATE TABLE quizzes (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ title TEXT NOT NULL,
+ description TEXT,
+ content JSON NOT NULL, -- Full quiz JSON structure
+ thumbnail_url TEXT,
+ is_public BOOLEAN DEFAULT false,
+ is_published BOOLEAN DEFAULT false,
+ source TEXT NOT NULL CHECK(source IN ('ai', 'upload', 'paste', 'template')),
+ ai_generation_id INTEGER,
+ question_count INTEGER GENERATED ALWAYS AS (json_array_length(json_extract(content, '$.subjects[0].questions'))) STORED,
+ difficulty TEXT CHECK(difficulty IN ('beginner', 'intermediate', 'advanced')),
+ tags JSON DEFAULT '[]',
+ views INTEGER DEFAULT 0,
+ likes INTEGER DEFAULT 0,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ FOREIGN KEY (ai_generation_id) REFERENCES ai_generations(id)
+ );
+
+ -- Quiz results/attempts
+ CREATE TABLE results (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER,
+ quiz_id INTEGER NOT NULL,
+ score INTEGER NOT NULL,
+ total_questions INTEGER NOT NULL,
+ percentage REAL GENERATED ALWAYS AS (CAST(score AS REAL) / total_questions * 100) STORED,
+ answers JSON NOT NULL, -- {questionId: selectedAnswer, ...}
+ time_taken INTEGER, -- seconds
+ ip_address TEXT,
+ user_agent TEXT,
+ started_at DATETIME NOT NULL,
+ completed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
+ FOREIGN KEY (quiz_id) REFERENCES quizzes(id) ON DELETE CASCADE
+ );
+
+ -- AI Generation tracking
+ CREATE TABLE ai_generations (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER NOT NULL,
+ conversation JSON NOT NULL, -- Full chat history
+ final_prompt TEXT NOT NULL,
+ model TEXT NOT NULL,
+ tokens_used INTEGER NOT NULL,
+ cost_cents INTEGER, -- Track cost in cents
+ status TEXT CHECK(status IN ('pending', 'completed', 'failed')),
+ error_message TEXT,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ completed_at DATETIME,
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
+ );
+
+ -- Quiz sharing
+ CREATE TABLE quiz_shares (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ quiz_id INTEGER NOT NULL,
+ shared_by INTEGER NOT NULL,
+ share_code TEXT UNIQUE NOT NULL,
+ access_level TEXT DEFAULT 'view' CHECK(access_level IN ('view', 'copy', 'edit')),
+ expires_at DATETIME,
+ max_uses INTEGER,
+ use_count INTEGER DEFAULT 0,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (quiz_id) REFERENCES quizzes(id) ON DELETE CASCADE,
+ FOREIGN KEY (shared_by) REFERENCES users(id) ON DELETE CASCADE
+ );
+
+ -- User favorites
+ CREATE TABLE favorites (
+ user_id INTEGER NOT NULL,
+ quiz_id INTEGER NOT NULL,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (user_id, quiz_id),
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ FOREIGN KEY (quiz_id) REFERENCES quizzes(id) ON DELETE CASCADE
+ );
+
+ -- Analytics events
+ CREATE TABLE analytics_events (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id INTEGER,
+ event_type TEXT NOT NULL,
+ event_data JSON,
+ ip_address TEXT,
+ user_agent TEXT,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
+ );
+ ```
+
+ ### Indexes for Performance
+
+ ```sql
+ -- User queries
+ CREATE INDEX idx_users_email ON users(email);
+ CREATE INDEX idx_users_tier ON users(tier);
+ CREATE INDEX idx_users_created_at ON users(created_at);
+
+ -- Quiz queries
+ CREATE INDEX idx_quizzes_user_id ON quizzes(user_id);
+ CREATE INDEX idx_quizzes_public ON quizzes(is_public, is_published);
+ CREATE INDEX idx_quizzes_created_at ON quizzes(created_at);
+ CREATE INDEX idx_quizzes_source ON quizzes(source);
+
+ -- Results queries
+ CREATE INDEX idx_results_user_id ON results(user_id);
+ CREATE INDEX idx_results_quiz_id ON results(quiz_id);
+ CREATE INDEX idx_results_completed_at ON results(completed_at);
+
+ -- AI usage queries
+ CREATE INDEX idx_ai_generations_user_id ON ai_generations(user_id);
+ CREATE INDEX idx_ai_generations_created_at ON ai_generations(created_at);
+
+ -- Share code lookup
+ CREATE INDEX idx_quiz_shares_code ON quiz_shares(share_code);
+ ```
+
+ ## Implementation Tasks
+
+ - [ ] Set up D1 database in Cloudflare dashboard
+ - [ ] Create migration system for schema updates
+ - [ ] Write initial schema migration
+ - [ ] Create database connection utilities
+ - [ ] Implement query builder helpers
+ - [ ] Build user CRUD operations
+ - [ ] Build quiz CRUD operations
+ - [ ] Implement result tracking
+ - [ ] Create AI usage tracking
+ - [ ] Add quiz sharing functionality
+ - [ ] Implement analytics tracking
+ - [ ] Create backup strategy
+ - [ ] Add data validation layer
+ - [ ] Implement pagination utilities
+ - [ ] Test database performance
+ - [ ] Document query patterns
+
+ ## API Integration
+
+ ### Database Connection
+ ```javascript
+ // db/connection.js
+ export class Database {
+ constructor(env) {
+ this.db = env.DB
+ }
+
+ async query(sql, params = []) {
+ const stmt = this.db.prepare(sql)
+ return params.length > 0 ? stmt.bind(...params) : stmt
+ }
+
+ async transaction(callback) {
+ // D1 doesn't support transactions yet
+ // Implement optimistic locking pattern
+ return callback(this)
+ }
+ }
+ ```
+
+ ### Query Patterns
+
+ #### Create Quiz
+ ```javascript
+ async function createQuiz(userId, quizData) {
+ const { title, description, content, source } = quizData
+
+ const result = await db.query(`
+ INSERT INTO quizzes (user_id, title, description, content, source)
+ VALUES (?, ?, ?, ?, ?)
+ RETURNING *
+ `, [userId, title, description, JSON.stringify(content), source]).first()
+
+ return result
+ }
+ ```
+
+ #### Get User's Quizzes with Pagination
+ ```javascript
+ async function getUserQuizzes(userId, page = 1, limit = 10) {
+ const offset = (page - 1) * limit
+
+ const quizzes = await db.query(`
+ SELECT q.*,
+ COUNT(r.id) as attempt_count,
+ AVG(r.percentage) as avg_score
+ FROM quizzes q
+ LEFT JOIN results r ON q.id = r.quiz_id
+ WHERE q.user_id = ?
+ GROUP BY q.id
+ ORDER BY q.created_at DESC
+ LIMIT ? OFFSET ?
+ `, [userId, limit, offset]).all()
+
+ const total = await db.query(
+ 'SELECT COUNT(*) as count FROM quizzes WHERE user_id = ?',
+ [userId]
+ ).first()
+
+ return {
+ quizzes,
+ pagination: {
+ page,
+ limit,
+ total: total.count,
+ pages: Math.ceil(total.count / limit)
+ }
+ }
+ }
+ ```
+
+ #### Track AI Usage
+ ```javascript
+ async function trackAIUsage(userId, conversation, tokensUsed) {
+ // Start tracking
+ const generation = await db.query(`
+ INSERT INTO ai_generations (user_id, conversation, tokens_used, status)
+ VALUES (?, ?, ?, 'pending')
+ RETURNING id
+ `, [userId, JSON.stringify(conversation), tokensUsed]).first()
+
+ // Update user credits
+ await db.query(`
+ UPDATE users
+ SET ai_credits = ai_credits - 1
+ WHERE id = ? AND ai_credits > 0
+ `, [userId]).run()
+
+ return generation.id
+ }
+ ```
+
+ ## Migration System
+
+ ```javascript
+ // migrations/001_initial_schema.js
+ export const up = async (db) => {
+ await db.exec(`
+ CREATE TABLE IF NOT EXISTS migrations (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT UNIQUE NOT NULL,
+ applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
+ )
+ `)
+
+ // Create all tables
+ await db.exec(INITIAL_SCHEMA)
+ }
+
+ export const down = async (db) => {
+ // Drop all tables
+ const tables = ['users', 'quizzes', 'results', ...]
+ for (const table of tables) {
+ await db.exec(`DROP TABLE IF EXISTS ${table}`)
+ }
+ }
+ ```
+
+ ## Data Validation
+
+ ```javascript
+ // validation/quiz.js
+ import { z } from 'zod'
+
+ export const QuizSchema = z.object({
+ title: z.string().min(3).max(200),
+ description: z.string().max(1000).optional(),
+ subjects: z.array(z.object({
+ name: z.string(),
+ questions: z.array(z.object({
+ id: z.number(),
+ question: z.string(),
+ options: z.array(z.string()).length(4),
+ correctAnswer: z.number().min(0).max(3)
+ }))
+ }))
+ })
+
+ export function validateQuiz(data) {
+ return QuizSchema.parse(data)
+ }
+ ```
+
+ ## Performance Considerations
+
+ 1. **Query Optimization**
+ - Use prepared statements
+ - Implement query result caching
+ - Batch operations where possible
+ - Use appropriate indexes
+
+ 2. **Data Limits**
+ - Quiz content: 1MB max (JSON)
+ - Results per query: 100 max
+ - Batch inserts: 1000 records max
+
+ 3. **Caching Strategy**
+ - Cache public quizzes in KV
+ - Cache user sessions
+ - Cache aggregated statistics
+
+ ## Backup & Recovery
+
+ 1. **Automated Backups**
+ - Daily exports to R2 storage
+ - Point-in-time recovery
+ - Cross-region replication
+
+ 2. **Data Export**
+ ```javascript
+ // Export user data for GDPR
+ async function exportUserData(userId) {
+ const data = {
+ user: await db.query('SELECT * FROM users WHERE id = ?', [userId]).first(),
+ quizzes: await db.query('SELECT * FROM quizzes WHERE user_id = ?', [userId]).all(),
+ results: await db.query('SELECT * FROM results WHERE user_id = ?', [userId]).all()
+ }
+ return data
+ }
+ ```
+
+ ## Monitoring
+
+ - Query performance metrics
+ - Database size tracking
+ - Error rate monitoring
+ - Usage patterns analysis
+ - Cost tracking
+
+ ## Security
+
+ - SQL injection prevention via parameterized queries
+ - Input sanitization
+ - Row-level security for multi-tenancy
+ - Encrypted sensitive data
+ - Audit logging for compliance
+
+ ## Future Enhancements
+
+ - Full-text search on quiz content
+ - Real-time collaboration
+ - Version history for quizzes
+ - Advanced analytics
+ - Data warehousing for insights
+ - GraphQL API layer
+
+ ## Documentation Plan
+
+ After implementation, create `dev/docs/2507121448-d1-database.md` with:
+ - Database schema documentation
+ - Query optimization guide
+ - Migration instructions
+ - Backup procedures
+ - Performance tuning tips
\ No newline at end of file