Add budget tabs and sidebar budget indicators

This commit is contained in:
Dotta
2026-03-16 08:12:38 -05:00
parent 76e6cc08a6
commit 411952573e
7 changed files with 224 additions and 112 deletions

View File

@@ -26,7 +26,7 @@ import { PluginSlotMount, PluginSlotOutlet, usePluginSlots } from "@/plugins/slo
/* ── Top-level tab types ── */
type ProjectBaseTab = "overview" | "list" | "configuration";
type ProjectBaseTab = "overview" | "list" | "configuration" | "budget";
type ProjectPluginTab = `plugin:${string}`;
type ProjectTab = ProjectBaseTab | ProjectPluginTab;
@@ -41,6 +41,7 @@ function resolveProjectTab(pathname: string, projectId: string): ProjectTab | nu
const tab = segments[projectsIdx + 2];
if (tab === "overview") return "overview";
if (tab === "configuration") return "configuration";
if (tab === "budget") return "budget";
if (tab === "issues") return "list";
return null;
}
@@ -328,6 +329,10 @@ export function ProjectDetail() {
navigate(`/projects/${canonicalProjectRef}/configuration`, { replace: true });
return;
}
if (activeTab === "budget") {
navigate(`/projects/${canonicalProjectRef}/budget`, { replace: true });
return;
}
if (activeTab === "list") {
if (filter) {
navigate(`/projects/${canonicalProjectRef}/issues/${filter}`, { replace: true });
@@ -454,6 +459,8 @@ export function ProjectDetail() {
}
if (tab === "overview") {
navigate(`/projects/${canonicalProjectRef}/overview`);
} else if (tab === "budget") {
navigate(`/projects/${canonicalProjectRef}/budget`);
} else if (tab === "configuration") {
navigate(`/projects/${canonicalProjectRef}/configuration`);
} else {
@@ -470,12 +477,20 @@ export function ProjectDetail() {
onSelect={(color) => updateProject.mutate({ color })}
/>
</div>
<InlineEditor
value={project.name}
onSave={(name) => updateProject.mutate({ name })}
as="h2"
className="text-xl font-bold"
/>
<div className="min-w-0 space-y-2">
<InlineEditor
value={project.name}
onSave={(name) => updateProject.mutate({ name })}
as="h2"
className="text-xl font-bold"
/>
{project.pauseReason === "budget" ? (
<div className="inline-flex items-center gap-2 rounded-full border border-red-500/30 bg-red-500/10 px-3 py-1 text-[11px] font-medium uppercase tracking-[0.18em] text-red-200">
<span className="h-2 w-2 rounded-full bg-red-400" />
Paused by budget hard stop
</div>
) : null}
</div>
</div>
<PluginSlotOutlet
@@ -515,6 +530,7 @@ export function ProjectDetail() {
{ value: "overview", label: "Overview" },
{ value: "list", label: "List" },
{ value: "configuration", label: "Configuration" },
{ value: "budget", label: "Budget" },
...pluginTabItems.map((item) => ({
value: item.value,
label: item.label,
@@ -526,15 +542,6 @@ export function ProjectDetail() {
/>
</Tabs>
{resolvedCompanyId ? (
<BudgetPolicyCard
summary={projectBudgetSummary}
compact
isSaving={budgetMutation.isPending}
onSave={(amount) => budgetMutation.mutate(amount)}
/>
) : null}
{activeTab === "overview" && (
<OverviewContent
project={project}
@@ -563,6 +570,17 @@ export function ProjectDetail() {
</div>
)}
{activeTab === "budget" && resolvedCompanyId ? (
<div className="max-w-3xl">
<BudgetPolicyCard
summary={projectBudgetSummary}
variant="plain"
isSaving={budgetMutation.isPending}
onSave={(amount) => budgetMutation.mutate(amount)}
/>
</div>
) : null}
{activePluginTab && (
<PluginSlotMount
slot={activePluginTab.slot}