Add course import/export: JSON bundle and direct Word docx upload
- 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>
This commit is contained in:
1 parent
a44cf659b5
commit
3197d29a28
6 files changed
+838
-2
No files matched your search
@@ -77,6 +77,47 @@
|
||||
<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>
|
||||
@@ -95,6 +136,7 @@
|
||||
<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>
|
||||
|
||||
Reference in new issue
Block a user