diff --git a/CHANGELOG.md b/CHANGELOG.md index 282eca1..2d32f9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,45 @@ All notable changes to Checkgate are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/). +## [0.1.19] - 2026-07-05 + +### Changed + +- **Dashboard layout overhaul** — the sidebar can now be collapsed to an icon-only rail (persisted + across sessions via `localStorage`), reclaiming space on smaller screens. Every list/table page + (Dashboard, Feature Flags, Environments, Compare, Change Requests, Projects, Users, Audit Log, + Impressions, SDK Health, Scheduled, Webhooks) is now genuinely full width instead of capped at an + arbitrary max-width with dead space on either side. +- **Flag Editor and Settings restructured into two columns** — behavior/configuration (flag type, + rollout, targeting rules; SDK snippets, personal access tokens) in a wider main column, with + identity/metadata (key, tags, ownership, prerequisites; auth info, API endpoint, account) in a + narrower sidebar. Previously everything was stacked in a single column, requiring far more + scrolling and leaving most of the screen empty on anything wider than a laptop. +- **Setup and Login pages** now fill the browser window edge to edge (previously framed/centered + with growing dead space in the middle on wide monitors, then over-corrected to a narrow centered + card — this settles on genuinely full-width panels with wider inner content blocks). The Setup + welcome screen's headline now wraps to two lines instead of three, and gained an "Already set up? + Sign in" link for anyone who lands there after setup is already complete. +- `ProjectSettings` now supports deep-linking to a specific tab via `?tab=keys` (etc.), so other + pages can link straight to e.g. SDK Keys instead of dropping the user on the default tab. + +### Fixed + +- **Setup/login redirect loop** — `/api/auth/me` 401s for anyone without a valid session cookie, + which the frontend was also using to determine "has setup been completed?" A fresh browser, a + different device, or a session that expired after a server restart would incorrectly conclude + setup had never been done and permanently redirect to `/setup` instead of `/login`, with no way + back. Fixed by checking the public `/api/auth/workspace` endpoint (now also returns + `is_setup_complete`) instead, and by fixing a related race where the redirect decision fired on a + stale guess before that check even resolved. +- **Dead "SDK Keys" link on the Settings page** — pointed to `/settings` (itself) instead of the + actual key-management page. Now links to `/projects/{id}?tab=keys` and lands directly on the SDK + Keys tab. +- **React Native SDK packaging** — `sdks/react-native/android/.gradle/` (a local Gradle build cache, + accidentally committed) was being swept into the published npm tarball via the `files` field + covering the whole `android/` directory. Untracked and gitignored; harmless to existing consumers, + just junk that shouldn't have shipped. + ## [0.1.18] - 2026-07-04 ### Added diff --git a/Cargo.lock b/Cargo.lock index c00f53d..39a02fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,7 +246,7 @@ dependencies = [ [[package]] name = "browser" -version = "0.1.18" +version = "0.1.19" dependencies = [ "checkgate-core", "console_error_panic_hook", @@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "checkgate-core" -version = "0.1.18" +version = "0.1.19" dependencies = [ "dashmap", "murmur3", @@ -638,7 +638,7 @@ dependencies = [ [[package]] name = "flutter" -version = "0.1.18" +version = "0.1.19" dependencies = [ "checkgate-core", "serde_json", @@ -1414,7 +1414,7 @@ dependencies = [ [[package]] name = "nodejs" -version = "0.1.18" +version = "0.1.19" dependencies = [ "checkgate-core", "napi", @@ -1850,7 +1850,7 @@ dependencies = [ [[package]] name = "react-native" -version = "0.1.18" +version = "0.1.19" dependencies = [ "checkgate-core", "serde_json", @@ -2132,7 +2132,7 @@ dependencies = [ [[package]] name = "server" -version = "0.1.18" +version = "0.1.19" dependencies = [ "argon2", "async-stream", diff --git a/Cargo.toml b/Cargo.toml index a791d57..5a00c83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,4 @@ members = [ resolver = "2" [workspace.package] -version = "0.1.18" +version = "0.1.19" diff --git a/README.md b/README.md index 65e9d2c..49bf46d 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ It is proudly built in Rust and ships with native SDKs for Node.js (NAPI), brows ![Environments](assets/screenshots/07-environments.png) +**Collapsible sidebar** — reclaim screen space on data-dense pages; the whole dashboard is full-width by default. + +![Collapsed sidebar](assets/screenshots/08-sidebar-collapsed.png) + --- ## Documentation diff --git a/assets/screenshots/01-dashboard.png b/assets/screenshots/01-dashboard.png index 60e7481..70aa505 100644 Binary files a/assets/screenshots/01-dashboard.png and b/assets/screenshots/01-dashboard.png differ diff --git a/assets/screenshots/02-feature-flags.png b/assets/screenshots/02-feature-flags.png index 5a0b174..9d0893c 100644 Binary files a/assets/screenshots/02-feature-flags.png and b/assets/screenshots/02-feature-flags.png differ diff --git a/assets/screenshots/03-flag-editor.png b/assets/screenshots/03-flag-editor.png index ac990b6..f6bcb32 100644 Binary files a/assets/screenshots/03-flag-editor.png and b/assets/screenshots/03-flag-editor.png differ diff --git a/assets/screenshots/04-change-requests.png b/assets/screenshots/04-change-requests.png index 23248b3..5051d6d 100644 Binary files a/assets/screenshots/04-change-requests.png and b/assets/screenshots/04-change-requests.png differ diff --git a/assets/screenshots/05-environment-diff.png b/assets/screenshots/05-environment-diff.png index c2768d1..b188a7d 100644 Binary files a/assets/screenshots/05-environment-diff.png and b/assets/screenshots/05-environment-diff.png differ diff --git a/assets/screenshots/06-settings-tokens.png b/assets/screenshots/06-settings-tokens.png index f14c060..6ff64de 100644 Binary files a/assets/screenshots/06-settings-tokens.png and b/assets/screenshots/06-settings-tokens.png differ diff --git a/assets/screenshots/07-environments.png b/assets/screenshots/07-environments.png index f7140bc..996baac 100644 Binary files a/assets/screenshots/07-environments.png and b/assets/screenshots/07-environments.png differ diff --git a/assets/screenshots/08-sidebar-collapsed.png b/assets/screenshots/08-sidebar-collapsed.png new file mode 100644 index 0000000..5ff8550 Binary files /dev/null and b/assets/screenshots/08-sidebar-collapsed.png differ diff --git a/dashboard/package.json b/dashboard/package.json index 1c4b39b..7e50fed 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -1,6 +1,6 @@ { "name": "checkgate-dashboard", - "version": "0.1.18", + "version": "0.1.19", "private": true, "type": "module", "scripts": { diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index c6ddee3..53ec112 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -23,7 +23,12 @@ import ProjectSettings from './pages/ProjectSettings' import Webhooks from './pages/Webhooks' function RequireAuth({ children }: { readonly children: React.ReactNode }) { - const { session, sessionLoading, isSetupComplete } = useAuth() + const { session, sessionLoading, isSetupComplete, setupLoading } = useAuth() + // Must not act on isSetupComplete until the authoritative check resolves — + // its pre-check value is only a localStorage guess, and routing to /setup + // on a wrong guess is a one-way trip (nothing there re-checks and bounces + // back once the real value arrives). + if (setupLoading) return null if (!isSetupComplete) return if (sessionLoading) return null if (!session) return @@ -31,18 +36,33 @@ function RequireAuth({ children }: { readonly children: React.ReactNode }) { } function PublicOnly({ children }: { readonly children: React.ReactNode }) { - const { session, sessionLoading, isSetupComplete } = useAuth() + const { session, sessionLoading, isSetupComplete, setupLoading } = useAuth() + if (setupLoading) return null if (!isSetupComplete) return if (sessionLoading) return null if (session) return return <>{children} } +function SetupOnly({ children }: { readonly children: React.ReactNode }) { + const { isSetupComplete, setupLoading } = useAuth() + if (setupLoading) return null + if (isSetupComplete) return + return <>{children} +} + export default function App() { return ( {/* Auth routes (no sidebar) */} - } /> + + + + } + /> !item.adminOnly || isAdmin) + const [collapsed, setCollapsed] = useState(() => localStorage.getItem(KEY_SIDEBAR_COLLAPSED) === 'true') + + function toggleCollapsed() { + setCollapsed(c => { + localStorage.setItem(KEY_SIDEBAR_COLLAPSED, String(!c)) + return !c + }) + } function handleLogout() { logout() @@ -180,35 +192,44 @@ export default function Sidebar() { } return ( -