Skip to content
25 changes: 5 additions & 20 deletions gearbox/internal/framework/dashboard/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ func (s *Storage) CreateDefaultDashboard() error {
return nil
}

// Create default dashboard
// Create default dashboard with no widgets.
// Empty dashboards auto-redirect to edit mode with the widget palette open,
// so users can immediately start adding widgets.
Comment thread
sarg3nt marked this conversation as resolved.
Outdated
dashboard := &Dashboard{
Version: "1.0",
Name: "Dashboard",
Expand All @@ -221,25 +223,8 @@ func (s *Storage) CreateDefaultDashboard() error {
Columns: 12,
Gap: 4,
},
Widgets: []Widget{
{
ID: "welcome-1",
Type: "alert-banner",
Position: WidgetPosition{
Row: 1,
Column: 1,
Width: 12,
Height: "auto",
},
Config: map[string]interface{}{
"severity": "info",
"message": "Welcome to Gearbox! This is your default dashboard. You can customize it by adding widgets.",
"icon": "info",
"dismissible": true,
},
},
},
Slug: "dashboard",
Widgets: []Widget{},
Slug: "dashboard",
}

// Save dashboard
Expand Down
41 changes: 40 additions & 1 deletion gearbox/internal/framework/handler/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,42 @@ func (h *DashboardHandler) ViewDashboard(w http.ResponseWriter, r *http.Request)
return
}

// If dashboard is empty and editable, guide the user to set up their dashboard
if dash.Editable && len(dash.Widgets) == 0 {
// Determine the current server ID to check plugin state
boxID := h.boxID
if boxID == "" {
// Fall back to the first enabled server
if servers, err := h.db.GetEnabledBoxes(); err == nil && len(servers) > 0 {
boxID = servers[0].BoxID
}
}

// Check if any plugins are enabled for this server.
// If not, send them to the plugins page first so they can enable plugins
// (which registers widgets they can then add to dashboards).
if boxID != "" {
enabledPlugins, err := h.db.GetEnabledPlugins(boxID)
if err == nil {
hasEnabledPlugin := false
for _, enabled := range enabledPlugins {
if enabled {
hasEnabledPlugin = true
break
}
}
if !hasEnabledPlugin {
http.Redirect(w, r, "/settings/plugins", http.StatusSeeOther)
return
}
}
}

// Plugins are enabled, redirect to edit mode with widget palette open
http.Redirect(w, r, "/dashboards/"+slug+"/edit?open_palette=1", http.StatusSeeOther)
return
}

// Render dashboard
content, err := h.renderer.Render(r.Context(), dash, h.boxID, h.userID)
if err != nil {
Expand Down Expand Up @@ -169,11 +205,14 @@ func (h *DashboardHandler) EditDashboardPage(w http.ResponseWriter, r *http.Requ
// Get available widgets
widgets := h.widgetRegistry.List()

// Check if palette should be auto-opened (e.g., redirected from empty dashboard)
openPalette := r.URL.Query().Get("open_palette") == "1"

// Get user from context
user, _ := auth.GetUserFromContext(r.Context())

// Render editor with live content
component := pages.DashboardEditorPage(dash, content, widgets, user, r.URL.Path)
component := pages.DashboardEditorPage(dash, content, widgets, user, r.URL.Path, openPalette)
Comment thread
sarg3nt marked this conversation as resolved.
if err := component.Render(r.Context(), w); err != nil {
h.logger.Error("failed to render dashboard editor", "error", err)
http.Error(w, "Failed to render page", http.StatusInternalServerError)
Expand Down
14 changes: 11 additions & 3 deletions gearbox/internal/framework/templates/pages/dashboard_editor.templ
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

// DashboardEditorPage renders the visual dashboard editor with live widget preview
templ DashboardEditorPage(dash *dashboard.Dashboard, content templ.Component, widgets []*widget.WidgetDefinition, user *models.User, currentPath string) {
templ DashboardEditorPage(dash *dashboard.Dashboard, content templ.Component, widgets []*widget.WidgetDefinition, user *models.User, currentPath string, openPalette bool) {
@layouts.Base("Edit Dashboard", user, currentPath) {
<!-- Widget Palette CSS -->
<link rel="stylesheet" href="/static/css/dashboard/palette.css"/>
Expand Down Expand Up @@ -49,8 +49,8 @@ templ DashboardEditorPage(dash *dashboard.Dashboard, content templ.Component, wi
</div>
</div>

<!-- Widget Palette Panel (collapsible) -->
<div id="widget-palette-panel" class="hidden mb-4 bg-white dark:bg-slate-900 border border-gray-300 dark:border-slate-600 rounded-lg shadow-lg">
<!-- Widget Palette Panel (collapsible, auto-opened when dashboard is empty) -->
<div id="widget-palette-panel" class={ widgetPaletteClass(openPalette) }>
<!-- Fixed Header -->
<div class="p-4 border-b border-gray-200 dark:border-slate-700 sticky top-0 bg-white dark:bg-slate-900 z-10">
<div class="flex items-center justify-between mb-3">
Expand Down Expand Up @@ -393,6 +393,14 @@ templ DashboardEditorScript(dash *dashboard.Dashboard) {
}

// Helper functions

func widgetPaletteClass(openPalette bool) string {
if openPalette {
return "mb-4 bg-white dark:bg-slate-900 border border-gray-300 dark:border-slate-600 rounded-lg shadow-lg"
}
return "hidden mb-4 bg-white dark:bg-slate-900 border border-gray-300 dark:border-slate-600 rounded-lg shadow-lg"
}

func getWidgetStyle(pos dashboard.WidgetPosition) string {
return fmt.Sprintf("grid-column: span %d;", pos.Width)
}
Expand Down
11 changes: 11 additions & 0 deletions gearbox/static/js/dashboard/palette.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,17 @@ function toggleWidgetPalette() {
}
}

// Auto-initialize palette if it's already visible on page load
// (e.g., when redirected from an empty dashboard with open_palette=1)
document.addEventListener('DOMContentLoaded', function() {
const panel = document.getElementById('widget-palette-panel');
if (panel && !panel.classList.contains('hidden')) {
const dashboardElement = document.getElementById('dashboard-grid-editor');
const boxID = dashboardElement ? dashboardElement.dataset.boxId : '';
initializeWidgetPalette(boxID);
Comment thread
sarg3nt marked this conversation as resolved.
}
});

// Export functions for use in other scripts
window.initializeWidgetPalette = initializeWidgetPalette;
window.toggleWidgetPalette = toggleWidgetPalette;