From 59b9ceb52c16ce51441bae35bc836ec458856ccf Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Mon, 28 Sep 2026 10:25:37 -0500 Subject: [PATCH] feat(dashboard): import, reprocess and remove models from the property 3D view The 3D view gets the property page's model actions: Reprocess all models (ugc_manage), Import LXFML at the file's positions and Remove all models (properties_import). Not on the showcase view. Co-Authored-By: Claude Opus 5.5 --- dDashboardServer/templates/property-3d.jinja2 | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/dDashboardServer/templates/property-3d.jinja2 b/dDashboardServer/templates/property-3d.jinja2 index 15ac8777c..780dff560 100644 --- a/dDashboardServer/templates/property-3d.jinja2 +++ b/dDashboardServer/templates/property-3d.jinja2 @@ -34,6 +34,14 @@ + {% if not exists("showcase") %} + {% if can.ugc_manage %}{% endif %} + {% if can.properties_import %} + + + + {% endif %} + {% endif %} @@ -476,6 +484,30 @@ document.getElementById('resetPropertyBtn').addEventListener('click', () => { document.getElementById('behaviorLog').innerHTML = '
  • Select a model with behaviors and trigger it.
  • '; load(); }); +const reprocessModelsBtn = document.getElementById('reprocessModelsBtn'); +if (reprocessModelsBtn) reprocessModelsBtn.addEventListener('click', () => { + reprocessModelsBtn.disabled = true; + api.action('/api/ugc/reprocess', { kind: 'model', property: propertyId }).then((d) => toast(d.message, 'success')) + .catch(() => {}).finally(() => { reprocessModelsBtn.disabled = false; }); +}); +const importModelsFile = document.getElementById('importModelsFile'); +if (importModelsFile) { + document.getElementById('importModelsBtn').addEventListener('click', () => importModelsFile.click()); + importModelsFile.addEventListener('change', async () => { + const file = importModelsFile.files[0]; + if (!file) return; + const lxfml = await file.text(); + importModelsFile.value = ''; + api.action('/api/properties/' + propertyId + '/import', { lxfml }).then((d) => { toast(d.message, 'success'); load(); }).catch(() => {}); + }); +} +const removeModelsBtn = document.getElementById('removeModelsBtn'); +if (removeModelsBtn) removeModelsBtn.addEventListener('click', () => { + if (window.prompt('This deletes every model on the property. Type the property ID (' + propertyId + ') to confirm:') !== propertyId) return; + removeModelsBtn.disabled = true; + api.action('/api/properties/' + propertyId + '/remove_models', { confirm: propertyId }).then((d) => { toast(d.message, 'success'); load(); }) + .catch(() => {}).finally(() => { removeModelsBtn.disabled = false; }); +}); document.getElementById('fullscreenBtn').addEventListener('click', () => { const wrap = document.getElementById('viewerWrap'); if (document.fullscreenElement) document.exitFullscreen();