- Replace reply-by-message-ID endpoint with POST /api/admin/messages/:studentId so parent can send without a prior message existing
- Switch GET /api/admin/messages to LEFT JOIN so all students appear in the thread list, not just those who have sent messages
- Remove guard that blocked sending when no messages existed in the thread
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Grading queue now shows short-answer questions and student responses inline so the parent can read answers before scoring
- Student inbox tab shows all graded items with score, percentage, and parent feedback; unread badge appears on login
- Parent-student messaging: students send messages from the Inbox tab; Messages panel in admin shows threads with unread count badge, full conversation history, and reply compose
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each time a student completes a pass through a spelling_practice course
item — initial round, missed-words drill, or a retry — a row is now
written to submission_history so the parent sees individual attempts
instead of a single day-grouped entry.
- app.js: send sessionAttempted/sessionCorrect with /complete calls
- server.js: write to submission_history on each spelling_practice
completion; add GET /api/spelling-sessions endpoint
- admin.html/js: new "Practice Sessions" table shows one row per pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows per-student, per-day practice history in the Spelling panel: date,
time range, word list(s), and first-try correct rate. Clicking a row opens
a detail modal with every word attempted, what the student typed, and
whether it was a first try or retype.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- School name now appears on the student home screen and updates the page title
- Logo can be uploaded in Settings (PNG/JPG/GIF/WebP/SVG, max 2 MB); stored in the data volume so it persists across container restarts
- When a logo is uploaded the text school name is hidden in favour of the logo
- Admin header shows the school name instead of hardcoded "Homeschool", updates live when saved
- New server endpoints: GET /api/logo, POST /api/admin/upload-logo, DELETE /api/admin/logo
- /api/public-settings now includes school_name and has_logo
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parents can set a birthday (MM-DD) per student in the Kids panel. During the student's birthday week (Mon–Sun), a festive overlay appears on login with balloons, birthday cake, confetti, and a personalized message — shown once per day via sessionStorage. Summer birthdays (June–Aug) show a note in admin suggesting the parent set an alternate school-week date to celebrate early.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Show full word list before practice starts so students can study words first
- Add per-student young learner mode (set in admin): word stays visible while typing
- Center all practice card content (speak button, prompt, feedback, check button)
- Remove 10-word-per-session cap so large lists (25-30 words) practice all words
- Double the misspelled-word study countdown from 4s to 8s
- Fix word list preview numbering for 10+ items (inline numbers, no clipping)
- Add young_learner column to students table with admin toggle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parent can turn the floating school emoji on/off from Settings > Student home screen. Setting persists in the DB and is read via a public /api/public-settings endpoint on the student home page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the plain muted text style with a white card button: border, shadow, hover lift, and transition — consistent across Switch, Back, Stop, and Cancel labels throughout the student app.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Student home gets a deep navy-to-indigo background so card glows pop
- Each kid card glows its own theme color via ::after blur pseudo-element with breathing animation; hover cranks it up to full brightness
- Floating school emoji opacity increased for visibility on dark bg
- 'Parent dashboard' link replaced with a frosted-glass card button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parent: floating card with box-shadow over a soft multi-radial gradient background; decorative emoji header above PIN form.
Student: each kid card gets a colored top border matching their saved theme; subtle floating emoji (stars, books, pencil, rainbow) drift in the background.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Grading: add student filter dropdown, Enter-to-save, and point max always defaults to 100
- Student side: show next-item/back-to-course actions after completing a lesson or assignment
- Lesson body: apply prose typography styles (headings, lists, blockquotes, links, hr)
- Gradebook: fix % display when points_possible is 0 (default to 100 on grade save)
- Auto-schedule: skip items that already have a submission
- Item editor: default new/existing item point values to 100 instead of 10
- Planner: friendly week date header, per-day short date, today highlight, course color on task pills, done toggle, within-day drag reorder, Today button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Today button jumps planner to current week; today column highlighted with accent outline
- Week label now shows friendly format (Aug 3–7, 2026) instead of ISO dates
- Course color shown as left border on scheduled course items
- Parent-side done toggle on every planner task (works for offline + course items)
- Within-day drag-to-reorder tasks via new /api/schedule/reorder endpoint
- Grading queue shows visible "/ N pts" max-points label (defaults to 100 when unset)
- New item default points changed from 10 to 100
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Single "Add task" button in the planner header opens a modal
- Day tabs (Mon–Fri with dates) let you switch the target day without closing
- Live search filters all course items across all courses instantly
- Items pre-fetched in background when planner loads — no per-click delay
- Clicking an item schedules it immediately (no separate Save step)
- Column "+ Add" buttons still work and open the modal pre-selected to that day
- Offline task field at the bottom for non-course entries
- Backdrop click or ✕ closes modal and re-renders the planner
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- School/family name: saved to DB, appears in printed week/month report headers
- Passing grade threshold: replaces hardcoded 80% in all gradebook/history views
- Week start day (Mon vs Sun): shifts the planner 5-day grid and month view accordingly
- Settings are loaded at unlock time so they apply before any panel opens
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- New `worksheet` item type: admin pastes raw worksheet text, parser auto-detects fill-in-the-blank (___), true/false (T F ...), section headers (Part N —), and short-answer questions
- Admin editor shows each parsed question with inputs to set correct answers for FITB and T/F; short answers are marked parent-grades
- Kid view renders FITB as inline underline inputs, T/F as tap-to-toggle T/F buttons, and short answer as a textarea; Name/Date header auto-populated
- Auto-grades T/F (exact) and FITB (case-insensitive); worksheets with short answers go to grading queue as status=done
- Print blank button opens a formatted print window with underlines, circled T/F, and ruled lines for short answers
- "I handed in my paper copy" button lets kids mark a printed worksheet as turned in without digital submission, queuing it for parent grading
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- requirePin: prepare pin-lookup statement once at module level instead of per-request
- Gradebook: replace O(students × items × 3) per-item queries with 2 bulk INs + in-memory Maps
- List/deck PUT: wrap DELETE + re-INSERT in a transaction to prevent partial-write corruption
- Print views: detect HTML vs plain-text lesson bodies before escaping, fixing rich-text output
- Planner add-task: parallelize course detail fetches with Promise.all
- Planner save-all: parallelize schedule inserts with Promise.all
- HOMESCHOOL_DIR: read from env var with existing path as fallback
- Remove unused isDown function in crossword-submit handler
- Remove dead empty-if stub in docx import change handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Name/Date header line on every print
- Numbered words (1. after, 2. again…)
- Ruled write slots: solid baseline + faint midline guide instead of bare underline
- Mode picker (screen-only): toggle between Practice (word shown, write twice) and Dictation/Test (numbered blank lines only) before hitting Print
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Export any course as a .json bundle (GET /api/admin/courses/:id/export)
- Import a .json bundle as a new course (POST /api/admin/import-course)
- Import one or more unit .docx files directly in the browser — server
parses each file with mammoth, extracts Week N: headings as lesson
items and the Unit Quiz section as a graded quiz with answer key
(POST /api/admin/import-course-docx, multer memory storage)
- Admin UI: Export button on course detail; Import card with JSON and
Word docs tabs; multi-file picker for docx uploads
- scripts/import-kitchen-course.js: one-time converter that produced
the Kitchen, Tools & Electrical Skills course JSON bundle (already
imported as course id 3: 8 units, 44 items)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace scrollIntoView (which escapes to the window when the list isn't
tall enough to scroll) with manual scrollTop logic that stays strictly
within the clue list container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
preventScroll was blocking the viewport from scrolling to the grid cell
when a clue was clicked from the sidebar. The clue list overflow fix is
sufficient to contain scrollIntoView without needing preventScroll.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Widen quiz, flashcards, practice, test, and crossword views to match the
960px max-width already used by course/lesson/assignment. Fix crossword
jumping to top on cell focus by using preventScroll and containing clue
list scrollIntoView within a max-height overflow container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Admin enters Across/Down words and clues; server auto-generates the grid
using a backtracking placement algorithm (longest-first, intersection-based).
Students fill in the interactive grid with click-to-select, direction toggle,
arrow-key navigation, and Tab-to-next-word. Auto-graded per word on submit,
with correct/wrong color highlighting on the results view. Includes a print
worksheet option for blank paper practice.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instructions are optional free text shown to the student above the questions/word bank.
Admin enters them in the item editor; stored in items.body for quiz and matching types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of embedding an iframe, mammoth HTML is inserted straight into
the contenteditable body so the admin can edit it like any other content.
A download link to the original .docx sits above the content.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the plain textarea with a contenteditable div + full toolbar
(bold, italic, underline, H2/H3, paragraph, bullet/numbered lists, link,
YouTube embed) — same as the lesson editor.
Toolbar buttons use mousedown+preventDefault so selection is preserved
when clicking format buttons. Both editors share the same button wiring
via closest('.rte-group') rather than hardcoded element IDs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Home, quiz, and spelling screens stay at 640px. Course units,
lesson content, and assignment bodies now use more of the screen.
Text-align set to left for content-heavy views.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Removed narrow max-width/margin from generated HTML page
- iframe now spans full card width (100%)
- onload JS auto-sizes iframe height to content so nothing is cut off
- Falls back to 500px if cross-origin restriction ever applies
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the <iframe> with an inline <div> containing the mammoth HTML.
Content now flows full-width with the page instead of being cramped in
a fixed-height scrolling box. Download link still available below.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Assignment body was being run through esc() which turned <details>/<iframe>
tags into visible raw text. Set innerHTML directly to render the HTML.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each imported doc now shows as a collapsed summary line (📄 filename.docx)
that expands to reveal the iframe and download link when clicked.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each imported .docx now adds its iframe below any existing body content
so multiple documents can be attached to a single item.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On upload, mammoth converts the doc to a styled HTML page saved alongside
the .docx. The item body gets an <iframe> pointing to the HTML so students
see the document inline. A download link below gives access to the original.
Falls back to a plain download link if HTML conversion fails.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of parsing .docx content via mammoth and inserting raw text/HTML,
the import flow now saves the file to data/attachments/ (UUID filename) and
inserts an <a> link into the item body so students can open/download the doc.
- Added GET /api/attachments/:filename (no PIN — students access directly)
- Added POST /api/admin/upload-attachment (PIN required)
- Removed mammoth dependency from server
- Updated admin.js import handler to POST to new endpoint and set link HTML
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lesson body is a contenteditable div — must use innerHTML, not .value.
Assignment body is a plain textarea — strip HTML tags before setting .value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove all AI processing from the import-docx endpoint. Instead:
- Convert the .docx to HTML with mammoth.convertToHtml (preserves
bold, headings, lists, etc.) — the result goes straight into the
item body and renders in the lesson viewer
- Extract raw text in parallel to pick the document title from the
first non-empty line
- No external services, no AI, works on the deployed server
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
btoa(String.fromCharCode(...new Uint8Array(buf))) exceeds V8's function
argument stack limit for files larger than ~65k bytes. Process in 8k
chunks instead to handle any file size.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The old import scanned a hardcoded Seafile CloudStorage path that only
worked on one specific machine. Replaced with a standard <input type=file>
browser picker that reads the .docx as base64 and uploads it directly, so
the feature works everywhere. Server endpoint now accepts either a base64
payload or the legacy path. Also fixed a crash where resetImportSection()
still referenced the removed select element, which was breaking the item
editor for all item types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Gradebook now groups items by unit with collapsible cards (same card
style as course detail) and a Course overall summary table at the bottom
- server.js: add unit_id/unit_name to gradebook API so items can be grouped
- Course detail: preserve which units are open and scroll position across
any in-place re-render (add unit, delete unit, save meta, return from
item editor) — no more jumping to the top with everything reset
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Student view auto-opens the unit whose item due-dates span today (falls back
to most-recent past unit, earliest future unit, then first with incomplete
work). Admin view starts all units expanded. Both share the same card design:
unit-number badge, name, progress/count indicator, animated chevron toggle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cells now expand to show all tasks rather than clipping with overflow:hidden.
Column widths stay equal (CSS grid 1fr) — only height is flexible.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>