UI polish: sidebar nav, Companies/Issues layout, assignee in issue defaults
- Sidebar: add Approvals and Companies links, remove temporary Design Guide link - Companies: replace inline create form with 'New Company' button (opens onboarding) - Issues: move filter tabs inline with page heading (consistent with Agents) - DialogContext: add assigneeAgentId to NewIssueDefaults - NewIssueDialog: wire assigneeAgentId default (pre-select assignee when opening from agent detail page) - CommandPalette: minor cleanup - App: add DesignGuide route (dev-only, no sidebar link) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,6 @@ import { Routes, Route, Navigate } from "react-router-dom";
|
||||
import { Layout } from "./components/Layout";
|
||||
import { Dashboard } from "./pages/Dashboard";
|
||||
import { Companies } from "./pages/Companies";
|
||||
import { Org } from "./pages/Org";
|
||||
import { Agents } from "./pages/Agents";
|
||||
import { AgentDetail } from "./pages/AgentDetail";
|
||||
import { Projects } from "./pages/Projects";
|
||||
@@ -16,6 +15,7 @@ import { Costs } from "./pages/Costs";
|
||||
import { Activity } from "./pages/Activity";
|
||||
import { Inbox } from "./pages/Inbox";
|
||||
import { MyIssues } from "./pages/MyIssues";
|
||||
import { DesignGuide } from "./pages/DesignGuide";
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
@@ -24,7 +24,7 @@ export function App() {
|
||||
<Route index element={<Navigate to="/dashboard" replace />} />
|
||||
<Route path="dashboard" element={<Dashboard />} />
|
||||
<Route path="companies" element={<Companies />} />
|
||||
<Route path="org" element={<Org />} />
|
||||
<Route path="org" element={<Navigate to="/agents" replace />} />
|
||||
<Route path="agents" element={<Agents />} />
|
||||
<Route path="agents/:agentId" element={<AgentDetail />} />
|
||||
<Route path="projects" element={<Projects />} />
|
||||
@@ -38,6 +38,7 @@ export function App() {
|
||||
<Route path="activity" element={<Activity />} />
|
||||
<Route path="inbox" element={<Inbox />} />
|
||||
<Route path="my-issues" element={<MyIssues />} />
|
||||
<Route path="design-guide" element={<DesignGuide />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
);
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
Inbox,
|
||||
DollarSign,
|
||||
History,
|
||||
GitBranch,
|
||||
SquarePen,
|
||||
Plus,
|
||||
} from "lucide-react";
|
||||
@@ -114,10 +113,6 @@ export function CommandPalette() {
|
||||
<History className="mr-2 h-4 w-4" />
|
||||
Activity
|
||||
</CommandItem>
|
||||
<CommandItem onSelect={() => go("/org")}>
|
||||
<GitBranch className="mr-2 h-4 w-4" />
|
||||
Org Chart
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
|
||||
<CommandSeparator />
|
||||
|
||||
@@ -94,6 +94,7 @@ export function NewIssueDialog() {
|
||||
setStatus(newIssueDefaults.status ?? "todo");
|
||||
setPriority(newIssueDefaults.priority ?? "");
|
||||
setProjectId(newIssueDefaults.projectId ?? "");
|
||||
setAssigneeId(newIssueDefaults.assigneeAgentId ?? "");
|
||||
}
|
||||
}, [newIssueOpen, newIssueDefaults]);
|
||||
|
||||
|
||||
@@ -4,15 +4,14 @@ import {
|
||||
Hexagon,
|
||||
Target,
|
||||
LayoutDashboard,
|
||||
GitBranch,
|
||||
Bot,
|
||||
DollarSign,
|
||||
History,
|
||||
Search,
|
||||
SquarePen,
|
||||
Building2,
|
||||
ListTodo,
|
||||
LayoutList,
|
||||
ShieldCheck,
|
||||
Building2,
|
||||
} from "lucide-react";
|
||||
import { CompanySwitcher } from "./CompanySwitcher";
|
||||
import { SidebarSection } from "./SidebarSection";
|
||||
@@ -70,8 +69,8 @@ export function Sidebar() {
|
||||
|
||||
<SidebarSection label="Company">
|
||||
<SidebarNavItem to="/dashboard" label="Dashboard" icon={LayoutDashboard} />
|
||||
<SidebarNavItem to="/org" label="Org Chart" icon={GitBranch} />
|
||||
<SidebarNavItem to="/agents" label="Agents" icon={Bot} />
|
||||
<SidebarNavItem to="/approvals" label="Approvals" icon={ShieldCheck} />
|
||||
<SidebarNavItem to="/costs" label="Costs" icon={DollarSign} />
|
||||
<SidebarNavItem to="/activity" label="Activity" icon={History} />
|
||||
<SidebarNavItem to="/companies" label="Companies" icon={Building2} />
|
||||
|
||||
@@ -4,6 +4,7 @@ interface NewIssueDefaults {
|
||||
status?: string;
|
||||
priority?: string;
|
||||
projectId?: string;
|
||||
assigneeAgentId?: string;
|
||||
}
|
||||
|
||||
interface DialogContextValue {
|
||||
|
||||
@@ -1,31 +1,26 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useCompany } from "../context/CompanyContext";
|
||||
import { useDialog } from "../context/DialogContext";
|
||||
import { useBreadcrumbs } from "../context/BreadcrumbContext";
|
||||
import { companiesApi } from "../api/companies";
|
||||
import { queryKeys } from "../lib/queryKeys";
|
||||
import { formatCents } from "../lib/utils";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Pencil, Check, X } from "lucide-react";
|
||||
import { Pencil, Check, X, Plus } from "lucide-react";
|
||||
|
||||
export function Companies() {
|
||||
const {
|
||||
companies,
|
||||
selectedCompanyId,
|
||||
setSelectedCompanyId,
|
||||
createCompany,
|
||||
loading,
|
||||
error,
|
||||
} = useCompany();
|
||||
const { openOnboarding } = useDialog();
|
||||
const { setBreadcrumbs } = useBreadcrumbs();
|
||||
const queryClient = useQueryClient();
|
||||
const [name, setName] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [budget, setBudget] = useState("0");
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [submitError, setSubmitError] = useState<string | null>(null);
|
||||
|
||||
// Inline edit state
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
@@ -44,28 +39,6 @@ export function Companies() {
|
||||
setBreadcrumbs([{ label: "Companies" }]);
|
||||
}, [setBreadcrumbs]);
|
||||
|
||||
async function onSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (!name.trim()) return;
|
||||
|
||||
setSubmitting(true);
|
||||
setSubmitError(null);
|
||||
try {
|
||||
await createCompany({
|
||||
name: name.trim(),
|
||||
description: description.trim() || null,
|
||||
budgetMonthlyCents: Number(budget) || 0,
|
||||
});
|
||||
setName("");
|
||||
setDescription("");
|
||||
setBudget("0");
|
||||
} catch (err) {
|
||||
setSubmitError(err instanceof Error ? err.message : "Failed to create company");
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
function startEdit(companyId: string, currentName: string) {
|
||||
setEditingId(companyId);
|
||||
setEditName(currentName);
|
||||
@@ -83,39 +56,16 @@ export function Companies() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">Companies</h2>
|
||||
<p className="text-sm text-muted-foreground">Create and manage your companies.</p>
|
||||
<p className="text-sm text-muted-foreground">Manage your companies.</p>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-4 space-y-3">
|
||||
<h3 className="text-sm font-semibold">Create Company</h3>
|
||||
<form onSubmit={onSubmit} className="space-y-3">
|
||||
<div className="grid md:grid-cols-3 gap-3">
|
||||
<Input
|
||||
placeholder="Company name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Monthly budget (cents)"
|
||||
value={budget}
|
||||
onChange={(e) => setBudget(e.target.value.replace(/[^0-9]/g, ""))}
|
||||
/>
|
||||
</div>
|
||||
{submitError && <p className="text-sm text-destructive">{submitError}</p>}
|
||||
<Button type="submit" size="sm" disabled={submitting}>
|
||||
{submitting ? "Creating..." : "Create Company"}
|
||||
<Button size="sm" onClick={openOnboarding}>
|
||||
<Plus className="h-3.5 w-3.5 mr-1.5" />
|
||||
New Company
|
||||
</Button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="h-6">
|
||||
{loading && <p className="text-sm text-muted-foreground">Loading companies...</p>}
|
||||
|
||||
@@ -89,13 +89,8 @@ export function Issues() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<h2 className="text-lg font-semibold">Issues</h2>
|
||||
<Button size="sm" onClick={() => openNewIssue()}>
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
New Issue
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Tabs value={tab} onValueChange={(v) => setTab(v as TabFilter)}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="all">All Issues</TabsTrigger>
|
||||
@@ -104,6 +99,12 @@ export function Issues() {
|
||||
<TabsTrigger value="done">Done</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
<Button size="sm" onClick={() => openNewIssue()}>
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
New Issue
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}
|
||||
{error && <p className="text-sm text-destructive">{error.message}</p>}
|
||||
|
||||
Reference in New Issue
Block a user