diff --git a/src/server/public/assets/css/modern-pixel.css b/src/server/public/assets/css/modern-pixel.css
index 9f2134d..35b4cae 100644
--- a/src/server/public/assets/css/modern-pixel.css
+++ b/src/server/public/assets/css/modern-pixel.css
@@ -76,7 +76,7 @@ body::before {
/* Tipografía Moderna */
h1 {
- font-family: 'Press Start 2P', monospace;
+ font-family: 'BoldPixels', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
font-size: clamp(2rem, 5vw, 4rem);
font-weight: 400;
line-height: 1.3;
diff --git a/src/server/views/partials/dashboard/dashboard_settings.ejs b/src/server/views/partials/dashboard/dashboard_settings.ejs
index 5cd13b3..e003d95 100644
--- a/src/server/views/partials/dashboard/dashboard_settings.ejs
+++ b/src/server/views/partials/dashboard/dashboard_settings.ejs
@@ -13,35 +13,25 @@
-
-
- <% if (typeof guildRoles !== 'undefined' && guildRoles && guildRoles.length) { %>
-
-
-
-
- <% } else { %>
- <% const fallbackStaff = (guildConfig && Array.isArray(guildConfig.staff) ? guildConfig.staff.map(String) : (guildConfig && guildConfig.staff ? String(guildConfig.staff).split(',') : [])) || []; %>
- <% if (fallbackStaff.length) { %>
+
+
+
+ <% const selectedStaff = (guildConfig && Array.isArray(guildConfig.staff) ? guildConfig.staff.map(String) : (guildConfig && guildConfig.staff ? String(guildConfig.staff).split(',') : [])) || []; %>
+ <% if (typeof guildRoles !== 'undefined' && guildRoles && guildRoles.length) { %>
-
+
+
+
+
Selecciona roles del servidor. Escribe para filtrar, pulsa Enter para elegir.
-
<% } else { %>
-
No se pudo obtener roles desde la API ni hay roles guardados. Introduce IDs manualmente separadas por coma.
+
No se pudo obtener roles desde la API. Introduce IDs manualmente separadas por coma.
<% } %>
- <% } %>
-
+
@@ -98,4 +88,134 @@
}
});
})();
+ (function(){
+ const form = document.getElementById('guildSettingsForm');
+ const status = document.getElementById('saveStatus');
+
+ // Multi-select tag/autocomplete for roles
+ const staffTagInput = document.getElementById('staffTagInput');
+ const staffSuggestions = document.getElementById('staffSuggestions');
+ const staffChips = document.getElementById('staffChips');
+ const staffHidden = document.getElementById('staffHidden');
+
+ // Prepare role options (id, name, color) from server-side `guildRoles`
+ <% const ROLE_JSON = JSON.stringify((guildRoles||[]).map(r=>{ var c; if (typeof r.color !== 'undefined' && r.color !== null) { if (typeof r.color === 'number') { c = '#'+('000000'+r.color.toString(16)).slice(-6); } else { c = (''+r.color).replace(/^#?/, '#'); } } else { c = (r.colorHex||r.hex||'#8b95a0'); } return { id: String(r.id), name: r.name, color: c }; })); %>
+ const ROLE_OPTIONS = <%- ROLE_JSON %>;
+ // Initial selected staff IDs (strings)
+ const INITIAL_STAFF = <%- JSON.stringify(selectedStaff || []) %>;
+
+ let selectedIds = Array.isArray(INITIAL_STAFF) ? INITIAL_STAFF.slice() : [];
+
+ function renderChips(){
+ staffChips.innerHTML = '';
+ for(const id of selectedIds){
+ const role = ROLE_OPTIONS.find(r=>r.id===String(id));
+ const label = role ? role.name : String(id);
+ const chip = document.createElement('div');
+ chip.className = 'px-2 py-1 bg-white/6 rounded-full text-sm flex items-center gap-2';
+ const swatch = role && role.color ? `` : '';
+ chip.innerHTML = `${swatch}${label}`;
+ staffChips.appendChild(chip);
+ }
+ // update hidden input
+ if(staffHidden) staffHidden.value = selectedIds.join(',');
+ }
+
+ function showSuggestions(query){
+ if(!staffSuggestions) return;
+ const q = (query||'').trim().toLowerCase();
+ const filtered = ROLE_OPTIONS.filter(r=> !selectedIds.includes(String(r.id)) && (!q || r.name.toLowerCase().includes(q) || String(r.id).includes(q)) ).slice(0,50);
+ if(!filtered.length){ staffSuggestions.classList.add('hidden'); staffSuggestions.innerHTML=''; return; }
+ staffSuggestions.classList.remove('hidden');
+ staffSuggestions.innerHTML = filtered.map(r=>`
${r.name}`).join('');
+ }
+
+ function addId(id){
+ id = String(id);
+ if(!id) return;
+ if(selectedIds.includes(id)) return;
+ selectedIds.push(id);
+ renderChips();
+ }
+
+ function removeId(id){
+ id = String(id);
+ selectedIds = selectedIds.filter(i=>String(i)!==id);
+ renderChips();
+ }
+
+ if(staffTagInput){
+ // initial render
+ renderChips();
+
+ staffTagInput.addEventListener('input', (e)=>{
+ showSuggestions(e.target.value);
+ });
+
+ staffTagInput.addEventListener('keydown', (e)=>{
+ if(e.key === 'Enter'){
+ e.preventDefault();
+ // pick first suggestion if any, otherwise ignore
+ const first = staffSuggestions.querySelector('li');
+ if(first){ addId(first.dataset.id); staffTagInput.value=''; staffSuggestions.classList.add('hidden'); }
+ } else if (e.key === 'Backspace' && !staffTagInput.value){
+ // remove last
+ selectedIds.pop(); renderChips();
+ }
+ });
+
+ document.addEventListener('click', (ev)=>{
+ if(!staffSuggestions) return;
+ if(ev.target.closest && ev.target.closest('#staffSuggestions')) return;
+ if(ev.target === staffTagInput) return;
+ staffSuggestions.classList.add('hidden');
+ });
+
+ staffSuggestions.addEventListener('click', (ev)=>{
+ const li = ev.target.closest('li');
+ if(!li) return;
+ addId(li.dataset.id);
+ staffTagInput.value='';
+ staffSuggestions.classList.add('hidden');
+ staffTagInput.focus();
+ });
+
+ staffChips.addEventListener('click', (ev)=>{
+ const btn = ev.target.closest('.remove-chip');
+ if(!btn) return;
+ removeId(btn.dataset.id);
+ });
+ }
+
+ form.addEventListener('submit', async (e)=>{
+ e.preventDefault();
+ status.textContent = 'Guardando...';
+ const prefix = document.getElementById('prefixInput').value.trim();
+ const aiRolePrompt = document.getElementById('aiRoleInput').value.trim();
+ let staffArr = [];
+ if(typeof selectedIds !== 'undefined' && selectedIds && selectedIds.length){
+ staffArr = selectedIds.slice();
+ } else if (document.getElementById('staffInput')){
+ const staffRaw = document.getElementById('staffInput').value.trim();
+ staffArr = staffRaw ? staffRaw.split(',').map(s=>s.trim()).filter(Boolean) : [];
+ }
+ const payload = { prefix: prefix, aiRolePrompt: aiRolePrompt.length ? aiRolePrompt : null, staff: staffArr };
+ try {
+ const res = await fetch(`/api/dashboard/${encodeURIComponent('<%= selectedGuild %>')}/settings`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ });
+ const json = await res.json();
+ if (res.ok && json.ok) {
+ status.textContent = 'Guardado';
+ setTimeout(()=> status.textContent = '', 2500);
+ } else {
+ status.textContent = json.error || 'Error';
+ }
+ } catch (err) {
+ status.textContent = 'Error de red';
+ }
+ });
+ })();