Feature/customizable dashboard - #29
Merged
Merged
Conversation
…heming Add Go struct definitions (ThemeColors, ThemeTokens, ThemeBranding) and exported default constants (DefaultThemeTokens, DefaultThemeBranding) as the single source of truth for dashboard design defaults (RFC 0002 §3.1, Phase 1). qpi-ui: - Add ThemeColors, ThemeTokens, and ThemeBranding structs in internal/config/theme_defaults.go. - Define DefaultThemeTokens matching tailwind.config.js dark colors and index.css light colors. - Define DefaultThemeBranding matching Sidebar.tsx hard-coded brand strings. - Add unit tests in internal/config/theme_defaults_test.go verifying defaults and JSON serialization. Verified: `go test` unit tests pass clean, `go build` succeeds.
Add PocketBase themes collection, Go struct Theme, schema migration ensureThemesCollection, configuration settings, and OnThemeUpsert hook enforcing single active theme uniqueness (RFC 0002 §3.2-3.5, Phase 2). qpi-ui: - Add DefaultThemesCollection constant, flagCollectionThemes, CollectionThemes AppConfig field, and resolution mappings in internal/config/config.go. - Define Theme model struct with ToRecord and RefreshFromRecord methods in internal/db/models.go. - Add ensureThemesCollection migration in internal/db/migrate.go creating themes collection with public read and superuser-only CUD rules. - Add OnThemeUpsert hook in internal/db/hooks.go deactivating other active themes when a theme is saved with is_active=true. - Register DefaultThemesCollection: db.OnThemeUpsert in main.go for OnRecordCreate and OnRecordUpdate. - Add unit tests in internal/db/theme_test.go, internal/db/migrate_test.go, and internal/db/hooks_test.go. Verified: `make format-go`, `make lint-go`, `make build`, and `go test ./...` in qpi-ui all pass clean.
Add custom public HTTP endpoints (/api/theme/active, /api/theme/defaults, /api/theme/css, /api/theme/js) and wire the in-memory active theme cache into PocketBase bootstrap and collection hooks. qpi-ui: - Implement in-memory active theme cache helpers SaveActiveThemeOnApp, GetActiveThemeFromApp, and GetDefaultThemeRecord in theme_cache.go. - Update OnThemeUpsert and add OnThemeDelete in hooks.go to sync active theme cache on create, update, and delete. - Initialize active theme cache on bootstrap and bind OnThemeDelete hook in main.go. - Add HTTP handlers handleThemeActive, handleThemeDefaults, handleThemeCSS, and handleThemeJS in theme_api.go. - Register theme routes in api.go. - Add unit tests for theme endpoints in theme_api_test.go. docs: - Update RFC 0002 and ROADMAP Phase 3 specifications reflecting active theme cache fallback to default theme. Verified: `make format-go`, `go vet ./...` in qpi-ui, and `go test -v ./internal/api -run TestThemeAPI` all pass clean.
Refactor hard-coded design tokens and Tailwind configuration into CSS custom properties with fallbacks matching Go default theme constants (RFC 0002 §5.2, Phase 4). qpi-ui/internal/dashboard: - Add @theme directive in src/index.css mapping Tailwind utilities to --qpi-* CSS custom properties with Light theme defaults. - Add temporary .dark fallback block setting --qpi-* custom properties to Dark theme defaults until Phase 5 ThemeContext runtime application. - Update base body and scrollbar styles in src/index.css to reference CSS custom properties. - Remove hardcoded colors and spacing overrides from tailwind.config.js in favor of CSS @theme directive. Verified: `make format-dashboard`, `make lint-dashboard`, and `make build-dashboard` all pass clean.
Implement the ThemeContext for the frontend dashboard to fetch the active theme from the server, apply design tokens as CSS custom properties, and handle mode switching (light/dark). qpi-ui: - Create `ThemeContext.tsx` to handle the active theme fetching and dynamic application of `--qpi-*` variables. - Update `main.tsx` to wrap the app in the new `ThemeProvider`. - Refactor `ThemeToggle.tsx` to use `toggleMode` and `isDark` from `useTheme()`. - Remove the fallback dark mode CSS block from `index.css` as `ThemeContext` now manages it dynamically. Verified: `make format` passed, `cd qpi-ui/internal/dashboard && npm run build` succeeded, and dashboard E2E tests in `make test` pass clean.
Replace hard-coded branding (site name, tagline, logo, favicon) across the dashboard sidebar, header, document title, and login modal with dynamic values from ThemeContext (RFC 0002 §5.3, Phase 6). qpi-ui/internal/dashboard: - Add useEffect hooks in ThemeContext.tsx for dynamic document.title and favicon <link rel="icon"> insertion/updates. - Update Sidebar.tsx to display dynamic siteName, tagline, and custom logo image with fallback to Cpu icon. - Update LoginModal.tsx to display custom siteName header and logo image with fallback to Lock icon. - Update index.html title tag for dynamic ThemeContext management. Verified: `make format-dashboard`, `make lint-dashboard`, `npm run build`, `make test-go`, and `make test-e2e-dashboard` (106/106 Cypress E2E tests) all pass clean.
Fetch and inject active theme custom CSS into document head and execute custom JS in body from /api/theme/css and /api/theme/js endpoints in ThemeContext (RFC 0002 §5.1, Phase 7). qpi-ui/internal/dashboard: - Add loadThemeCSS and loadThemeJS helpers and useEffect hook in ThemeContext.tsx for dynamic active theme CSS and JS injection. - Add Cypress E2E test suite in cypress/e2e/overview-header/theme-custom-injection.cy.ts verifying style/script element DOM injection, JS execution, 204 handling, and mode toggle re-fetch prevention. Verified: `make format-dashboard`, `make lint-dashboard`, `npm run build`, `make test-go`, and Cypress E2E tests (`./e2e/test_dashboard_cypress.sh`) all pass clean.
- Extract `ThemeRecord` interface to `types.ts` for global reuse - Implement `AppearanceInnerTab` to list, activate, and delete themes - Build `ThemeEditorModal` for comprehensive theme creation/editing (branding, CSS, JS, design tokens, icons) - Integrate Appearance tab into `AdminPanelTab` routing and UI navigation - Fix Cypress test flakiness in `dashboard.cy.ts` where toggling the Mock QPU offline broke subsequent tests in `jobs-console` suite - Add E2E tests for Appearance Admin Tab (`appearance.cy.ts`) - Defer Live Theme Preview feature to Phase 9 in ROADMAP.md
…shboard theming Finalize the dashboard theming feature by adding end-to-end tests, a live preview feature in the admin panel, robust handling of theme JSON parsing, and documentation updates. qpi-ui: - Add TestThemeCollectionRules in theme_test.go to verify unauthenticated users cannot access themes via the PocketBase collection API. dashboard: - Export applyTokens in ThemeContext.tsx and add debouncing to the theme toggle. - Add Preview and Reset Preview functionality to the ThemeEditorModal for live previewing themes before saving. - Expand Cypress appearance.cy.ts tests to cover the full lifecycle: creating, previewing, activating, and deleting themes. docs: - Update README.md to mention the new Dashboard Theming feature. - Add theming.md to MkDocs covering theming configuration and JSON token examples. - Update mkdocs.yml to include the new theming documentation in the navigation. Verified: `make format`, `make lint`, `make test`, and `make test-e2e-dashboard` all pass clean.
Tinitto
force-pushed
the
feature/customizable-dashboard
branch
from
July 24, 2026 13:35
a1d1675 to
def6ef6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
QPI ships a single, hard-coded dashboard look-and-feel. Every deployment looks
identical — same colours, same logo icon, same "QPI Interface" brand text. For
organisations hosting their own QPI instance, this is limiting: a university lab
wants its crest in the sidebar, a startup wants its brand palette, and some
operators want to go further — injecting custom CSS or even JavaScript to reshape
the dashboard without forking the codebase.
This PR introduces runtime-customizable theming. A superuser creates theme
records in a new PocketBase
themescollection, activates one, and every user'sdashboard immediately picks up the new look.
More details ...
What was done
Added
qpi-ui: Added Admin Theme Management feature (RFC 0002) allowing administrators to create, preview, activate, and delete custom themes directly from the dashboard.qpi-ui: Addedthemescollection to PocketBase database for persisting theme records and custom branding configurations (logo, favicon).qpi-ui: Added/api/theme/defaults,/api/theme/active,/api/theme/css, and/api/theme/jsendpoints to serve active theme configuration and injected assets.qpi-ui: AddedactiveThemetoAppConfigto serve as a high-performance, globally consistent in-memory cache for the active theme, avoiding expensive database queries.qpi-ui: Added ReactThemeContexton the frontend for dynamic application of CSS variables (rgb()variants) and custom assets based on the active theme, gracefully falling back to a compiled-in default theme.qpi-ui: Added a Theme management UI to the Admin Dashboard (Settings -> Appearance) for customizing Design Tokens (JSON) and raw Custom CSS/JS with real-time preview functionality.qpi-ui: OptimizedOnThemeUpserthook to use a raw database query to efficiently deactivate sibling themes, avoiding nested hook executions.docs: Addeddocs/theming.mddocumentation guide for the Dashboard Theming engine.