aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/static/dropeditor.js
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-03 16:55:23 -0400
committerhistoria <[not public]>2026-07-03 16:55:23 -0400
commit034187d1c434b6bee32aeb539ea543f6ec537376 (patch)
treef238a9c6a657af20edf0c53e9d0980bc58c1133a /internal/admin/static/dropeditor.js
parent778157981cccb8cb4856e3ab5a72085830c59120 (diff)
downloadthehouseoficarus-034187d1c434b6bee32aeb539ea543f6ec537376.tar.gz
feat: card ui added to drops, hazards, techs, and mods in admin gui
Diffstat (limited to 'internal/admin/static/dropeditor.js')
-rw-r--r--internal/admin/static/dropeditor.js224
1 files changed, 224 insertions, 0 deletions
diff --git a/internal/admin/static/dropeditor.js b/internal/admin/static/dropeditor.js
new file mode 100644
index 0000000..1569a04
--- /dev/null
+++ b/internal/admin/static/dropeditor.js
@@ -0,0 +1,224 @@
+var dropData = null;
+var dropID = null;
+var expandedCards = {};
+var cardWidths = {};
+
+var DROP_SECTIONS = [
+ {
+ id: 'drops', label: 'Drops', always: true, path: 'drops', isArray: true,
+ fields: [
+ ['item_id', 'Item ID', 'search', '', '', null, 'items'],
+ ['table', 'Table', 'search', '', '', null, 'drops'],
+ ['weight', 'Weight', 'number', '', 'narrow'],
+ ['quantity', 'Quantity', 'number', '1', 'narrow'],
+ ['level', 'Level', 'number', '', 'narrow'],
+ ['xp', 'XP', 'number', '', 'narrow'],
+ ['depletes', 'Depletes', 'checkbox'],
+ ['success_message', 'Success Message', 'text', '', 'wide'],
+ ]
+ }
+];
+
+function initDropEditor() {
+ editorType = 'drops';
+ editorFields = [];
+ loadList();
+ if (window.location.hash) loadDropEditor(window.location.hash.substring(1));
+}
+
+function cardPath(sec) { return sec.path || ''; }
+
+function getVal(data, sec, key) {
+ return ced_getVal(data, sec, key);
+}
+
+function fieldID(sec, key, idx) {
+ return ced_fieldID(sec, key, idx);
+}
+
+function setupSearchFields() { ced_setupSearchFields(); }
+
+function collectFieldValue(el, type) { return ced_collectFieldValue(el, type); }
+
+function loadDropEditor(id) {
+ dropID = id;
+ currentID = id;
+ renderList('');
+ window.location.hash = id;
+ API.get('/api/drops/' + encodeURIComponent(id)).then(function(data) {
+ dropData = data;
+ renderDropEditor(id, data);
+ }).catch(function(e) {
+ $('#editorMain').innerHTML = '<p style="color:var(--danger)">Failed to load: ' + esc(e.message) + '</p>';
+ });
+}
+
+function loadItem(id) { loadDropEditor(id); }
+
+function renderDropEditor(id, data) {
+ var html = '<h2>Drop Table: ' + esc(id) + '</h2>';
+ html += '<div class="tabs"><button class="tab active" onclick="switchTab(event,\'fields\')">Fields</button>';
+ html += '<button class="tab" onclick="switchTab(event,\'yaml\')">Raw YAML</button></div>';
+
+ html += '<div class="tab-content" id="tabFields">';
+ html += '<div class="obj-fields-wrap">';
+ DROP_SECTIONS.forEach(function(sec) {
+ html += renderCard(sec, data);
+ });
+ html += '</div>';
+ html += '</div>';
+
+ html += '<div class="tab-content" id="tabYaml" style="display:none">';
+ html += '<pre>' + esc(data._raw || JSON.stringify(data, null, 2)) + '</pre>';
+ html += '</div>';
+
+ html += '<div class="btn-row">';
+ html += '<button class="btn btn-primary" onclick="saveDrop()">Save</button>';
+ html += '<button class="btn btn-danger" onclick="deleteDrop()">Delete</button>';
+ html += '</div>';
+
+ $('#editorMain').innerHTML = html;
+ setupSearchFields();
+}
+
+function renderCurrent() {
+ if (!dropData || !dropID) return;
+ renderDropEditor(dropID, dropData);
+}
+
+function renderCard(sec, data) {
+ var collapsed = expandedCards[sec.id] === false;
+ var isFull = cardWidths[sec.id] !== undefined ? cardWidths[sec.id] : (sec.fullWidth || false);
+ var fullClass = isFull ? ' obj-card-full' : '';
+ var h = '<div class="obj-card' + fullClass + '" data-card="' + sec.id + '">';
+ h += '<div class="obj-card-header" onclick="toggleCard(\'' + sec.id + '\')">';
+ h += '<span class="obj-card-arrow">' + (collapsed ? '&#9654;' : '&#9660;') + '</span>';
+ h += '<button class="btn btn-sm obj-card-expand" onclick="event.stopPropagation();toggleCardWidth(\'' + sec.id + '\')" title="' + (isFull ? 'Half width' : 'Full width') + '">' + (isFull ? '\u229F' : '\u229E') + '</button>';
+ h += '<span class="obj-card-label">' + sec.label + '</span>';
+ h += '</div>';
+ if (!collapsed) {
+ h += '<div class="obj-card-body">';
+ h += renderArraySection(sec, data);
+ h += '</div>';
+ }
+ h += '</div>';
+ return h;
+}
+
+function renderArraySection(sec, data) {
+ var arr = dropData[cardPath(sec)] || [];
+ var h = '';
+ arr.forEach(function(item, idx) {
+ h += '<div class="obj-sub-row" data-idx="' + idx + '">';
+ h += '<div class="obj-card-grid">';
+ sec.fields.forEach(function(f) {
+ h += renderField(sec, f, data, idx);
+ });
+ h += '</div>';
+ h += '<button class="btn btn-sm btn-danger obj-sub-remove" onclick="removeArrayItem(\'' + sec.id + '\',' + idx + ')">X</button>';
+ h += '</div>';
+ });
+ h += '<button class="btn btn-sm obj-sub-add" onclick="addArrayItem(\'' + sec.id + '\')">+ Add Entry</button>';
+ return h;
+}
+
+function renderField(sec, f, data, idx) {
+ return ced_renderField(sec, f, data, idx, null, null, cardPath, fieldID, getVal);
+}
+
+function toggleCard(id) {
+ expandedCards[id] = expandedCards[id] === false ? true : false;
+ renderCurrent();
+}
+
+function toggleCardWidth(id) {
+ cardWidths[id] = !cardWidths[id];
+ renderCurrent();
+}
+
+function addArrayItem(cardID) {
+ var sec = DROP_SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec || !sec.isArray) return;
+ var arr = dropData[sec.path] || [];
+ var empty = {};
+ sec.fields.forEach(function(f) {
+ empty[f[0]] = f[2] === 'checkbox' ? false : (f[2] === 'number' ? 0 : '');
+ });
+ arr.push(empty);
+ dropData[sec.path] = arr;
+ renderCurrent();
+}
+
+function removeArrayItem(cardID, idx) {
+ var sec = DROP_SECTIONS.find(function(s) { return s.id === cardID; });
+ if (!sec || !sec.isArray) return;
+ var arr = dropData[sec.path] || [];
+ arr.splice(idx, 1);
+ renderCurrent();
+}
+
+function saveDrop() {
+ if (!ced_validateFields(DROP_SECTIONS, fieldID, null)) return;
+
+ var out = {};
+
+ DROP_SECTIONS.forEach(function(sec) {
+ if (sec.isArray) {
+ var arr = [];
+ var card = document.querySelector('.obj-card[data-card="' + sec.id + '"]');
+ if (!card) return;
+ var rows = card.querySelectorAll('.obj-sub-row');
+ rows.forEach(function(row, idx) {
+ var item = {};
+ sec.fields.forEach(function(f) {
+ var fid = fieldID(sec, f[0], idx);
+ var el = document.getElementById(fid);
+ if (el) item[f[0]] = collectFieldValue(el, f[2]);
+ });
+ if (Object.keys(item).length > 0) arr.push(item);
+ });
+ if (arr.length > 0) out[sec.path] = arr;
+ return;
+ }
+
+ var sectionObj = {};
+ sec.fields.forEach(function(f) {
+ var fid = fieldID(sec, f[0]);
+ var el = document.getElementById(fid);
+ if (!el) return;
+ sectionObj[f[0]] = collectFieldValue(el, f[2]);
+ });
+
+ if (sec.always && sec.path) {
+ out[sec.path] = sectionObj;
+ } else if (sec.always) {
+ Object.keys(sectionObj).forEach(function(k) { out[k] = sectionObj[k]; });
+ } else if (sec.path) {
+ out[sec.path] = sectionObj;
+ } else {
+ Object.keys(sectionObj).forEach(function(k) { out[k] = sectionObj[k]; });
+ }
+ });
+
+ ced_cleanOutput(out);
+ out.id = dropID;
+
+ API.put('/api/drops/' + encodeURIComponent(dropID), out).then(function(resp) {
+ notify('Drop table ' + dropID + ' saved', 'success');
+ updateUndoBar();
+ if (resp && resp._raw) dropData._raw = resp._raw;
+ }).catch(function(e) { notify('Save failed: ' + e.message, 'error'); });
+}
+
+function deleteDrop() {
+ if (!confirm('Delete drop table "' + dropID + '"?')) return;
+ API.del('/api/drops/' + encodeURIComponent(dropID)).then(function() {
+ notify('Drop table ' + dropID + ' deleted', 'success');
+ updateUndoBar();
+ dropID = null;
+ currentID = null;
+ dropData = null;
+ $('#editorMain').innerHTML = '<p style="color:#888;text-align:center;margin-top:60px">Deleted</p>';
+ loadList();
+ }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); });
+}