Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
26ce632
refactor(nav): remove the Organizations section from the console
levigtri Jul 3, 2026
1544d8b
feat(auth): gate the console shell to platform admins and managers
levigtri Jul 3, 2026
06a40e1
feat(access): make the project role selector editable only for platfo…
levigtri Jul 3, 2026
edae6e2
feat(auth): align the frontend manager model to per-project scoping
levigtri Jul 4, 2026
a7ee6fc
feat(apps): migrate app types to platforms array
levigtri Jul 5, 2026
01d9676
feat(apps): multi-select platforms with checkboxes
levigtri Jul 5, 2026
6e5376b
feat(apps): render one badge per platform
levigtri Jul 5, 2026
b355b76
feat(map): show team size in the project popup
levigtri Jul 5, 2026
9e3bed0
feat(projects): create a language inline from the project form
levigtri Jul 5, 2026
ccf9c44
feat(common): add a reusable FilterBar component
levigtri Jul 5, 2026
3b0c715
refactor(users): use the shared FilterBar on the Users page
levigtri Jul 5, 2026
35b4fb3
refactor(access): use the shared FilterBar on Access Requests
levigtri Jul 5, 2026
2a7ee91
refactor(phases): move dependency management into a dialog
levigtri Jul 5, 2026
94d45bc
feat(phases): show the phase description in the dependency dialog
levigtri Jul 5, 2026
783ae29
feat(phases): show phase description on graph node hover
levigtri Jul 5, 2026
236b9af
feat(languages): edit a language's name and code in a dialog
levigtri Jul 5, 2026
fcc9109
feat(languages): confirm with usage stats before deactivating
levigtri Jul 5, 2026
944cad9
feat(map): show completed phases summary in the project popup
levigtri Jul 5, 2026
88843b2
feat(dashboard): platform-specific launch button icons
levigtri Jul 5, 2026
bb71e06
feat(image-upload): make change/remove actions visually obvious
levigtri Jul 5, 2026
fd4b922
Merge remote-tracking branch 'origin/levigft/obt-240-us-129-align-the…
levigtri Jul 5, 2026
c9241e4
Merge remote-tracking branch 'origin/levigft/obt-205-us-42-the-typesc…
levigtri Jul 5, 2026
48c1629
Merge remote-tracking branch 'origin/levigft/obt-206-us-43-select-mul…
levigtri Jul 5, 2026
53933ad
Merge remote-tracking branch 'origin/levigft/obt-207-us-44-see-indivi…
levigtri Jul 5, 2026
acc10f9
Merge remote-tracking branch 'origin/levigft/obt-208-us-51-the-launch…
levigtri Jul 5, 2026
b48e093
Merge remote-tracking branch 'origin/levigft/obt-209-us-61-the-change…
levigtri Jul 5, 2026
245f7ad
Merge remote-tracking branch 'origin/levigft/obt-210-us-71-a-reusable…
levigtri Jul 5, 2026
5b6bc53
Merge remote-tracking branch 'origin/levigft/obt-211-us-72-the-filter…
levigtri Jul 5, 2026
e1b809e
Merge remote-tracking branch 'origin/levigft/obt-212-us-73-the-filter…
levigtri Jul 5, 2026
8669ddc
Merge remote-tracking branch 'origin/levigft/obt-213-us-81-see-the-ph…
levigtri Jul 5, 2026
1d7b893
Merge remote-tracking branch 'origin/levigft/obt-214-us-82-see-a-phas…
levigtri Jul 5, 2026
ef9f3de
Merge remote-tracking branch 'origin/levigft/obt-217-us-92-see-how-ma…
levigtri Jul 5, 2026
fb5c175
Merge remote-tracking branch 'origin/levigft/obt-218-us-93-see-a-summ…
levigtri Jul 5, 2026
3f712c5
Merge remote-tracking branch 'origin/levigft/obt-199-us-12-click-on-a…
levigtri Jul 5, 2026
422e06c
Merge remote-tracking branch 'origin/levigft/obt-202-us-23-see-a-conf…
levigtri Jul 5, 2026
1f8e3e9
Merge remote-tracking branch 'origin/levigft/obt-203-us-31-create-a-n…
levigtri Jul 5, 2026
a844700
chore(local): integrate US-12.8, US-2.3, US-1.2 refinements and US-13…
levigtri Jul 9, 2026
87e3d5d
feat(change-requests): manager requests view, admin toggle, new langu…
levigtri Jul 9, 2026
0bc7591
feat(change-requests): scope manager "My Requests" per page like the …
levigtri Jul 9, 2026
01b7c67
feat(dashboard): count pending change requests in Platform Overview
levigtri Jul 10, 2026
c8680ff
refactor(ui): use bordered variants for text action buttons
levigtri Jul 10, 2026
abca3f3
feat(users): show role tag (member/manager/admin) on user cards
levigtri Jul 10, 2026
458fe18
feat(users): add role types, badge variants and updateRole API method
levigtri Jul 10, 2026
030b4f8
feat(users): manage user role from the user detail page
levigtri Jul 10, 2026
6990f9e
chore(local): integrate US-14.1 (bordered action buttons)
levigtri Jul 10, 2026
5f52a51
chore(local): integrate US-14.3 (role tags on user cards)
levigtri Jul 10, 2026
1d3bfd2
chore(local): integrate US-14.4 (role & status management on user det…
levigtri Jul 10, 2026
06071d1
feat(api): add public request types and publicAPI namespace
levigtri Jul 10, 2026
21b45bf
feat(request): add public request page with reCAPTCHA
levigtri Jul 10, 2026
5456574
feat(login): link to the public request page
levigtri Jul 10, 2026
bfbb200
feat(request): make reCAPTCHA optional and handle rate limiting
levigtri Jul 10, 2026
2955cc4
Merge branch 'levigft/obt-250-us-152-frontend-public-request-page' in…
levigtri Jul 10, 2026
001332d
feat(request): searchable language combobox with new-language proposal
levigtri Jul 10, 2026
78399b8
fix(request): harden the public page against review findings
levigtri Jul 10, 2026
6473764
Merge branch 'levigft/obt-250-us-152-frontend-public-request-page' in…
levigtri Jul 10, 2026
4d901b6
feat(user-picker): add excludePlatformAdmins option to UserSearchPicker
levigtri Jul 13, 2026
d932479
feat(project-access): exclude platform admins from the Grant User picker
levigtri Jul 13, 2026
236da27
Merge branch 'levigft/obt-253-us-162-exclude-platform-admins-from-the…
levigtri Jul 13, 2026
d379f98
fix(auth): commit session state atomically to stop Access Denied flash
levigtri Jul 13, 2026
83c89e2
merge: US 17.1 / OBT-254 — atomic auth session (no Access Denied flash)
levigtri Jul 13, 2026
cb58640
feat(phases): restrict the phase catalog to platform admins
levigtri Jul 13, 2026
7a26f9c
refactor(phases): share the PhaseStatus type across the console
levigtri Jul 13, 2026
372369b
feat(phases): drop attach/detach now that phases are global
levigtri Jul 13, 2026
38037d9
merge: US-18.2 console phases admin-only
levigtri Jul 13, 2026
043129c
feat(design): shema semantic design tokens, brand fonts and assets
levigtri Jul 16, 2026
78d7383
feat(ui): reskin primitives and style constants to the redesign
levigtri Jul 16, 2026
bba93ae
feat(shell): dark sidebar rail, mobile header and themed toaster
levigtri Jul 16, 2026
b5024db
feat(common): reskin shared widgets to the new tokens
levigtri Jul 16, 2026
3c9be86
feat(pages): reimplement all console screens on the new design
levigtri Jul 16, 2026
56f4f90
docs: sync CLAUDE.md with the audited code and the redesign
levigtri Jul 16, 2026
991d9f4
feat(requests): platform-admin-only language deactivation and public …
levigtri Jul 16, 2026
b056f45
chore: untrack the working notes committed by mistake
levigtri Jul 17, 2026
af71187
feat(common): avatar palette, solid role badges and pending-count store
levigtri Jul 17, 2026
a149e78
feat(shell): pending-request badges on the sidebar nav
levigtri Jul 17, 2026
3283c49
feat(dashboard): stat cards, needs-review panel and a real map preview
levigtri Jul 17, 2026
9cd51a2
feat(projects): phase progress, member avatars and a project image
levigtri Jul 17, 2026
9a7ce2f
feat(pages): role pills, user cards and app tiles per the design
levigtri Jul 17, 2026
c978612
fix(users): upload admin-set avatars to the avatars folder
levigtri Jul 17, 2026
9823ce9
fix(shell): label the Main nav item Dashboard for platform admins
levigtri Jul 22, 2026
fd0be4f
feat(languages): reactivate inactive languages and darken their rows
levigtri Jul 22, 2026
d6dc1d3
fix(projects,apps): show a labeled upload/change affordance for image…
levigtri Jul 22, 2026
4898334
feat(map): restyle the project popup to the Shema card design
levigtri Jul 22, 2026
f6b568a
feat(shell): collapsible desktop icon rail for the sidebar
levigtri Jul 22, 2026
80381c0
fix(public-request): keep the top nav fixed on scroll
levigtri Jul 22, 2026
3dd8483
feat(profile): larger avatar and keep the dialog above the map
levigtri Jul 22, 2026
914a8fa
fix(map): color the popup phase chips by phase status
levigtri Jul 23, 2026
d49405b
fix(languages): show project usage and block deactivating a language …
levigtri Jul 23, 2026
2cd2a70
fix(languages): let a platform admin deactivate a language after seei…
levigtri Jul 23, 2026
7b3d0bb
feat(ui): render the console at a 1.25x scale by default
levigtri Jul 23, 2026
327b3b4
feat(phases): scale the dependency graph with the console UI scale
levigtri Jul 23, 2026
987ae90
feat(ui): raise the console UI scale to 1.375x
levigtri Jul 23, 2026
683b301
feat(profile): match the profile dialog to the design mock
levigtri Jul 23, 2026
f9dc0a2
feat(profile): show name initials and drop the avatar hover overlay
levigtri Jul 23, 2026
5a6f536
feat(profile): ring the avatar on hover like the rest of the console
levigtri Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@
# Set to "local" to proxy to http://host.docker.internal:8000 (no GCP auth needed).
# Set to "remote" to fetch the production URL from GCP Secret Manager.
BACKEND_TARGET=local

# Google reCAPTCHA v2 site key for the public request page (/request).
# Leave unset to disable the captcha (the API still rate-limits by IP).
# For dev testing of the captcha flow, use Google's test key:
# 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI (always passes).
VITE_RECAPTCHA_SITE_KEY=
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ dist-ssr

# Local environment overrides (copy from .env.example)
.env

pr-us*.md
decisoes-front.md
266 changes: 162 additions & 104 deletions CLAUDE.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export default defineConfig([
ecmaVersion: 2020,
globals: globals.browser,
},
rules: {
'no-empty': ['error', { allowEmptyCatch: true }],
},
},
{
files: ['src/components/ui/**/*.{ts,tsx}'],
Expand Down
8 changes: 7 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/assets/icon-dark.svg" />
<link rel="icon" type="image/svg+xml" href="/assets/icon-telha.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,400;0,500;0,600;0,700;0,900;1,400&family=Merriweather:ital,wght@0,300;0,400;0,700;1,400&display=swap"
rel="stylesheet"
/>
<title>Tripod Console</title>
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions public/assets/icon-branco.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/assets/icon-telha.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/assets/logo-branco.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/assets/logo-verde.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 14 additions & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"
import { Toaster } from "sonner"
import { ThemeProvider } from "@/contexts/ThemeContext"
import { ThemeProvider, useTheme } from "@/contexts/ThemeContext"
import { AuthProvider, useAuth } from "@/contexts/AuthContext"
import AppShell from "@/components/layout/AppShell"
import LoginPage from "@/components/pages/LoginPage"
import PublicRequestPage from "@/components/pages/publicRequest"
import DashboardPage from "@/components/pages/DashboardPage"
import LanguagesPage from "@/components/pages/LanguagesPage"
import OrganizationsPage from "@/components/pages/OrganizationsPage"
import OrganizationDetailPage from "@/components/pages/OrganizationDetailPage"
import ProjectsPage from "@/components/pages/ProjectsPage"
import ProjectDetailPage from "@/components/pages/ProjectDetailPage"
import UsersPage from "@/components/pages/UsersPage"
import UserDetailPage from "@/components/pages/UserDetailPage"
import UserDetailPage from "@/components/pages/userDetail"
import AppsPage from "@/components/pages/AppsPage"
import AppDetailPage from "@/components/pages/AppDetailPage"
import PhasesPage from "@/components/pages/PhasesPage"
import MapPage from "@/components/pages/MapPage"
import NotFoundPage from "@/components/pages/NotFoundPage"
import AccessDeniedPage from "@/components/pages/AccessDeniedPage"
import { LoadingSpinner } from "@/components/common/LoadingSpinner"

function ThemedToaster() {
const { resolvedTheme } = useTheme()
return <Toaster position="top-right" richColors theme={resolvedTheme} />
}

function AdminRoute({ children }: { children: React.ReactNode }) {
const { isPlatformAdmin } = useAuth()
const { isPlatformAdmin, isLoading } = useAuth()
if (isLoading) return <LoadingSpinner size="lg" />
if (!isPlatformAdmin) return <AccessDeniedPage />
return <>{children}</>
}
Expand All @@ -32,28 +38,27 @@ function App() {
<AuthProvider>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/request" element={<PublicRequestPage />} />

<Route path="/" element={<Navigate to="/app/dashboard" replace />} />

<Route path="/app" element={<AppShell />}>
<Route index element={<Navigate to="/app/dashboard" replace />} />
<Route path="dashboard" element={<DashboardPage />} />
<Route path="languages" element={<LanguagesPage />} />
<Route path="organizations" element={<OrganizationsPage />} />
<Route path="organizations/:orgId" element={<OrganizationDetailPage />} />
<Route path="projects" element={<ProjectsPage />} />
<Route path="projects/:projectId" element={<ProjectDetailPage />} />
<Route path="users" element={<AdminRoute><UsersPage /></AdminRoute>} />
<Route path="users/:userId" element={<AdminRoute><UserDetailPage /></AdminRoute>} />
<Route path="apps" element={<AdminRoute><AppsPage /></AdminRoute>} />
<Route path="apps/:appId" element={<AdminRoute><AppDetailPage /></AdminRoute>} />
<Route path="phases" element={<PhasesPage />} />
<Route path="phases" element={<AdminRoute><PhasesPage /></AdminRoute>} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align phase routing with the manager-editable contract.

AdminRoute rejects every non-platform-admin, while PhasesPage says managers may edit the global phase catalog. Use an authorization wrapper accepting isPlatformAdmin || isManager, or update the documented requirement and page copy if phases are intentionally admin-only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/App.tsx` at line 55, Update the phases route in App.tsx to use an
authorization wrapper that permits users when isPlatformAdmin or isManager is
true, preserving platform-admin access while allowing managers to edit the
global phase catalog; only change the page documentation/copy instead if the
intended contract is admin-only.

<Route path="map" element={<MapPage />} />
</Route>

<Route path="*" element={<NotFoundPage />} />
</Routes>
<Toaster position="top-right" richColors />
<ThemedToaster />
</AuthProvider>
</BrowserRouter>
</ThemeProvider>
Expand Down
8 changes: 4 additions & 4 deletions src/components/common/EmptyState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export function EmptyState({
}: EmptyStateProps) {
return (
<div className={cn(states.empty, className)}>
<div className="rounded-2xl bg-surface-alt/60 p-5 mb-5">
<Icon className="h-10 w-10 text-areia" />
<div className="rounded-[1rem] bg-muted p-5 mb-5">
<Icon className="h-9 w-9 text-fg-subtle" strokeWidth={1.75} />
</div>
<h3 className="text-lg font-semibold text-preto mb-2">{title}</h3>
<p className="text-sm text-verde/70 max-w-sm mb-6 text-center leading-relaxed">{description}</p>
<h3 className="text-[1.0625rem] font-semibold text-fg-strong mb-2">{title}</h3>
<p className="text-sm text-fg-muted max-w-sm mb-6 text-center leading-relaxed">{description}</p>
{actionLabel && onAction && (
<Button onClick={onAction}>{actionLabel}</Button>
)}
Expand Down
6 changes: 3 additions & 3 deletions src/components/common/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt

return (
<div className="flex flex-col items-center justify-center py-16 text-center">
<AlertTriangle className="h-12 w-12 text-telha mb-4" />
<h3 className="text-lg font-semibold text-preto mb-2">
<AlertTriangle className="h-11 w-11 text-accent mb-4" strokeWidth={1.75} />
<h3 className="text-lg font-semibold text-fg-strong mb-2">
Something went wrong
</h3>
<p className="text-sm text-verde max-w-md mb-6">
<p className="text-sm text-fg-muted max-w-md mb-6">
{this.state.error?.message || "An unexpected error occurred."}
</p>
<Button
Expand Down
12 changes: 6 additions & 6 deletions src/components/common/FeatureSpotlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ export function FeatureSpotlight({
return (
<Popover open>
<PopoverTrigger asChild>
<div className="relative inline-block ring-2 ring-telha/30 animate-pulse rounded-lg">
<div className="relative inline-block ring-2 ring-accent/30 animate-pulse rounded-full">
{children}
</div>
</PopoverTrigger>
<PopoverContent side={side} className="w-72">
<div className="space-y-2">
<h4 className="text-sm font-semibold text-preto">{title}</h4>
<p className="text-sm text-verde">{description}</p>
<PopoverContent side={side} className="w-[16.875rem] bg-inverse text-on-dark">
<div className="flex flex-col gap-2">
<h4 className="text-[0.8125rem] font-semibold text-on-dark">{title}</h4>
<p className="text-xs text-on-dark/80 leading-relaxed">{description}</p>
<Button
size="sm"
variant="outline"
className="self-start"
onClick={() => dismiss(featureKey)}
>
Got it
Expand Down
Loading
Loading