Fix editor full-screen and add parent-side doc viewer
Build and Push Docker Image / build-and-push (push) Successful in 45s
Build and Push Docker Image / build-and-push (push) Successful in 45s
Student editor: - Add body.docs-editor-open class when editor opens; hide hero, nav, confetti so nothing bleeds behind the fixed overlay Admin docs panel: - New "Documents" sidebar entry listing all submitted docs with student name, date, and unread badge - Click any doc to open it in OnlyOffice in view (read-only) mode - Download button for offline access - /api/docs/bykey/:key/editor-config server route for admin viewer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
6a5ac46294
commit
ad3e0bc7be
5 files changed
+128
-2
No files matched your search
+80
-2
@@ -120,7 +120,7 @@ document.querySelectorAll('[data-nav]').forEach((btn) =>
|
||||
const PANEL_TITLES = {
|
||||
dashboard: 'Dashboard', kids: 'Kids', courses: 'Courses', planner: 'Planner',
|
||||
grading: 'Grading', messages: 'Messages', gradebook: 'Gradebook',
|
||||
spelling: 'Spelling', decks: 'Flashcards', settings: 'Settings',
|
||||
spelling: 'Spelling', decks: 'Flashcards', docs: 'Documents', settings: 'Settings',
|
||||
'course-detail': 'Course Detail', 'item-editor': 'Item Editor',
|
||||
};
|
||||
|
||||
@@ -132,7 +132,7 @@ function showPanel(name) {
|
||||
const loaders = {
|
||||
dashboard: loadDashboard, kids: loadKids, courses: loadCourses, planner: loadPlannerPanel,
|
||||
grading: loadGrading, messages: loadMessages, gradebook: loadGradebookPanel, spelling: loadSpelling, decks: loadDecks,
|
||||
settings: loadSettings,
|
||||
docs: loadAdminDocs, settings: loadSettings,
|
||||
};
|
||||
if (loaders[name]) loaders[name]();
|
||||
if (name === 'course-detail') openCourseDetail(currentCourseId);
|
||||
@@ -3290,3 +3290,81 @@ $('#app-settings-form').addEventListener('submit', async (e) => {
|
||||
msg('Settings saved.');
|
||||
});
|
||||
|
||||
|
||||
// ── Admin: Submitted Documents ────────────────────────────────────────────────
|
||||
|
||||
let adminDocsEditorInstance = null;
|
||||
|
||||
async function loadAdminDocs() {
|
||||
const { docs } = await api('/api/admin/docs');
|
||||
const el = $('#admin-docs-list');
|
||||
if (!docs.length) {
|
||||
el.innerHTML = '<p class="hint">No submitted documents yet.</p>';
|
||||
return;
|
||||
}
|
||||
el.innerHTML = docs.map((d) => `
|
||||
<div class="doc-card${!d.parent_read ? ' inbox-new' : ''}" style="cursor:pointer"
|
||||
data-id="${d.id}" data-key="${esc(d.doc_key)}" data-filename="${esc(d.filename)}" data-title="${esc(d.title)}">
|
||||
<div class="doc-card-icon">📄</div>
|
||||
<div class="doc-card-info">
|
||||
<div class="doc-card-title">${esc(d.title)}${!d.parent_read ? ' <span class="feedback-badge">New</span>' : ''}</div>
|
||||
<div class="doc-card-meta">${esc(d.student_name)} · Submitted ${new Date(d.submitted_at + 'Z').toLocaleDateString()}</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
el.querySelectorAll('.doc-card').forEach((card) => {
|
||||
card.addEventListener('click', () => openAdminDocViewer(
|
||||
+card.dataset.id, card.dataset.key, card.dataset.filename, card.dataset.title
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
async function openAdminDocViewer(id, key, filename, title) {
|
||||
await api(`/api/admin/docs/${id}/read`, { method: 'PUT' });
|
||||
$('#admin-docs-list').hidden = true;
|
||||
$('#admin-doc-viewer').hidden = false;
|
||||
$('#admin-doc-viewer-title').textContent = title;
|
||||
$('#admin-doc-download').href = `/api/docs/file/${filename}`;
|
||||
$('#admin-doc-download').download = title + '.docx';
|
||||
|
||||
const frame = $('#admin-doc-frame');
|
||||
frame.innerHTML = '';
|
||||
if (adminDocsEditorInstance) {
|
||||
try { adminDocsEditorInstance.destroyEditor(); } catch { /* ignore */ }
|
||||
adminDocsEditorInstance = null;
|
||||
}
|
||||
|
||||
const { onlyofficeUrl, config } = await api(`/api/docs/bykey/${key}/editor-config`).catch(() => null) || {};
|
||||
if (!onlyofficeUrl || !config) {
|
||||
frame.innerHTML = `<div class="docs-error">Could not load document config.</div>`;
|
||||
return;
|
||||
}
|
||||
// Parent views in read mode
|
||||
config.editorConfig = config.editorConfig || {};
|
||||
config.editorConfig.mode = 'view';
|
||||
|
||||
function initEditor() {
|
||||
adminDocsEditorInstance = new window.DocsAPI.DocEditor('admin-doc-frame', config);
|
||||
}
|
||||
if (window.DocsAPI) {
|
||||
initEditor();
|
||||
} else {
|
||||
const s = document.createElement('script');
|
||||
s.src = `${onlyofficeUrl}/web-apps/apps/api/documents/api.js`;
|
||||
s.onload = initEditor;
|
||||
s.onerror = () => { frame.innerHTML = `<div class="docs-error">Could not reach OnlyOffice server.</div>`; };
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('btn-admin-doc-back')?.addEventListener('click', () => {
|
||||
if (adminDocsEditorInstance) {
|
||||
try { adminDocsEditorInstance.destroyEditor(); } catch { /* ignore */ }
|
||||
adminDocsEditorInstance = null;
|
||||
}
|
||||
$('#admin-doc-frame').innerHTML = '';
|
||||
$('#admin-doc-viewer').hidden = true;
|
||||
$('#admin-docs-list').hidden = false;
|
||||
loadAdminDocs();
|
||||
});
|
||||
Reference in new issue
Block a user