Polish UI: enhance dialogs, command palette, and page layouts
Expand NewIssueDialog with richer form fields. Add NewProjectDialog. Enhance CommandPalette with more actions and search. Improve CompanySwitcher, EmptyState, and IssueProperties. Flesh out Activity, Companies, Dashboard, and Inbox pages with real content and layouts. Refine sidebar, routing, and dialog context. CSS tweaks for dark theme. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useCompany } from "../context/CompanyContext";
|
||||
import { useDialog } from "../context/DialogContext";
|
||||
import { issuesApi } from "../api/issues";
|
||||
import { agentsApi } from "../api/agents";
|
||||
import { projectsApi } from "../api/projects";
|
||||
@@ -11,8 +12,21 @@ import {
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandSeparator,
|
||||
} from "@/components/ui/command";
|
||||
import { CircleDot, Bot, Hexagon, Target, LayoutDashboard, Inbox } from "lucide-react";
|
||||
import {
|
||||
CircleDot,
|
||||
Bot,
|
||||
Hexagon,
|
||||
Target,
|
||||
LayoutDashboard,
|
||||
Inbox,
|
||||
DollarSign,
|
||||
History,
|
||||
GitBranch,
|
||||
SquarePen,
|
||||
Plus,
|
||||
} from "lucide-react";
|
||||
import type { Issue, Agent, Project } from "@paperclip/shared";
|
||||
|
||||
export function CommandPalette() {
|
||||
@@ -22,6 +36,7 @@ export function CommandPalette() {
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
const navigate = useNavigate();
|
||||
const { selectedCompanyId } = useCompany();
|
||||
const { openNewIssue } = useDialog();
|
||||
|
||||
useEffect(() => {
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
@@ -57,6 +72,11 @@ export function CommandPalette() {
|
||||
navigate(path);
|
||||
}
|
||||
|
||||
const agentName = (id: string | null) => {
|
||||
if (!id) return null;
|
||||
return agents.find((a) => a.id === id)?.name ?? null;
|
||||
};
|
||||
|
||||
return (
|
||||
<CommandDialog open={open} onOpenChange={setOpen}>
|
||||
<CommandInput placeholder="Search issues, agents, projects..." />
|
||||
@@ -64,7 +84,7 @@ export function CommandPalette() {
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
|
||||
<CommandGroup heading="Pages">
|
||||
<CommandItem onSelect={() => go("/")}>
|
||||
<CommandItem onSelect={() => go("/dashboard")}>
|
||||
<LayoutDashboard className="mr-2 h-4 w-4" />
|
||||
Dashboard
|
||||
</CommandItem>
|
||||
@@ -72,7 +92,7 @@ export function CommandPalette() {
|
||||
<Inbox className="mr-2 h-4 w-4" />
|
||||
Inbox
|
||||
</CommandItem>
|
||||
<CommandItem onSelect={() => go("/tasks")}>
|
||||
<CommandItem onSelect={() => go("/issues")}>
|
||||
<CircleDot className="mr-2 h-4 w-4" />
|
||||
Issues
|
||||
</CommandItem>
|
||||
@@ -88,42 +108,92 @@ export function CommandPalette() {
|
||||
<Bot className="mr-2 h-4 w-4" />
|
||||
Agents
|
||||
</CommandItem>
|
||||
<CommandItem onSelect={() => go("/costs")}>
|
||||
<DollarSign className="mr-2 h-4 w-4" />
|
||||
Costs
|
||||
</CommandItem>
|
||||
<CommandItem onSelect={() => go("/activity")}>
|
||||
<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 />
|
||||
|
||||
<CommandGroup heading="Actions">
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
setOpen(false);
|
||||
openNewIssue();
|
||||
}}
|
||||
>
|
||||
<SquarePen className="mr-2 h-4 w-4" />
|
||||
Create new issue
|
||||
<span className="ml-auto text-xs text-muted-foreground">C</span>
|
||||
</CommandItem>
|
||||
<CommandItem onSelect={() => go("/agents")}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
Create new agent
|
||||
</CommandItem>
|
||||
<CommandItem onSelect={() => go("/projects")}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
Create new project
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
|
||||
{issues.length > 0 && (
|
||||
<CommandGroup heading="Issues">
|
||||
{issues.slice(0, 10).map((issue) => (
|
||||
<CommandItem key={issue.id} onSelect={() => go(`/issues/${issue.id}`)}>
|
||||
<CircleDot className="mr-2 h-4 w-4" />
|
||||
<span className="text-muted-foreground mr-2 font-mono text-xs">
|
||||
{issue.id.slice(0, 8)}
|
||||
</span>
|
||||
{issue.title}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
<>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Issues">
|
||||
{issues.slice(0, 10).map((issue) => (
|
||||
<CommandItem key={issue.id} onSelect={() => go(`/issues/${issue.id}`)}>
|
||||
<CircleDot className="mr-2 h-4 w-4" />
|
||||
<span className="text-muted-foreground mr-2 font-mono text-xs">
|
||||
{issue.id.slice(0, 8)}
|
||||
</span>
|
||||
<span className="flex-1 truncate">{issue.title}</span>
|
||||
{issue.assigneeAgentId && (
|
||||
<span className="text-xs text-muted-foreground ml-2">
|
||||
{agentName(issue.assigneeAgentId)}
|
||||
</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
{agents.length > 0 && (
|
||||
<CommandGroup heading="Agents">
|
||||
{agents.slice(0, 10).map((agent) => (
|
||||
<CommandItem key={agent.id} onSelect={() => go(`/agents/${agent.id}`)}>
|
||||
<Bot className="mr-2 h-4 w-4" />
|
||||
{agent.name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
<>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Agents">
|
||||
{agents.slice(0, 10).map((agent) => (
|
||||
<CommandItem key={agent.id} onSelect={() => go(`/agents/${agent.id}`)}>
|
||||
<Bot className="mr-2 h-4 w-4" />
|
||||
{agent.name}
|
||||
<span className="text-xs text-muted-foreground ml-2">{agent.role}</span>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
{projects.length > 0 && (
|
||||
<CommandGroup heading="Projects">
|
||||
{projects.slice(0, 10).map((project) => (
|
||||
<CommandItem key={project.id} onSelect={() => go(`/projects/${project.id}`)}>
|
||||
<Hexagon className="mr-2 h-4 w-4" />
|
||||
{project.name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
<>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Projects">
|
||||
{projects.slice(0, 10).map((project) => (
|
||||
<CommandItem key={project.id} onSelect={() => go(`/projects/${project.id}`)}>
|
||||
<Hexagon className="mr-2 h-4 w-4" />
|
||||
{project.name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</>
|
||||
)}
|
||||
</CommandList>
|
||||
</CommandDialog>
|
||||
|
||||
Reference in New Issue
Block a user