Dashboard: Card-Menü für Sessions und Agents

- Session-Cards: Menü mit 'Chat öffnen' und 'Bearbeiten'
- Agent-Cards: Menü mit 'Bearbeiten'
- Drei-Punkte-Trigger (ellipsis-v) erscheint bei Hover
- Click-outside schließt offene Menüs
- Card overflow auf visible geändert für Dropdown-Anzeige
This commit is contained in:
Meleki committed 2026-03-01 11:18:12 +01:00
1 parent 5037c7d24e
commit 92b1c8a9bc
2 files changed
+126 -4

No files matched your search

+63 -1
View File
@@ -21,11 +21,12 @@
}
.card {
position: relative;
background: var(--f3-bg);
border: var(--border-width) solid var(--f3-border);
display: flex;
flex-direction: column;
overflow: hidden;
overflow: visible;
border-radius: var(--border-radius);
}
@@ -42,6 +43,7 @@
aspect-ratio: 1;
background: var(--f2-bg);
overflow: hidden;
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
.card-image img {
@@ -99,6 +101,66 @@
padding: var(--button-padding);
}
/* Card Menu */
.card-menu {
position: absolute;
top: var(--badge-spacing);
right: var(--badge-spacing);
z-index: 10;
}
.card-menu-trigger {
opacity: 0;
transition: opacity .15s;
padding: .15em .45em;
font-size: var(--font-size);
background: var(--f3-bg);
border: var(--border-width) solid var(--f3-border);
border-radius: var(--border-radius);
}
.card:hover .card-menu-trigger {
opacity: var(--semi-transparent);
}
.card-menu-trigger:hover {
opacity: 1 !important;
}
.card-menu-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: .3em;
background: var(--f3-bg);
border: var(--border-width) solid var(--f3-border);
border-radius: var(--border-radius);
min-width: 10em;
display: flex;
flex-direction: column;
overflow: hidden;
padding-block: calc(var(--badge-spacing) / 2) var(--badge-spacing);
}
.card-menu-dropdown[hidden] {
display: none;
}
.card-menu-item {
border: none;
border-radius: unset;
text-align: left;
padding: var(--menu-item-padding);
background: none;
white-space: nowrap;
}
.card-menu-item:not(:disabled):hover {
background: var(--f2-bg);
border: none;
}
.card-meta {
display: flex;
flex-direction: column;
+63 -3
View File
@@ -2,6 +2,9 @@
registerPage('dashboard', {
async init() {
// Globaler Click-Handler zum Schließen aller Card-Menüs
document.addEventListener('click', this._closeCardMenus = () => closeAllCardMenus());
const agents = await apiGetAgents();
const agentMap = {};
for (const a of agents) agentMap[a.agentId] = a;
@@ -33,8 +36,35 @@ registerPage('dashboard', {
${workspaceLabel ? `<span class="card-meta-item card-workspace" title="${esc(workspaceLabel)}"><i class="fa fa-folder"></i> ${esc(workspaceLabel)}</span>` : ''}
</div>
</div>
<div class="card-menu">
<button class="card-menu-trigger icon-btn"><i class="fa fa-ellipsis-v"></i></button>
<div class="card-menu-dropdown" hidden>
<button class="card-menu-item" data-action="chat">Chat öffnen</button>
<button class="card-menu-item" data-action="edit">Bearbeiten</button>
</div>
</div>
`;
card.addEventListener('click', () => navigateTo('session', { id: s.sessionId }));
card.addEventListener('click', (e) => {
if (e.target.closest('.card-menu')) return;
navigateTo('session', { id: s.sessionId });
});
const menuTrigger = card.querySelector('.card-menu-trigger');
const menuDropdown = card.querySelector('.card-menu-dropdown');
menuTrigger.addEventListener('click', (e) => {
e.stopPropagation();
closeAllCardMenus();
menuDropdown.hidden = !menuDropdown.hidden;
});
menuDropdown.querySelector('[data-action="chat"]').addEventListener('click', (e) => {
e.stopPropagation();
menuDropdown.hidden = true;
navigateTo('chat', { id: s.sessionId });
});
menuDropdown.querySelector('[data-action="edit"]').addEventListener('click', (e) => {
e.stopPropagation();
menuDropdown.hidden = true;
navigateTo('session', { id: s.sessionId });
});
grid.appendChild(card);
// Git-Status asynchron laden
@@ -100,8 +130,29 @@ registerPage('dashboard', {
${a.description ? `<div class="card-desc">${esc(a.description)}</div>` : ''}
${a.persona ? `<div class="card-meta"><span class="card-meta-item card-persona"><i class="fa fa-user-secret"></i> ${esc(a.persona)}</span></div>` : ''}
</div>
<div class="card-menu">
<button class="card-menu-trigger icon-btn"><i class="fa fa-ellipsis-v"></i></button>
<div class="card-menu-dropdown" hidden>
<button class="card-menu-item" data-action="edit">Bearbeiten</button>
</div>
</div>
`;
card.addEventListener('click', () => navigateTo('agent', { id: a.agentId }));
card.addEventListener('click', (e) => {
if (e.target.closest('.card-menu')) return;
navigateTo('agent', { id: a.agentId });
});
const menuTrigger = card.querySelector('.card-menu-trigger');
const menuDropdown = card.querySelector('.card-menu-dropdown');
menuTrigger.addEventListener('click', (e) => {
e.stopPropagation();
closeAllCardMenus();
menuDropdown.hidden = !menuDropdown.hidden;
});
menuDropdown.querySelector('[data-action="edit"]').addEventListener('click', (e) => {
e.stopPropagation();
menuDropdown.hidden = true;
navigateTo('agent', { id: a.agentId });
});
agentGrid.appendChild(card);
}
@@ -111,9 +162,18 @@ registerPage('dashboard', {
placeholder.addEventListener('click', () => navigateTo('agent'));
document.getElementById('newAgentCard').appendChild(placeholder);
},
destroy() {}
destroy() {
if (this._closeCardMenus) {
document.removeEventListener('click', this._closeCardMenus);
this._closeCardMenus = null;
}
}
});
function closeAllCardMenus() {
document.querySelectorAll('.card-menu-dropdown').forEach(d => d.hidden = true);
}
function esc(s) {
const d = document.createElement('div');
d.textContent = s;