diff options
Diffstat (limited to 'internal/admin/static/dropeditor.js')
| -rw-r--r-- | internal/admin/static/dropeditor.js | 224 |
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 ? '▶' : '▼') + '</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'); }); +} |
