From b756517ab59314a710462821fcb24663ef0d8f1a Mon Sep 17 00:00:00 2001 From: perco Date: Fri, 15 May 2026 11:05:58 +0200 Subject: [PATCH] PrintVault: direct page navigation + GCode metadata cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clicking a model card now navigates to /printvault-viewer.php (no popup) - viewer page uses HouseHub header/nav (integrated look) - GCode metadata displayed as cards: โฑ time, ๐Ÿงต filament, ๐Ÿ”ฅ nozzle, ๐ŸŸฆ bed - Two-column layout: Three.js viewer left + metadata sidebar right - viewer.css: new stylesheet for detail page - Removed now-unused detail/edit modals from printvault.php Co-Authored-By: Claude Sonnet 4.6 --- modules/printvault/assets/printvault.js | 90 +---- modules/printvault/assets/viewer.css | 161 ++++++++ printvault-viewer.php | 492 +++++++++++++----------- printvault.php | 52 --- 4 files changed, 429 insertions(+), 366 deletions(-) create mode 100644 modules/printvault/assets/viewer.css diff --git a/modules/printvault/assets/printvault.js b/modules/printvault/assets/printvault.js index ab4530e..1ff68ba 100644 --- a/modules/printvault/assets/printvault.js +++ b/modules/printvault/assets/printvault.js @@ -128,7 +128,7 @@ function cardHtml(m) { : icon; const dims = m.dim_x > 0 ? `${Math.round(m.dim_x)}ร—${Math.round(m.dim_y)}ร—${Math.round(m.dim_z)}mm` : ''; const gcInfo = m.gcode_time || ''; - return ` - `; -} - -// โ”€โ”€โ”€ Detail panel โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -let currentDetailId = null; - -function openDetail(id) { - const m = allModels.find(x => x.id === id); - if (!m) return; - currentDetailId = id; - const ext = m.file_type.toLowerCase(); - const canView = ['stl','3mf'].includes(ext); - const isGcode = ['gcode','gco','g'].includes(ext); - const icon = typeIcon[ext] || '๐Ÿ“„'; - const thumbUrl = m.thumb ? `/uploads/printvault/thumbs/${encodeURIComponent(m.thumb)}` : null; - - document.getElementById('pv-detail-body').innerHTML = ` -
- ${thumbUrl ? `` : icon} -
-
${escHtml(m.name)}
- ${m.description ? `
${escHtml(m.description)}
` : ''} - ${m.tags ? `
${m.tags.split(',').filter(Boolean).map(t=>`${escHtml(t.trim())}`).join('')}
` : ''} -
-
Type${escHtml(m.file_type.toUpperCase())}
-
Catรฉgorie${escHtml(m.category)}
-
Taille${escHtml(fmtSize(m.file_size))}
-
Ajoutรฉ le${escHtml(fmtDate(m.created_at))}
- ${m.dim_x > 0 ? `
Dimensions${Math.round(m.dim_x)}ร—${Math.round(m.dim_y)}ร—${Math.round(m.dim_z)} mm${m.volume > 0 ? ' ยท '+m.volume+' cmยณ' : ''}
` : ''} - ${isGcode && m.gcode_time ? `
Temps${escHtml(m.gcode_time)}
` : ''} - ${isGcode && m.gcode_filament ? `
Filament${escHtml(m.gcode_filament)}
` : ''} - ${isGcode && m.gcode_nozzle ? `
Buse${escHtml(m.gcode_nozzle)}
` : ''} - ${isGcode && m.gcode_bed ? `
Plateau${escHtml(m.gcode_bed)}
` : ''} -
`; - - const btn3d = document.getElementById('pv-detail-3d-btn'); - if (canView) { - btn3d.style.display = ''; - btn3d.onclick = () => window.open(`/printvault-viewer.php?id=${id}`, '_blank', 'width=1200,height=800'); - } else { - btn3d.style.display = 'none'; - } - - document.getElementById('pv-detail-dl-btn').href = `${API}?action=file&id=${id}`; - document.getElementById('pv-detail-edit-btn').onclick = () => openEdit(m); - document.getElementById('pv-detail-del-btn').onclick = () => deleteModel(id, m.name); - document.getElementById('pv-detail-modal').classList.add('show'); -} - -async function deleteModel(id, name) { - if (!confirm(`Supprimer "${name}" ?`)) return; - try { - await api('models', 'DELETE', null, '&id='+id); - document.getElementById('pv-detail-modal').classList.remove('show'); - toast('Modรจle supprimรฉ'); - allModels = allModels.filter(m => m.id !== id); - renderSidebar(); renderModels(); - } catch(e) { toast(e.message, 'error'); } -} - -// โ”€โ”€โ”€ Edit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -function openEdit(m) { - document.getElementById('pv-detail-modal').classList.remove('show'); - document.getElementById('pv-edit-id').value = m.id; - document.getElementById('pv-edit-name').value = m.name; - document.getElementById('pv-edit-desc').value = m.description || ''; - document.getElementById('pv-edit-tags').value = m.tags || ''; - const sel = document.getElementById('pv-edit-cat'); - sel.innerHTML = categories.map(c => ``).join(''); - document.getElementById('pv-edit-modal').classList.add('show'); -} - -async function saveEdit() { - const id = document.getElementById('pv-edit-id').value; - const name = document.getElementById('pv-edit-name').value.trim(); - if (!name) { toast('Nom requis', 'error'); return; } - try { - await api('models', 'PUT', { - name, - description: document.getElementById('pv-edit-desc').value, - category: document.getElementById('pv-edit-cat').value, - tags: document.getElementById('pv-edit-tags').value, - }, '&id='+id); - toast('Mis ร  jour โœ“'); - document.getElementById('pv-edit-modal').classList.remove('show'); - await loadModels(); - } catch(e) { toast(e.message, 'error'); } +
`; } // โ”€โ”€โ”€ Upload โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ diff --git a/modules/printvault/assets/viewer.css b/modules/printvault/assets/viewer.css new file mode 100644 index 0000000..abebc8e --- /dev/null +++ b/modules/printvault/assets/viewer.css @@ -0,0 +1,161 @@ +/* PrintVault โ€” detail/viewer page */ + +.pv-detail-page { + display: flex; + flex-direction: column; + height: calc(100vh - 64px); + overflow: hidden; + background: var(--bg-page); +} + +/* Top bar */ +.pv-detail-topbar { + display: flex; + align-items: center; + gap: .75rem; + padding: .75rem 1.25rem; + background: var(--bg-panel); + border-bottom: 1px solid var(--border-light); + flex-shrink: 0; + flex-wrap: wrap; +} +.pv-detail-topbar-title { + display: flex; + align-items: center; + gap: .6rem; + min-width: 0; +} +.pv-detail-topbar-title h1 { + font-size: 1rem; + font-weight: 700; + color: var(--text-main); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Body: viewer + sidebar */ +.pv-detail-body { + display: grid; + grid-template-columns: 1fr 320px; + flex: 1; + overflow: hidden; +} + +/* Viewer area */ +.pv-detail-left { + overflow: hidden; + position: relative; + background: #0f172a; +} +.pv-viewer-wrap { + width: 100%; + height: 100%; + position: relative; +} +.pv-viewer-wrap canvas { display: block; width: 100% !important; height: 100% !important; } + +.pv-viewer-loading { + position: absolute; inset: 0; + display: flex; flex-direction: column; align-items: center; justify-content: center; + gap: 1rem; background: #0f172a; z-index: 10; color: #94a3b8; font-size: .9rem; +} +.pv-spinner { + width: 36px; height: 36px; + border: 3px solid #1e293b; border-top-color: #3b82f6; + border-radius: 50%; animation: pvSpin .8s linear infinite; +} +@keyframes pvSpin { to { transform: rotate(360deg); } } + +.pv-viewer-controls-hint { + position: absolute; bottom: 1rem; left: 50%; transform: translateX(-50%); + font-size: .72rem; color: #475569; background: rgba(15,23,42,.7); + padding: .3rem .7rem; border-radius: 999px; pointer-events: none; + white-space: nowrap; +} +.pv-viewer-screenshot-btn { + position: absolute; top: .75rem; right: .75rem; + background: rgba(15,23,42,.8); border: 1px solid rgba(255,255,255,.15); + color: #e2e8f0; border-radius: 8px; padding: .35rem .6rem; + cursor: pointer; font-size: .85rem; transition: background .15s; +} +.pv-viewer-screenshot-btn:hover { background: rgba(59,130,246,.4); } + +/* Thumbnail fallback */ +.pv-detail-thumb-large { + width: 100%; height: 100%; + display: flex; align-items: center; justify-content: center; + font-size: 8rem; background: #1e293b; +} +.pv-detail-thumb-large img { width: 100%; height: 100%; object-fit: contain; } +.pv-detail-thumb-placeholder { color: #334155; } + +/* Right sidebar metadata */ +.pv-detail-right { + overflow-y: auto; + background: var(--bg-panel); + border-left: 1px solid var(--border-light); + display: flex; + flex-direction: column; + gap: 0; +} +.pv-meta-section { + padding: 1rem 1.25rem; + border-bottom: 1px solid var(--border-light); +} +.pv-meta-section:last-child { border-bottom: none; } +.pv-meta-title { + font-size: .72rem; font-weight: 700; text-transform: uppercase; + letter-spacing: .06em; color: var(--text-muted); margin-bottom: .75rem; +} +.pv-meta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; } +.pv-meta-item { display: flex; flex-direction: column; gap: .15rem; } +.pv-meta-item--full { grid-column: 1 / -1; } +.pv-meta-label { font-size: .72rem; color: var(--text-muted); font-weight: 500; } +.pv-meta-value { font-size: .875rem; color: var(--text-main); font-weight: 500; } + +/* Description & tags */ +.pv-detail-description { font-size: .875rem; color: var(--text-muted); line-height: 1.5; } +.pv-tags-row { display: flex; gap: .35rem; flex-wrap: wrap; } +.pv-tag { + font-size: .72rem; background: var(--bg-page); border: 1px solid var(--border-light); + padding: .15rem .5rem; border-radius: 999px; color: var(--text-muted); +} + +/* GCode metadata cards */ +.pv-gcode-cards { + display: grid; + grid-template-columns: 1fr 1fr; + gap: .6rem; +} +.pv-gcode-card { + background: var(--bg-page); + border: 1px solid var(--border-light); + border-radius: 10px; + padding: .85rem .9rem; + display: flex; flex-direction: column; gap: .2rem; + transition: border-color .15s; +} +.pv-gcode-card:hover { border-color: #cbd5e1; } +.pv-gcode-icon { font-size: 1.3rem; margin-bottom: .1rem; } +.pv-gcode-value { + font-size: 1.05rem; font-weight: 700; color: var(--text-main); + line-height: 1.2; +} +.pv-gcode-label { + font-size: .72rem; color: var(--text-muted); line-height: 1.3; +} + +/* Responsive */ +@media (max-width: 900px) { + .pv-detail-body { grid-template-columns: 1fr; grid-template-rows: 55vh 1fr; } + .pv-detail-right { border-left: none; border-top: 1px solid var(--border-light); } + .pv-detail-page { height: auto; overflow: auto; } + .pv-detail-body { overflow: visible; } +} + +/* Dark mode */ +[data-theme="dark"] .pv-detail-topbar { background: var(--bg-panel); } +[data-theme="dark"] .pv-detail-right { background: var(--bg-panel); } +[data-theme="dark"] .pv-gcode-card { background: var(--bg-page); } +[data-theme="dark"] .pv-detail-thumb-large { background: #0d1117; } diff --git a/printvault-viewer.php b/printvault-viewer.php index 476626e..4cc95dc 100644 --- a/printvault-viewer.php +++ b/printvault-viewer.php @@ -4,112 +4,239 @@ require_login(); require_once __DIR__ . '/includes/db.php'; $id = (int)($_GET['id'] ?? 0); -if (!$id) { http_response_code(404); exit('Modรจle introuvable'); } +if (!$id) { header('Location: /printvault.php'); exit; } $s = $pdo->prepare("SELECT * FROM pf_pv_models WHERE id=?"); $s->execute([$id]); $model = $s->fetch(); -if (!$model) { http_response_code(404); exit('Modรจle introuvable'); } +if (!$model) { header('Location: /printvault.php'); exit; } -$ext = strtolower($model['file_type']); -$canView = in_array($ext, ['stl','3mf']); -?> - - - - - <?= htmlspecialchars($model['name']) ?> โ€” PrintVault - - - - +$s2 = $pdo->query("SELECT name, color FROM pf_pv_categories ORDER BY name"); +$categories = $s2->fetchAll(); -
-
-
-
- - 0): ?> - ยท ร—ร— mm - - ยท Ko +$ext = strtolower($model['file_type']); +$canView = in_array($ext, ['stl','3mf']); +$isGcode = in_array($ext, ['gcode','gco','g']); +$thumbUrl = $model['thumb'] ? '/uploads/printvault/thumbs/' . rawurlencode($model['thumb']) : null; +$typeIcon = ['stl'=>'๐ŸŸฃ','3mf'=>'๐Ÿ”ต','gcode'=>'๐ŸŸข','gco'=>'๐ŸŸข','g'=>'๐ŸŸข'][$ext] ?? '๐Ÿ“„'; + +function fmtSize(int $b): string { + if ($b > 1048576) return round($b/1048576,1).' Mo'; + return round($b/1024).' Ko'; +} +function fmtDate(string $s): string { + return date('d/m/Y', strtotime($s)); +} + +$pageTitle = htmlspecialchars($model['name']) . ' โ€” PrintVault'; +$activePage = 'printvault'; +require __DIR__ . '/header.php'; +?> + + + +
+ +
+ + +
+ โ† Retour +
+ +

+
+
+ + โฌ‡ Tรฉlรฉcharger +
-
- - - - - - โฌ‡ Tรฉlรฉcharger + + +
+ + +
+ +
+
+
+
Chargementโ€ฆ
+
+ +
Clic+glisser : rotation ยท Scroll : zoom
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ + +
+

+
+ + + +
+ + + +
+ + + +
+
Informations
+
+
Catรฉgorie
+
Taille fichier
+
Ajoutรฉ le
+ 0): ?> +
+ Dimensions + ร— ร— mm 0 ? ' ยท '.round($model['volume'],2).' cmยณ' : '' ?> +
+ +
+
+ + + +
+
Mรฉtadonnรฉes impression
+
+ +
+
โฑ
+
+
Temps d'impression estimรฉ
+
+ + +
+
๐Ÿงต
+
+
Filament utilisรฉ
+
+ + +
+
๐Ÿ”ฅ
+
+
Tempรฉrature buse
+
+ + +
+
๐ŸŸฆ
+
+
Tempรฉrature plateau
+
+ +
+
+ + +
- - -
-
-
Chargement du modรจleโ€ฆ
-
+ +
+
+
+

Modifier le modรจle

+ +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ +
-
-
Clic+glisser : rotation ยท Scroll : zoom ยท Clic droit : dรฉplacer
+ + + + - - - -
-
-

๐ŸŸข

- - -
- - -
- -
-
- - + diff --git a/printvault.php b/printvault.php index c10b62e..50ace6c 100644 --- a/printvault.php +++ b/printvault.php @@ -45,58 +45,6 @@ require __DIR__ . '/header.php';
- -
-
-
-

Dรฉtail du modรจle

- -
-
- -
-
- - -
-
-
-

Modifier le modรจle

- -
-
- -
- - -
-
- - -
-
-
- - -
-
- - -
-
-
- -
-
-