From 5ea48c1f91298b42924f6865ffc30810cceec2d3 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Mon, 13 Jul 2026 20:36:26 -0400 Subject: feat(admin): triggers organized by directory, support duplicate and delete --- internal/admin/api_triggers.go | 60 ++---------- internal/admin/static/triggerseditor.js | 165 ++++++++++++++++++++++++++++---- 2 files changed, 154 insertions(+), 71 deletions(-) (limited to 'internal/admin') diff --git a/internal/admin/api_triggers.go b/internal/admin/api_triggers.go index ccfd52d..749f5a1 100644 --- a/internal/admin/api_triggers.go +++ b/internal/admin/api_triggers.go @@ -13,23 +13,22 @@ import ( "thehouseoficarus/internal/behavior" ) -type triggerEntry struct { - ID string `json:"id"` - Kind string `json:"kind"` -} - func (s *AdminServer) handleTriggers(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - entries, err := listTriggerEntries(s.dataDir) + tree, err := listYAMLTree(s.dataDir, "triggers") if err != nil { + if os.IsNotExist(err) { + writeJSON(w, &YAMLTree{Root: []string{}, Dirs: map[string][]string{}}) + return + } writeJSON(w, map[string]any{"error": err.Error()}) return } - if entries == nil { - entries = []triggerEntry{} + if tree == nil { + tree = &YAMLTree{Root: []string{}, Dirs: map[string][]string{}} } - writeJSON(w, entries) + writeJSON(w, tree) case http.MethodPost: var m map[string]any if err := readJSON(r, &m); err != nil { @@ -200,49 +199,6 @@ func kindToSubdir(kind string) string { return "player_flag" } -func listTriggerEntries(dataDir string) ([]triggerEntry, error) { - root := filepath.Join(dataDir, "triggers") - if _, err := os.Stat(root); os.IsNotExist(err) { - return []triggerEntry{}, nil - } - var entries []triggerEntry - var walk func(p string) error - walk = func(p string) error { - dirs, err := os.ReadDir(p) - if err != nil { - return err - } - for _, entry := range dirs { - full := filepath.Join(p, entry.Name()) - if entry.IsDir() { - if err := walk(full); err != nil { - return err - } - continue - } - ext := filepath.Ext(entry.Name()) - if ext != ".yaml" && ext != ".yml" { - continue - } - id := strings.TrimSuffix(entry.Name(), ext) - // Determine kind from the parent directory name. - parent := filepath.Base(filepath.Dir(full)) - kind := "" - if parent == "global_flag" { - kind = "global" - } else if parent == "player_flag" { - kind = "player" - } - entries = append(entries, triggerEntry{ID: id, Kind: kind}) - } - return nil - } - if err := walk(root); err != nil { - return nil, err - } - return entries, nil -} - // findTriggerFile searches for a trigger file in the triggers directory tree // and returns its full path and the parent directory name (relative to the // triggers root). Searches player_flag/ then global_flag/ then root. diff --git a/internal/admin/static/triggerseditor.js b/internal/admin/static/triggerseditor.js index 7cb5d9e..0c18ce4 100644 --- a/internal/admin/static/triggerseditor.js +++ b/internal/admin/static/triggerseditor.js @@ -19,31 +19,80 @@ function initTriggerEditor() { } function loadTriggerList() { - API.get('/api/triggers').then(function(data) { - allIDs = (data || []).map(function(e) { return e.id || e; }); - window._triggerEntries = data || []; - renderTriggerList(''); - }).catch(function(e) { - notify('Failed to load list: ' + e.message, 'error'); - }); + loadList(); } function renderTriggerList(filter) { + renderList(filter); +} + +function renderList(filter) { var el = $('#listEntries'); var f = (filter || '').toLowerCase(); - var entries = window._triggerEntries || []; + var t = treeData; + + function filterIDs(ids) { + if (!f) return ids; + return ids.filter(function(id) { return String(id).toLowerCase().indexOf(f) >= 0; }); + } + + function renderItems(ids) { + var h = ''; + (ids || []).forEach(function(id) { + h += '
No triggers
'; + + el.querySelectorAll('details.dir-group').forEach(function(d) { + d.addEventListener('toggle', function() { + var n = d.getAttribute('data-name'); + if (n) dirOpenState[n] = d.open; + }); + }); } function loadTriggerItem(id) { @@ -170,7 +219,8 @@ function renderTriggerEditor(id, data) { html += 'Deleted
'; + } + loadTriggerList(); + }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); }); +} + function deleteTrigger() { if (!confirm('Delete trigger "' + triggerID + '"?')) return; API.del('/api/triggers/' + encodeURIComponent(triggerID)).then(function() { -- cgit v1.2.3