Files
homeschool/public/admin.html
T
nmemmertandClaude Sonnet 4.6 a10239ead9 Remove page scanning / Ollama AI vision feature
Removes the scan-a-page workflow (photo → lesson/quiz content), all
Ollama/cloud provider code, the scan settings panel, and the related
DB seed values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-04 13:52:55 -04:00

522 lines
28 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parent Dashboard</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<section id="pin-gate" class="view parent">
<h1>👩‍🏫 Parent Dashboard</h1>
<form id="pin-form">
<input id="pin-input" type="password" inputmode="numeric" placeholder="Enter PIN" aria-label="Parent PIN">
<button type="submit">Unlock</button>
</form>
<p id="pin-error" class="error" hidden>Wrong PIN, try again.</p>
<p class="hint">Default PIN is 1234 — change it in Settings after unlocking.</p>
<p><a class="link-btn" href="index.html">← Back to kid picker</a></p>
</section>
<div id="console" hidden>
<header class="admin-header">
<a href="index.html" class="admin-logo">🎓 Homeschool</a>
<nav class="admin-nav">
<button class="nav-pill active" data-panel="kids">🧒 Kids</button>
<button class="nav-pill" data-panel="courses">📚 Courses</button>
<button class="nav-pill" data-panel="planner">🗓️ Planner</button>
<button class="nav-pill" data-panel="grading">✅ Grading</button>
<button class="nav-pill" data-panel="gradebook">📊 Gradebook</button>
<button class="nav-pill" data-panel="spelling">✏️ Spelling</button>
<button class="nav-pill" data-panel="decks">🗂️ Flashcards</button>
<button class="nav-pill" data-panel="settings">⚙️ Settings</button>
</nav>
</header>
<main class="admin-main">
<!-- Kids -->
<section id="panel-kids" class="panel">
<h1>Kids</h1>
<div class="panel-cols">
<div class="panel-col">
<div id="student-rows"></div>
</div>
<div class="panel-col">
<div class="admin-card">
<h2 style="margin:0 0 0.75rem">Add a kid</h2>
<form id="add-student-form" class="inline-form" style="flex-direction:column">
<input id="new-student-name" placeholder="Name" aria-label="New kid name">
<select id="new-student-emoji" aria-label="Avatar">
<option>🦊</option><option>🐸</option><option>🦄</option><option>🐯</option>
<option>🐼</option><option>🦖</option><option>🐙</option><option>🦉</option>
<option>🐻</option><option>🐰</option><option>🦁</option><option>🐶</option>
</select>
<button type="submit">Add kid</button>
</form>
</div>
</div>
</div>
</section>
<!-- Courses list -->
<section id="panel-courses" class="panel">
<h1>Courses</h1>
<div class="panel-cols">
<div class="panel-col">
<div id="course-rows"></div>
</div>
<div class="panel-col">
<div class="admin-card">
<h2 style="margin:0 0 0.75rem">Add a course</h2>
<form id="add-course-form" class="inline-form" style="flex-direction:column">
<input id="new-course-name" placeholder="Course name (e.g. Science 3)" aria-label="Course name">
<input id="new-course-subject" placeholder="Subject" aria-label="Subject">
<input id="new-course-color" type="color" value="#4f86f7" aria-label="Color">
<button type="submit">Add course</button>
</form>
</div>
<div class="admin-card" style="margin-top:1rem">
<h2 style="margin:0 0 0.75rem">Import course</h2>
<!-- mode tabs -->
<div style="display:flex;gap:0.5rem;margin-bottom:1rem">
<button type="button" class="import-tab-btn secondary" data-tab="json" style="flex:1">📄 From JSON</button>
<button type="button" class="import-tab-btn secondary" data-tab="docx" style="flex:1">📝 From Word docs</button>
</div>
<!-- JSON import -->
<div id="import-tab-json">
<p class="hint" style="margin:0 0 0.75rem">Import a <code>.json</code> bundle previously exported from this app.</p>
<input type="file" id="import-course-file" accept=".json" hidden>
<div class="inline-form" style="align-items:center;gap:0.5rem">
<button type="button" id="import-course-browse" class="secondary">📂 Choose file…</button>
<span id="import-course-filename" class="hint" style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"></span>
<button type="button" id="import-course-go" class="secondary" disabled>📥 Import</button>
</div>
</div>
<!-- Word docx import -->
<div id="import-tab-docx" hidden>
<p class="hint" style="margin:0 0 0.75rem">Select all unit <code>.docx</code> files at once — one file per unit, sorted by filename.</p>
<div class="inline-form" style="flex-direction:column;gap:0.5rem">
<input id="import-docx-name" placeholder="Course name (required)" aria-label="Course name" style="width:100%">
<input id="import-docx-subject" placeholder="Subject (optional)" aria-label="Subject" style="width:100%">
<div style="display:flex;align-items:center;gap:0.5rem">
<label style="font-size:0.85rem">Color</label>
<input id="import-docx-color" type="color" value="#4f86f7" aria-label="Color">
</div>
</div>
<input type="file" id="import-docx-files" accept=".docx" multiple hidden>
<div class="inline-form" style="align-items:center;gap:0.5rem;margin-top:0.5rem">
<button type="button" id="import-docx-browse" class="secondary">📂 Choose .docx files…</button>
<span id="import-docx-filenames" class="hint" style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"></span>
<button type="button" id="import-docx-go" class="secondary" disabled>📥 Import</button>
</div>
</div>
<p id="import-course-status" class="hint" style="margin:0.5rem 0 0"></p>
</div>
</div>
</div>
</section>
<!-- Course detail (units + items + roster) -->
<section id="panel-course-detail" class="panel" hidden>
<button class="back-btn" data-nav="courses">← All courses</button>
<h1 id="cd-name"></h1>
<div class="admin-card">
<h2>Course info</h2>
<form id="course-meta-form" class="inline-form">
<input id="cd-name-input" placeholder="Course name">
<input id="cd-subject-input" placeholder="Subject">
<input id="cd-color-input" type="color">
<label class="checkbox-label"><input type="checkbox" id="cd-archived"> Archived</label>
<button type="submit">Save</button>
<button type="button" id="cd-duplicate" class="secondary">Duplicate course</button>
<button type="button" id="cd-export" class="secondary">⬇ Export course</button>
<button type="button" id="cd-delete" class="danger">Delete course</button>
</form>
</div>
<div class="admin-card">
<h2>Roster</h2>
<div id="cd-roster"></div>
</div>
<div class="admin-card">
<h2>Units &amp; content <button type="button" id="cd-print" class="secondary small" style="float:right">🖨 Print course</button></h2>
<div id="cd-units"></div>
<form id="add-unit-form" class="inline-form">
<input id="new-unit-name" placeholder="Unit name (e.g. Week 1: Weather)" aria-label="Unit name">
<button type="submit">Add unit</button>
</form>
</div>
</section>
<!-- Item editor (lesson / assignment / quiz / spelling / flashcards) -->
<section id="panel-item-editor" class="panel" hidden>
<button class="back-btn" data-nav="course-detail">← Back to course</button>
<h1 id="ie-heading">New item</h1>
<form id="item-form" class="admin-card">
<input type="hidden" id="ie-id">
<input type="hidden" id="ie-unit-id">
<label>Type
<select id="ie-type">
<option value="lesson">📖 Lesson</option>
<option value="assignment">📝 Assignment</option>
<option value="quiz">❓ Quiz</option>
<option value="spelling_practice">✏️ Spelling Practice</option>
<option value="spelling_test">⭐ Spelling Test</option>
<option value="matching">🔤 Matching</option>
<option value="crossword">⬛ Crossword</option>
<option value="flashcards">🗂️ Flashcards</option>
<option value="worksheet">📋 Worksheet</option>
</select>
</label>
<label>Title <input id="ie-title" placeholder="Item title"></label>
<div class="inline-form">
<label>Due date <input type="date" id="ie-due-date"></label>
<label class="checkbox-label"><input type="checkbox" id="ie-allow-retakes"> Allow retakes</label>
</div>
<label>Prerequisite item (must be done first)
<select id="ie-prereq"><option value="">None</option></select>
</label>
<div class="inline-form">
<label>Evidence from student
<select id="ie-evidence-mode">
<option value="none">None</option>
<option value="optional">Optional</option>
<option value="required">Required</option>
</select>
</label>
<label>Retake score policy
<select id="ie-retake-policy">
<option value="latest">Latest attempt</option>
<option value="highest">Highest score</option>
<option value="average">Average of all</option>
</select>
</label>
</div>
<!-- Import from .docx file: visible for lesson, assignment, quiz types -->
<div id="ie-import-section" class="scan-card" hidden>
<strong>📂 Import from .docx</strong>
<input type="file" id="import-local-input" accept=".docx" hidden>
<div class="scan-controls">
<button type="button" id="import-browse-btn" class="secondary">📂 Choose file…</button>
<span id="import-local-name" class="hint" style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"></span>
<button type="button" id="import-go-btn" class="secondary" disabled>📥 Import</button>
</div>
<p id="import-status" class="hint"></p>
</div>
<div id="ie-field-lesson" class="ie-field rte-group">
<div class="rte-label">Lesson content</div>
<div class="rte-toolbar">
<button type="button" class="rte-btn" data-cmd="bold" title="Bold"><b>B</b></button>
<button type="button" class="rte-btn" data-cmd="italic" title="Italic"><i>I</i></button>
<button type="button" class="rte-btn" data-cmd="underline" title="Underline"><u>U</u></button>
<span class="rte-sep"></span>
<button type="button" class="rte-btn" data-cmd="formatBlock" data-val="h2" title="Heading">H2</button>
<button type="button" class="rte-btn" data-cmd="formatBlock" data-val="h3" title="Sub-heading">H3</button>
<button type="button" class="rte-btn" data-cmd="formatBlock" data-val="p" title="Paragraph"></button>
<span class="rte-sep"></span>
<button type="button" class="rte-btn" data-cmd="insertUnorderedList" title="Bullet list">• List</button>
<button type="button" class="rte-btn" data-cmd="insertOrderedList" title="Numbered list">1. List</button>
<span class="rte-sep"></span>
<button type="button" class="rte-btn rte-link-btn" title="Add link">🔗 Link</button>
<button type="button" class="rte-btn rte-youtube-btn" title="Embed YouTube">▶ YouTube</button>
</div>
<div id="ie-body-lesson" class="rte-editor lesson-body" contenteditable="true"
data-placeholder="Weather is what the sky and air are doing right now.&#10;Add images, YouTube embeds, and more."></div>
</div>
<div id="ie-field-assignment" class="ie-field rte-group">
<div class="rte-label">Instructions</div>
<div class="rte-toolbar">
<button type="button" class="rte-btn" data-cmd="bold" title="Bold"><b>B</b></button>
<button type="button" class="rte-btn" data-cmd="italic" title="Italic"><i>I</i></button>
<button type="button" class="rte-btn" data-cmd="underline" title="Underline"><u>U</u></button>
<span class="rte-sep"></span>
<button type="button" class="rte-btn" data-cmd="formatBlock" data-val="h2" title="Heading">H2</button>
<button type="button" class="rte-btn" data-cmd="formatBlock" data-val="h3" title="Sub-heading">H3</button>
<button type="button" class="rte-btn" data-cmd="formatBlock" data-val="p" title="Paragraph"></button>
<span class="rte-sep"></span>
<button type="button" class="rte-btn" data-cmd="insertUnorderedList" title="Bullet list">• List</button>
<button type="button" class="rte-btn" data-cmd="insertOrderedList" title="Numbered list">1. List</button>
<span class="rte-sep"></span>
<button type="button" class="rte-btn rte-link-btn" title="Add link">🔗 Link</button>
<button type="button" class="rte-btn rte-youtube-btn" title="Embed YouTube">▶ YouTube</button>
</div>
<div id="ie-body-assignment" class="rte-editor lesson-body" contenteditable="true"
data-placeholder="Draw today's weather and write one sentence."></div>
<label style="margin-top:.75rem">Points possible <input id="ie-points" type="number" min="0" value="10"></label>
</div>
<div id="ie-field-quiz" class="ie-field">
<label>Instructions <small style="color:#888">(optional — shown to student before questions)</small>
<textarea id="ie-quiz-instructions" rows="2" placeholder="e.g. Choose the best answer for each question." style="width:100%;margin-top:.25rem;resize:vertical"></textarea>
</label>
<div class="inline-form" style="margin:.5rem 0">
<select id="ie-template-select"><option value="">— load from template —</option></select>
<button type="button" id="ie-load-template" class="secondary">Load</button>
<button type="button" id="ie-save-template" class="secondary">Save as template</button>
</div>
<div id="ie-questions"></div>
<button type="button" id="ie-add-question" class="secondary">+ Add question</button>
</div>
<div id="ie-field-matching" class="ie-field">
<label>Instructions <small style="color:#888">(optional — shown to student before the word bank)</small>
<textarea id="ie-matching-instructions" rows="2" placeholder="e.g. Match each definition to the correct term." style="width:100%;margin-top:.25rem;resize:vertical"></textarea>
</label>
<p class="hint" style="margin:.5rem 0">Enter word-clue pairs. Words are shown as A, B, C… (alphabetical order) in the word bank.</p>
<div id="ie-pairs"></div>
<button type="button" id="ie-add-pair" class="secondary">+ Add pair</button>
</div>
<div id="ie-field-crossword" class="ie-field" hidden>
<div class="cw-entry-cols">
<div>
<h3 style="margin-bottom:.5rem">Across clues</h3>
<div id="ie-across-clues"></div>
<button type="button" id="ie-add-across" class="secondary small">+ Add across clue</button>
</div>
<div>
<h3 style="margin-bottom:.5rem">Down clues</h3>
<div id="ie-down-clues"></div>
<button type="button" id="ie-add-down" class="secondary small">+ Add down clue</button>
</div>
</div>
<div style="margin-top:1rem;display:flex;gap:.75rem;align-items:center;flex-wrap:wrap">
<button type="button" id="ie-crossword-generate" class="secondary">⚡ Generate crossword</button>
<span id="ie-crossword-status" class="hint"></span>
</div>
<div id="ie-crossword-preview" style="margin-top:.75rem;overflow-x:auto"></div>
</div>
<div id="ie-field-spelling" class="ie-field">
<label>Word list <select id="ie-list"></select></label>
</div>
<div id="ie-field-flashcards" class="ie-field">
<label>Deck <select id="ie-deck"></select></label>
</div>
<div id="ie-field-worksheet" class="ie-field" hidden>
<label style="margin-bottom:.25rem">Paste your worksheet text
<textarea id="ie-ws-paste" rows="10" placeholder="Paste your printed worksheet here. The parser handles:&#10;• Name: ____ Date: ____ → header line&#10;• Part 1 — Title → section header&#10;• 1. Sentence with _____ blanks. → fill-in-the-blank&#10;• T F Statement text → true/false question&#10;• 4. Question with no blanks → short answer (parent grades)" style="width:100%;margin-top:.25rem;resize:vertical;font-family:monospace;font-size:.85rem"></textarea>
</label>
<button type="button" id="ie-ws-parse" class="secondary" style="margin:.5rem 0">⚡ Parse worksheet</button>
<p id="ie-ws-parse-status" class="hint" hidden></p>
<div id="ie-ws-editor"></div>
</div>
<div class="row-actions">
<button type="submit">Save item</button>
<button type="button" id="ie-delete" class="danger" hidden>Delete item</button>
</div>
<p id="ie-error" class="error" hidden></p>
</form>
</section>
<!-- Planner -->
<section id="panel-planner" class="panel">
<h1>Planner</h1>
<div class="inline-form">
<label>Kid <select id="planner-student"></select></label>
<button id="planner-view-week" class="secondary active-view">Week</button>
<button id="planner-view-month" class="secondary">Month</button>
<button id="planner-prev" class="secondary">← Prev</button>
<span id="planner-week-label" class="week-label"></span>
<button id="planner-next" class="secondary">Next →</button>
<button id="planner-copy" class="secondary">Copy last week</button>
<button id="planner-print">🖨 Print</button>
<button id="planner-save-all" hidden>💾 Save 0 items</button>
</div>
<details id="auto-schedule-details" class="admin-card" style="margin-bottom:1rem">
<summary style="cursor:pointer;font-weight:600">⚡ Auto-schedule a course</summary>
<div class="inline-form" style="margin-top:.75rem">
<label>Course <select id="auto-course"></select></label>
<label>Start date <input type="date" id="auto-start-date"></label>
<label>Items/day <input type="number" id="auto-items-per-day" value="1" min="1" max="10" style="width:5rem"></label>
<button id="auto-schedule-btn">Schedule</button>
</div>
<p id="auto-schedule-msg" class="hint" style="margin-top:.5rem"></p>
</details>
<div id="planner-grid" class="planner-grid"></div>
</section>
<!-- Grading queue -->
<section id="panel-grading" class="panel">
<h1>Grading Queue</h1>
<div id="grading-rows"></div>
<p id="grading-empty" class="hint" hidden>Nothing waiting to be graded 🎉</p>
<!-- Evidence lightbox -->
<div id="evidence-lightbox" class="modal" hidden>
<div class="modal-box">
<h3>Evidence</h3>
<div id="evidence-lightbox-body"></div>
<button id="evidence-lightbox-close" class="secondary">Close</button>
</div>
</div>
</section>
<!-- Gradebook -->
<section id="panel-gradebook" class="panel">
<h1>Gradebook</h1>
<div class="inline-form">
<label>Course <select id="gradebook-course"></select></label>
<button id="gradebook-csv-btn" class="secondary">⬇ Export CSV</button>
</div>
<div id="gradebook-table"></div>
<h2>Spelling Test History</h2>
<div class="inline-form">
<label>Period <select id="spelling-tests-since">
<option value="">All time</option>
<option value="30">Last 30 days</option>
<option value="90">Last 90 days</option>
</select></label>
<button id="spelling-tests-csv-btn" class="secondary small">⬇ CSV</button>
</div>
<div id="spelling-tests-table"></div>
<div id="history-modal" class="modal" hidden>
<div class="modal-box">
<h3 id="history-modal-title">Attempt History</h3>
<div id="history-modal-body"></div>
<button id="history-modal-close" class="secondary">Close</button>
</div>
</div>
</section>
<!-- Spelling (legacy word lists + weekly assignment) -->
<section id="panel-spelling" class="panel">
<h1>Spelling</h1>
<div class="panel-cols">
<div class="panel-col">
<h2>This week's list per kid</h2>
<div id="assign-rows"></div>
<h2>Word lists</h2>
<div class="filter-bar">
<input id="list-filter" type="search" placeholder="Filter lists…" aria-label="Filter lists">
<button type="button" id="list-show-hidden" class="secondary small" hidden>Show hidden</button>
</div>
<div id="list-rows"></div>
</div>
<div class="panel-col">
<div class="toggle-btns">
<button type="button" id="list-new-btn" class="secondary">+ New list</button>
<button type="button" id="list-bulk-btn" class="secondary">⬆ Bulk import</button>
</div>
<div id="list-editor-section" hidden>
<h3 id="list-editor-title" class="section-form-title">New list</h3>
<form id="list-form" class="admin-card">
<input type="hidden" id="list-id">
<input id="list-name" placeholder="List name (e.g. Week of July 6)" aria-label="List name">
<input id="list-group" placeholder="Group (e.g. Bible 6)" aria-label="Group" list="list-groups-datalist">
<datalist id="list-groups-datalist"></datalist>
<textarea id="list-words" rows="8"
placeholder="One word per line. Paste any list from your curriculum!&#10;&#10;Add an example sentence with |&#10;receive | Did you receive my letter?&#10;&#10;Or just a plain list:&#10;because&#10;enough&#10;friend"></textarea>
<div class="row-actions">
<button type="submit">Save list</button>
<button type="button" id="list-cancel" class="secondary">Cancel</button>
</div>
</form>
</div>
<div id="list-bulk-section" hidden>
<h3 class="section-form-title">Bulk import lists</h3>
<div class="admin-card">
<p class="hint" style="margin:0 0 0.5rem">Start each list with <strong># List name</strong> on its own line, then words below it. Optional group applies to all imported lists.</p>
<input id="list-bulk-group" placeholder="Group (e.g. Bible 6) — optional" aria-label="Group for bulk import" list="list-groups-datalist">
<textarea id="list-bulk-text" rows="12"
placeholder="# Week 1&#10;because&#10;enough&#10;friend&#10;&#10;# Week 2&#10;receive | Did you receive my letter?&#10;achieve&#10;believe"></textarea>
<div class="row-actions">
<button type="button" id="list-bulk-save">Import all lists</button>
<button type="button" id="list-bulk-clear" class="secondary">Clear</button>
</div>
<p id="list-bulk-status" class="hint" style="margin:0.4rem 0 0"></p>
</div>
</div>
</div>
</div>
</section>
<!-- Flashcard decks -->
<section id="panel-decks" class="panel">
<h1>Flashcard Decks</h1>
<div class="panel-cols">
<div class="panel-col">
<div class="filter-bar">
<input id="deck-filter" type="search" placeholder="Filter decks…" aria-label="Filter decks">
</div>
<div id="deck-rows"></div>
</div>
<div class="panel-col">
<div class="toggle-btns">
<button type="button" id="deck-new-btn" class="secondary">+ New deck</button>
<button type="button" id="deck-bulk-btn" class="secondary">⬆ Bulk import</button>
</div>
<div id="deck-editor-section" hidden>
<h3 id="deck-editor-title" class="section-form-title">New deck</h3>
<form id="deck-form" class="admin-card">
<input type="hidden" id="deck-id">
<input id="deck-name" placeholder="Deck name (e.g. State Capitals)" aria-label="Deck name">
<input id="deck-group" placeholder="Group (e.g. Bible 6)" aria-label="Group" list="deck-groups-datalist">
<datalist id="deck-groups-datalist"></datalist>
<textarea id="deck-cards" rows="8"
placeholder="One card per line: front | back&#10;What is the capital of Ohio? | Columbus"></textarea>
<div class="row-actions">
<button type="submit">Save deck</button>
<button type="button" id="deck-cancel" class="secondary">Cancel</button>
</div>
</form>
</div>
<div id="deck-bulk-section" hidden>
<h3 class="section-form-title">Bulk import decks</h3>
<div class="admin-card">
<p class="hint" style="margin:0 0 0.5rem">Start each deck with <strong># Deck name</strong> on its own line, then cards below as <strong>front | back</strong>. Optional group applies to all imported decks.</p>
<input id="deck-bulk-group" placeholder="Group (e.g. Bible 6) — optional" aria-label="Group for bulk import" list="deck-groups-datalist">
<textarea id="deck-bulk-text" rows="12"
placeholder="# Chapter 1 Vocab&#10;What is photosynthesis? | The process plants use to make food&#10;What does chlorophyll do? | Absorbs light&#10;&#10;# Chapter 2 Vocab&#10;What is mitosis? | Cell division producing identical cells"></textarea>
<div class="row-actions">
<button type="button" id="deck-bulk-save">Import all decks</button>
<button type="button" id="deck-bulk-clear" class="secondary">Clear</button>
</div>
<p id="deck-bulk-status" class="hint" style="margin:0.4rem 0 0"></p>
</div>
</div>
</div>
</div>
</section>
<!-- Settings -->
<section id="panel-settings" class="panel">
<h1>Settings</h1>
<form id="pin-change-form" class="inline-form">
<input id="new-pin" type="password" inputmode="numeric" placeholder="New PIN (4+ digits)" aria-label="New PIN">
<button type="submit">Change PIN</button>
</form>
</section>
</main>
</div>
<!-- Floating assign popup -->
<div id="assign-popup" hidden>
<p class="assign-popup-label">Assign to…</p>
<div id="assign-popup-students"></div>
</div>
<div id="toast" role="status" aria-live="polite" hidden></div>
<script src="admin.js"></script>
</body>
</html>