Skip to content

Commit b4a6165

Browse files
fix: improve dashboard UI (View as, icon, chat, connections)
Dashboard UX: move View as into the sidebar, monochrome dashboard icon, markdown + brevity for agent chat, and fold Connections into the connection dropdown. Verified on Stayflexi before merge.
1 parent 17c60bb commit b4a6165

8 files changed

Lines changed: 449 additions & 128 deletions

File tree

‎backend/src/main/java/com/dbaagent/service/DashboardAgentService.java‎

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ private String buildChatTask(String prompt) {
220220
+ "It does not read as a request to build or change a chart/dashboard — it looks like a "
221221
+ "greeting, small talk, or a question about what you can do. Reply briefly and naturally "
222222
+ "in plain text (no HTML, no code block, no tool calls, no grounding, no SQL). If it's a "
223-
+ "greeting, greet back and invite them to describe a dashboard. Keep it to 1-2 sentences.";
223+
+ "greeting, greet back and invite them to describe a dashboard.\n\n"
224+
+ "STYLE: Keep it to 1-2 short sentences. Lead with the answer. No filler phrases like "
225+
+ "\"Great question!\" or restating what they asked. Just answer directly.";
224226
}
225227

226228
// ── the task the agent runs ────────────────────────────────────────────
@@ -335,17 +337,25 @@ Output your FINAL message as ONLY fenced blocks (one ```dashboard-shell``` first
335337
outside a fence, no tool calls after the last one.
336338
Do NOT wrap the whole thing in a single ```html block — the shell and each widget are SEPARATE fences.
337339
338-
7. END with a ```dashboard-note``` fence: 1-3 sentences to the person who asked, in the same plain
340+
7. END with a ```dashboard-note``` fence: a SHORT reply to the person who asked, in the same plain
339341
business language as the two hard rules above (a note naming a table, a column, SQL, or the
340342
connection id breaks the same security requirement the dashboard itself is bound by).
341-
Say what THIS turn actually changed — not that a dashboard exists. Then, in the same note:
342-
- State anything you could NOT do, could not verify, or chose to skip, and why. A build that
343-
partly worked must say so. Never claim a number is correct because a query returned it.
343+
344+
REPLY STYLE — brevity is mandatory:
345+
- Lead with what changed or what you built. No filler, no restating their request.
346+
- Keep it to 1-3 short lines. Use markdown bullets when listing multiple items (requirements
347+
met, status updates, or next steps). Never write long paragraphs.
348+
- Skip phrases like "I've created...", "Here's what I did...", "As requested...". Just state
349+
the facts: "Added revenue chart. Date range defaults to last 30 days."
350+
- Only write more than 3 lines when explaining an error, a limitation you hit, or a breaking
351+
change — and even then stay concise.
352+
353+
Content requirements (still apply):
354+
- State anything you could NOT do, could not verify, or chose to skip, and why.
344355
- If the user was correcting or disputing something (a wrong figure, a chart that didn't load),
345-
say plainly whether it is now fixed, and what the value/behaviour is now versus what they
346-
reported. If you could not reproduce or resolve their complaint, say THAT — do not answer a
347-
correction with a description of what you built.
348-
Write it as you would to a colleague: specific and short. Never open with "Done".""");
356+
say plainly whether it is now fixed. If you could not resolve it, say THAT.
357+
- Never claim a number is correct just because a query returned it.
358+
- Never open with "Done" or "Great".""");
349359
return sb.toString();
350360
}
351361

‎src/components/layout/AppSidebar.jsx‎

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AGENTS_ENABLED, canAccessHomeSection, getConnectionAccessBadge, getConn
77
import { PERMISSIONS } from '@/lib/permissions'
88
import ManageConnectionsModal from '@/components/ManageConnectionsModal'
99
import SettingsModal from '@/components/SettingsModal'
10+
import ProfileSwitch from './ProfileSwitch'
1011
import { useAuth } from '@/hooks/useAuth'
1112
import styles from './AppSidebar.module.css'
1213

@@ -30,7 +31,7 @@ export default function AppSidebar() {
3031
const [showConnectionDropdown, setShowConnectionDropdown] = useState(false)
3132
const userMenuRef = useRef(null)
3233
const connectionDropdownRef = useRef(null)
33-
const { logout, role, username, impersonating, permissions, hasPermission } = useAuth()
34+
const { logout, role, username, impersonating, permissions, hasPermission, canSwitchProfile } = useAuth()
3435
// Connections (add/edit/delete a database) is administrative: the backend already
3536
// refuses it to Developer and Data Engineer with 403, so hiding the button stops the
3637
// UI offering a door that only leads to an error. Settings is likewise administrative
@@ -137,12 +138,13 @@ export default function AppSidebar() {
137138
{/* Bottom: connections + profile */}
138139
<div className={styles.divider} />
139140
<div className={styles.bottom}>
141+
{canSwitchProfile && <ProfileSwitch collapsed={collapsed} />}
140142
<div className={styles.connectionSwitcher} ref={connectionDropdownRef}>
141143
<button
142144
className={styles.bottomItem}
143145
onClick={() => setShowConnectionDropdown((v) => !v)}
144146
title={collapsed ? connectionLabel : undefined}
145-
disabled={isLoading || connections.length === 0}
147+
disabled={isLoading || (connections.length === 0 && !canManageConnections)}
146148
>
147149
<Database size={15} className={styles.navIcon} />
148150
<span className={`${styles.navLabel} ${collapsed ? styles.navLabelHidden : ''}`}>
@@ -154,62 +156,69 @@ export default function AppSidebar() {
154156
{!collapsed && <ChevronDown size={14} className={styles.chevronIcon} />}
155157
</button>
156158

157-
{showConnectionDropdown && !collapsed && connections.length > 0 && (
159+
{showConnectionDropdown && !collapsed && (
158160
<div className={styles.connectionDropdown}>
159-
<div className={styles.dropdownLabel}>Connections</div>
160-
{connections.map((conn) => (
161-
<div
162-
key={conn.id}
163-
className={`${styles.dropdownRow} ${conn.id === connectionId ? styles.dropdownItemActive : ''}`}
164-
>
161+
{connections.length > 0 && (
162+
<>
163+
<div className={styles.dropdownLabel}>Connections</div>
164+
{connections.map((conn) => (
165+
<div
166+
key={conn.id}
167+
className={`${styles.dropdownRow} ${conn.id === connectionId ? styles.dropdownItemActive : ''}`}
168+
>
169+
<button
170+
className={styles.dropdownItem}
171+
onClick={() => {
172+
changeConnection(conn.id)
173+
setShowConnectionDropdown(false)
174+
}}
175+
>
176+
<Database size={14} />
177+
<span className={styles.dropdownItemName}>
178+
{conn.connectionName}
179+
{getConnectionAccessLabel(conn) ? ` · ${getConnectionAccessLabel(conn)}` : ''}
180+
</span>
181+
<span className={styles.dbTypeBadge}>{getConnectionAccessBadge(conn) || conn.dbType}</span>
182+
{conn.id === connectionId && <Check size={13} className={styles.dropdownItemCheck} />}
183+
</button>
184+
<button
185+
className={`${styles.dropdownPin} ${conn.pinned ? styles.dropdownPinActive : ''}`}
186+
onClick={() =>
187+
setConnectionPin.mutate({ connectionId: conn.id, pinned: !conn.pinned })
188+
}
189+
disabled={setConnectionPin.isPending}
190+
aria-pressed={Boolean(conn.pinned)}
191+
title={
192+
conn.pinned
193+
? 'Pinned as your default — DeepSQL opens on this connection. Click to unpin.'
194+
: 'Pin as your default — DeepSQL will open on this connection every time you load it.'
195+
}
196+
>
197+
<Pin size={13} />
198+
</button>
199+
</div>
200+
))}
201+
</>
202+
)}
203+
{canManageConnections && (
204+
<>
205+
{connections.length > 0 && <div className={styles.dropdownDivider} />}
165206
<button
166-
className={styles.dropdownItem}
207+
className={styles.dropdownManage}
167208
onClick={() => {
168-
changeConnection(conn.id)
169209
setShowConnectionDropdown(false)
210+
setShowConnections(true)
170211
}}
171212
>
172-
<Database size={14} />
173-
<span className={styles.dropdownItemName}>
174-
{conn.connectionName}
175-
{getConnectionAccessLabel(conn) ? ` · ${getConnectionAccessLabel(conn)}` : ''}
176-
</span>
177-
<span className={styles.dbTypeBadge}>{getConnectionAccessBadge(conn) || conn.dbType}</span>
178-
{conn.id === connectionId && <Check size={13} className={styles.dropdownItemCheck} />}
213+
<Settings size={14} />
214+
<span>Manage connections…</span>
179215
</button>
180-
<button
181-
className={`${styles.dropdownPin} ${conn.pinned ? styles.dropdownPinActive : ''}`}
182-
onClick={() =>
183-
setConnectionPin.mutate({ connectionId: conn.id, pinned: !conn.pinned })
184-
}
185-
disabled={setConnectionPin.isPending}
186-
aria-pressed={Boolean(conn.pinned)}
187-
title={
188-
conn.pinned
189-
? 'Pinned as your default — DeepSQL opens on this connection. Click to unpin.'
190-
: 'Pin as your default — DeepSQL will open on this connection every time you load it.'
191-
}
192-
>
193-
<Pin size={13} />
194-
</button>
195-
</div>
196-
))}
216+
</>
217+
)}
197218
</div>
198219
)}
199220
</div>
200221

201-
{canManageConnections && (
202-
<button
203-
className={styles.bottomItem}
204-
onClick={() => setShowConnections(true)}
205-
title={collapsed ? 'Connections' : undefined}
206-
>
207-
<Settings size={15} className={styles.navIcon} />
208-
<span className={`${styles.navLabel} ${collapsed ? styles.navLabelHidden : ''}`}>
209-
Connections
210-
</span>
211-
</button>
212-
)}
213222
<div className={styles.userMenuWrap} ref={userMenuRef}>
214223
<button
215224
className={styles.bottomItem}

‎src/components/layout/AppSidebar.module.css‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,34 @@
338338
color: #111827;
339339
}
340340

341+
.dropdownDivider {
342+
height: 1px;
343+
background: #e5e7eb;
344+
margin: 4px 8px;
345+
}
346+
347+
.dropdownManage {
348+
display: flex;
349+
align-items: center;
350+
gap: 8px;
351+
width: calc(100% - 8px);
352+
margin: 4px;
353+
padding: 8px 10px;
354+
border: none;
355+
border-radius: 8px;
356+
background: transparent;
357+
font-size: 13px;
358+
color: #6b7280;
359+
cursor: pointer;
360+
text-align: left;
361+
transition: background 0.1s, color 0.1s;
362+
}
363+
364+
.dropdownManage:hover {
365+
background: #f3f4f6;
366+
color: #111827;
367+
}
368+
341369
/* ── User profile dropdown ── */
342370
.userMenuWrap {
343371
position: relative;

0 commit comments

Comments
 (0)