feat: viewer local upload, Bambu collapse, planning done/fail, stats waste, portal link in jobs, sidebar collapsible, settings layout fix
Deploy via Portainer / deploy (push) Successful in 0s
Deploy via Portainer / deploy (push) Successful in 0s
This commit is contained in:
+70
-17
@@ -56,12 +56,22 @@
|
||||
<span class="font-monospace">{{ job.source_file.split('/')[-1] }}</span>
|
||||
{% set ext = job.source_file.rsplit('.',1)[-1].lower() %}
|
||||
{% if ext in ['stl','3mf','step','stp','obj'] %}
|
||||
<button class="btn btn-xs btn-outline-secondary py-0 px-1 ms-1"
|
||||
style="font-size:.7rem"
|
||||
data-bs-toggle="modal" data-bs-target="#viewerModal"
|
||||
data-file="{{ job.source_file }}" data-ext="{{ ext }}">
|
||||
<i class="bi bi-box me-1"></i>Voir
|
||||
</button>
|
||||
{% set is_nc_path = job.source_file.startswith('/') %}
|
||||
{% if is_nc_path %}
|
||||
<button class="btn btn-xs btn-outline-secondary py-0 px-1 ms-1"
|
||||
style="font-size:.7rem"
|
||||
data-bs-toggle="modal" data-bs-target="#viewerModal"
|
||||
data-file="{{ job.source_file }}" data-ext="{{ ext }}" data-src="nextcloud">
|
||||
<i class="bi bi-box me-1"></i>Voir
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="btn btn-xs btn-outline-secondary py-0 px-1 ms-1"
|
||||
style="font-size:.7rem"
|
||||
data-bs-toggle="modal" data-bs-target="#viewerModal"
|
||||
data-file="" data-ext="{{ ext }}" data-src="upload">
|
||||
<i class="bi bi-upload me-1"></i>Charger pour voir
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}—{% endif %}
|
||||
</td></tr>
|
||||
@@ -246,7 +256,12 @@
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body p-0 position-relative" style="height:75vh">
|
||||
<div id="viewerUploadZone" class="p-4 text-center" style="display:none">
|
||||
<i class="bi bi-cloud-upload fs-1 text-white-50 d-block mb-3"></i>
|
||||
<p class="text-white-50 mb-3">Le fichier source n'est pas sur Nextcloud.<br>Chargez-le manuellement pour le visualiser.</p>
|
||||
<input type="file" id="viewerLocalFile" class="form-control form-control-sm mx-auto" style="max-width:300px" accept=".stl,.3mf,.obj">
|
||||
</div>
|
||||
<div class="modal-body p-0 position-relative" style="height:75vh" id="viewerCanvasWrap">
|
||||
<canvas id="viewerCanvas" style="width:100%;height:100%;display:block"></canvas>
|
||||
<div id="viewerLoader" class="position-absolute top-50 start-50 translate-middle text-white text-center">
|
||||
<div class="spinner-border mb-2"></div><br>Chargement du modèle…
|
||||
@@ -332,14 +347,20 @@ function fitCamera() {
|
||||
controls.update();
|
||||
}
|
||||
|
||||
function loadModel(path, ext) {
|
||||
const uploadZone = document.getElementById('viewerUploadZone');
|
||||
const canvasWrap = document.getElementById('viewerCanvasWrap');
|
||||
const localFileIn = document.getElementById('viewerLocalFile');
|
||||
|
||||
function makeMat() {
|
||||
return new THREE.MeshPhongMaterial({ color: 0xff6b35, specular: 0x333333, shininess: 40, side: THREE.DoubleSide });
|
||||
}
|
||||
|
||||
function loadFromUrl(url, ext) {
|
||||
loader.style.display = '';
|
||||
errDiv.style.display = 'none';
|
||||
if (modelMesh) { scene.remove(modelMesh); modelMesh = null; }
|
||||
|
||||
const url = '/api/nextcloud/file?path=' + encodeURIComponent(path);
|
||||
const mat = new THREE.MeshPhongMaterial({ color: 0xff6b35, specular: 0x333333, shininess: 40, side: THREE.DoubleSide });
|
||||
|
||||
const mat = makeMat();
|
||||
const onError = (e) => {
|
||||
loader.style.display = 'none';
|
||||
errDiv.style.display = '';
|
||||
@@ -355,7 +376,6 @@ function loadModel(path, ext) {
|
||||
loader.style.display = 'none';
|
||||
fitCamera();
|
||||
}, undefined, onError);
|
||||
|
||||
} else if (ext === '3mf') {
|
||||
new ThreeMFLoader().load(url, obj => {
|
||||
obj.traverse(child => {
|
||||
@@ -366,7 +386,6 @@ function loadModel(path, ext) {
|
||||
loader.style.display = 'none';
|
||||
fitCamera();
|
||||
}, undefined, onError);
|
||||
|
||||
} else {
|
||||
loader.style.display = 'none';
|
||||
errDiv.style.display = '';
|
||||
@@ -374,19 +393,53 @@ function loadModel(path, ext) {
|
||||
}
|
||||
}
|
||||
|
||||
function showUploadZone(ext) {
|
||||
uploadZone.style.display = '';
|
||||
canvasWrap.style.display = 'none';
|
||||
loader.style.display = 'none';
|
||||
// Reset file input
|
||||
localFileIn.value = '';
|
||||
localFileIn.onchange = (ev) => {
|
||||
const file = ev.target.files[0];
|
||||
if (!file) return;
|
||||
const detectedExt = file.name.split('.').pop().toLowerCase();
|
||||
const objectUrl = URL.createObjectURL(file);
|
||||
title.textContent = file.name;
|
||||
uploadZone.style.display = 'none';
|
||||
canvasWrap.style.display = '';
|
||||
initRenderer();
|
||||
loadFromUrl(objectUrl, detectedExt);
|
||||
// Revoke the blob URL after loading
|
||||
setTimeout(() => URL.revokeObjectURL(objectUrl), 10000);
|
||||
};
|
||||
}
|
||||
|
||||
// Ouvrir la modale
|
||||
modal.addEventListener('show.bs.modal', e => {
|
||||
const btn = e.relatedTarget;
|
||||
const path = btn.dataset.file;
|
||||
const ext = btn.dataset.ext;
|
||||
title.textContent = path.split('/').pop();
|
||||
initRenderer();
|
||||
loadModel(path, ext);
|
||||
const ext = (btn.dataset.ext || '').toLowerCase();
|
||||
const src = btn.dataset.src;
|
||||
|
||||
if (src === 'upload') {
|
||||
title.textContent = 'Charger un fichier local';
|
||||
uploadZone.style.display = '';
|
||||
canvasWrap.style.display = 'none';
|
||||
showUploadZone(ext);
|
||||
} else {
|
||||
title.textContent = path.split('/').pop();
|
||||
uploadZone.style.display = 'none';
|
||||
canvasWrap.style.display = '';
|
||||
initRenderer();
|
||||
loadFromUrl('/api/nextcloud/file?path=' + encodeURIComponent(path), ext);
|
||||
}
|
||||
});
|
||||
|
||||
// Fermer → libérer
|
||||
modal.addEventListener('hidden.bs.modal', () => {
|
||||
if (modelMesh) { scene.remove(modelMesh); modelMesh = null; }
|
||||
uploadZone.style.display = 'none';
|
||||
canvasWrap.style.display = '';
|
||||
});
|
||||
|
||||
// Wireframe toggle
|
||||
|
||||
Reference in New Issue
Block a user