feat: Add AI guide with copy-paste prompts and JSON paste functionality

Torey Heinz committed Jul 12, 2025
commit f4c202bbe0f5aadd42798dc23ff99e790e1c22df
Showing 1 changed file with 410 additions and 31 deletions
src/views/Home.vue +410 -31
@@ @@ -17,21 +17,109 @@
</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>
+ <h2>Create Custom Assessment</h2>
+ <p>Upload a JSON file or paste JSON content to create a custom assessment</p>
+
+ <div class="tab-buttons">
+ <button
+ @click="activeTab = 'upload'"
+ :class="{ active: activeTab === 'upload' }"
+ class="tab-button"
+ >
+ Upload File
+ </button>
+ <button
+ @click="activeTab = 'paste'"
+ :class="{ active: activeTab === 'paste' }"
+ class="tab-button"
+ >
+ Paste JSON
+ </button>
+ <button
+ @click="activeTab = 'guide'"
+ :class="{ active: activeTab === 'guide' }"
+ class="tab-button"
+ >
+ AI Guide
+ </button>
+ </div>
+
+ <div v-if="activeTab === 'upload'" class="tab-content">
+ <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>
+
+ <div v-if="activeTab === 'paste'" class="tab-content">
+ <textarea
+ v-model="pastedJson"
+ placeholder="Paste your assessment JSON here..."
+ class="json-textarea"
+ rows="10"
+ ></textarea>
+ <button @click="handlePastedJson" class="submit-button">
+ Create Assessment
+ </button>
+ </div>
+
+ <div v-if="activeTab === 'guide'" class="tab-content guide-content">
+ <h3>How to Create Assessments with AI</h3>
+
+ <div class="instructions">
+ <p><strong>Step 1:</strong> Copy one of the example prompts below</p>
+ <p><strong>Step 2:</strong> Paste it into your AI chat client (ChatGPT, Claude, etc.)</p>
+ <p><strong>Step 3:</strong> Copy the generated JSON from the AI's response</p>
+ <p><strong>Step 4:</strong> Switch to the "Paste JSON" tab and paste it there</p>
+ </div>
+
+ <h4>Example Prompts</h4>
+
+ <div class="prompt-example">
+ <h5>Example 1: Science Assessment</h5>
+ <button @click="copyToClipboard(sciencePrompt)" class="copy-button">Copy Prompt</button>
+ <pre>{{ sciencePrompt }}</pre>
+ </div>
+
+ <div class="prompt-example">
+ <h5>Example 2: Math Assessment</h5>
+ <button @click="copyToClipboard(mathPrompt)" class="copy-button">Copy Prompt</button>
+ <pre>{{ mathPrompt }}</pre>
+ </div>
+
+ <div class="prompt-example">
+ <h5>Example 3: Language Assessment</h5>
+ <button @click="copyToClipboard(languagePrompt)" class="copy-button">Copy Prompt</button>
+ <pre>{{ languagePrompt }}</pre>
+ </div>
+
+ <div class="prompt-example">
+ <h5>Example 4: Custom Assessment</h5>
+ <button @click="copyToClipboard(customPrompt)" class="copy-button">Copy Prompt</button>
+ <pre>{{ customPrompt }}</pre>
+ </div>
+
+ <div class="tips">
+ <h4>Tips for Best Results:</h4>
+ <ul>
+ <li>Be specific about the topics you want covered</li>
+ <li>Specify the difficulty level clearly</li>
+ <li>Always verify the AI's answers are correct</li>
+ <li>Start with fewer questions to test the format</li>
+ <li>Make sure the JSON is valid before pasting</li>
+ </ul>
+ </div>
</div>
+
<div v-if="uploadError" class="error-message">
{{ uploadError }}
</div>
@@ @@ -63,6 +151,88 @@ const availableAssessments = ref([
const uploadedAssessments = ref([])
const uploadError = ref('')
const fileInput = ref(null)
+ const activeTab = ref('upload')
+ const pastedJson = ref('')
+
+ const jsonStructure = `{
+ "title": "Assessment title",
+ "description": "Brief description",
+ "subjects": [
+ {
+ "name": "Subject name",
+ "questions": [
+ {
+ "id": unique number,
+ "question": "Question text?",
+ "options": ["Option A", "Option B", "Option C", "Option D"],
+ "correctAnswer": index (0-3)
+ }
+ ]
+ }
+ ]
+ }`
+
+ const importantNotes = `IMPORTANT:
+ - Each question must have exactly 4 options
+ - correctAnswer must be the array index (0-3) of the correct option
+ - All question IDs must be unique numbers
+ - Ensure the JSON is valid and properly formatted`
+
+ const sciencePrompt = `Create a JSON assessment file for Environmental Science suitable for high school students (grades 9-10).
+
+ The assessment should:
+ - Have 20 total questions
+ - Be divided into 4 subjects: Climate Change, Ecosystems, Pollution, and Conservation
+ - Include multiple choice questions only
+ - Cover basic concepts and current environmental issues
+ - Be at intermediate level
+
+ Format the output as a valid JSON file following this exact structure:
+ ${jsonStructure}
+
+ ${importantNotes}`
+
+ const mathPrompt = `Create a JSON assessment file for Algebra I suitable for 8th-9th grade students.
+
+ The assessment should:
+ - Have 24 total questions
+ - Be divided into 4 subjects: Linear Equations, Quadratic Functions, Systems of Equations, and Polynomials
+ - Include multiple choice questions only
+ - Include both conceptual and computational questions
+ - Be at intermediate level with some challenging problems
+
+ Format the output as a valid JSON file following this exact structure:
+ ${jsonStructure}
+
+ ${importantNotes}`
+
+ const languagePrompt = `Create a JSON assessment file for Spanish Language (Level 2) suitable for intermediate learners.
+
+ The assessment should:
+ - Have 20 total questions
+ - Be divided into 4 subjects: Vocabulary, Grammar, Reading Comprehension, and Cultural Knowledge
+ - Include multiple choice questions only
+ - Focus on present and past tenses, common vocabulary, and Hispanic culture
+ - Be at intermediate level
+
+ Format the output as a valid JSON file following this exact structure:
+ ${jsonStructure}
+
+ ${importantNotes}`
+
+ const customPrompt = `Create a JSON assessment file for [YOUR SUBJECT HERE] suitable for [YOUR AUDIENCE HERE].
+
+ The assessment should:
+ - Have [NUMBER] total questions
+ - Be divided into [NUMBER] subjects: [LIST YOUR TOPICS HERE]
+ - Include multiple choice questions only
+ - Cover [DESCRIBE WHAT TO COVER]
+ - Be at [DIFFICULTY LEVEL] level
+
+ Format the output as a valid JSON file following this exact structure:
+ ${jsonStructure}
+
+ ${importantNotes}`
const allAssessments = computed(() => {
return [...availableAssessments.value, ...uploadedAssessments.value]
@@ @@ -111,6 +281,27 @@ const validateAssessment = (data) => {
return true
}
+ const createAssessment = (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))
+
+ return assessment
+ }
+
const handleFileUpload = async (event) => {
uploadError.value = ''
const file = event.target.files[0]
@@ @@ -122,35 +313,69 @@ const handleFileUpload = async (event) => {
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))
+ createAssessment(data)
// Clear the file input
if (fileInput.value) {
fileInput.value.value = ''
}
+ uploadError.value = ''
+
} catch (error) {
uploadError.value = error.message || 'Failed to parse JSON file'
console.error('Upload error:', error)
}
}
+ const handlePastedJson = () => {
+ uploadError.value = ''
+
+ if (!pastedJson.value.trim()) {
+ uploadError.value = 'Please paste JSON content'
+ return
+ }
+
+ try {
+ const data = JSON.parse(pastedJson.value)
+ validateAssessment(data)
+ createAssessment(data)
+
+ // Clear the textarea
+ pastedJson.value = ''
+ uploadError.value = ''
+
+ // Switch to upload tab to show success
+ activeTab.value = 'upload'
+
+ } catch (error) {
+ uploadError.value = error.message || 'Failed to parse JSON content'
+ console.error('Paste error:', error)
+ }
+ }
+
+ const copyToClipboard = async (text) => {
+ try {
+ await navigator.clipboard.writeText(text)
+ // You could add a success notification here
+ } catch (err) {
+ console.error('Failed to copy:', err)
+ // Fallback for older browsers
+ const textArea = document.createElement('textarea')
+ textArea.value = text
+ textArea.style.position = 'fixed'
+ textArea.style.left = '-999999px'
+ document.body.appendChild(textArea)
+ textArea.select()
+ try {
+ document.execCommand('copy')
+ } catch (err) {
+ console.error('Fallback copy failed:', err)
+ }
+ document.body.removeChild(textArea)
+ }
+ }
+
onMounted(() => {
loadUploadedAssessments()
})
@@ @@ -278,5 +503,159 @@ p {
background: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 4px;
+ margin-top: 1rem;
+ }
+
+ .tab-buttons {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 1.5rem;
+ border-bottom: 2px solid #e0e0e0;
+ }
+
+ .tab-button {
+ padding: 0.75rem 1.5rem;
+ background: none;
+ border: none;
+ border-bottom: 2px solid transparent;
+ color: #666;
+ cursor: pointer;
+ font-size: 1rem;
+ transition: all 0.2s;
+ }
+
+ .tab-button:hover {
+ color: #333;
+ }
+
+ .tab-button.active {
+ color: #42b883;
+ border-bottom-color: #42b883;
+ }
+
+ .tab-content {
+ padding: 1rem 0;
+ }
+
+ .json-textarea {
+ width: 100%;
+ padding: 1rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-family: 'Courier New', monospace;
+ font-size: 0.9rem;
+ resize: vertical;
+ min-height: 200px;
+ }
+
+ .submit-button {
+ margin-top: 1rem;
+ padding: 0.75rem 1.5rem;
+ background: #42b883;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 1rem;
+ transition: background-color 0.2s;
+ }
+
+ .submit-button:hover {
+ background: #35a372;
+ }
+
+ .guide-content h3 {
+ color: #2c3e50;
+ margin-bottom: 1rem;
+ }
+
+ .guide-content h4 {
+ color: #2c3e50;
+ margin: 1.5rem 0 1rem;
+ }
+
+ .guide-content h5 {
+ color: #333;
+ margin-bottom: 0.5rem;
+ }
+
+ .instructions {
+ background: #f0f9ff;
+ padding: 1rem;
+ border-radius: 4px;
+ margin-bottom: 1.5rem;
+ }
+
+ .instructions p {
+ margin: 0.5rem 0;
+ color: #666;
+ }
+
+ .prompt-example {
+ margin-bottom: 2rem;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+ overflow: hidden;
+ }
+
+ .prompt-example h5 {
+ background: #f8f9fa;
+ padding: 0.75rem 1rem;
+ margin: 0;
+ border-bottom: 1px solid #e0e0e0;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .copy-button {
+ padding: 0.5rem 1rem;
+ background: #42b883;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 0.875rem;
+ transition: background-color 0.2s;
+ float: right;
+ margin-left: 1rem;
+ }
+
+ .copy-button:hover {
+ background: #35a372;
+ }
+
+ .prompt-example pre {
+ margin: 0;
+ padding: 1rem;
+ background: #f8f9fa;
+ overflow-x: auto;
+ font-family: 'Courier New', monospace;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ }
+
+ .tips {
+ background: #f0f9ff;
+ padding: 1rem;
+ border-radius: 4px;
+ margin-top: 1.5rem;
+ }
+
+ .tips h4 {
+ color: #2c3e50;
+ margin-bottom: 0.5rem;
+ }
+
+ .tips ul {
+ margin: 0;
+ padding-left: 1.5rem;
+ color: #666;
+ }
+
+ .tips li {
+ margin-bottom: 0.25rem;
}
</style>