Add configuration tabs to project and agent pages
This commit is contained in:
@@ -16,15 +16,13 @@ import { InlineEditor } from "../components/InlineEditor";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
import { IssuesList } from "../components/IssuesList";
|
||||
import { PageSkeleton } from "../components/PageSkeleton";
|
||||
import { PageTabBar } from "../components/PageTabBar";
|
||||
import { projectRouteRef, cn } from "../lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { SlidersHorizontal } from "lucide-react";
|
||||
import { Tabs } from "@/components/ui/tabs";
|
||||
|
||||
/* ── Top-level tab types ── */
|
||||
|
||||
type ProjectTab = "overview" | "list";
|
||||
type ProjectTab = "overview" | "list" | "configuration";
|
||||
|
||||
function resolveProjectTab(pathname: string, projectId: string): ProjectTab | null {
|
||||
const segments = pathname.split("/").filter(Boolean);
|
||||
@@ -32,6 +30,7 @@ function resolveProjectTab(pathname: string, projectId: string): ProjectTab | nu
|
||||
if (projectsIdx === -1 || segments[projectsIdx + 1] !== projectId) return null;
|
||||
const tab = segments[projectsIdx + 2];
|
||||
if (tab === "overview") return "overview";
|
||||
if (tab === "configuration") return "configuration";
|
||||
if (tab === "issues") return "list";
|
||||
return null;
|
||||
}
|
||||
@@ -198,9 +197,8 @@ export function ProjectDetail() {
|
||||
filter?: string;
|
||||
}>();
|
||||
const { companies, selectedCompanyId, setSelectedCompanyId } = useCompany();
|
||||
const { openPanel, closePanel, panelVisible, setPanelVisible } = usePanel();
|
||||
const { closePanel } = usePanel();
|
||||
const { setBreadcrumbs } = useBreadcrumbs();
|
||||
const [mobilePropsOpen, setMobilePropsOpen] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -264,6 +262,10 @@ export function ProjectDetail() {
|
||||
navigate(`/projects/${canonicalProjectRef}/overview`, { replace: true });
|
||||
return;
|
||||
}
|
||||
if (activeTab === "configuration") {
|
||||
navigate(`/projects/${canonicalProjectRef}/configuration`, { replace: true });
|
||||
return;
|
||||
}
|
||||
if (activeTab === "list") {
|
||||
if (filter) {
|
||||
navigate(`/projects/${canonicalProjectRef}/issues/${filter}`, { replace: true });
|
||||
@@ -276,11 +278,9 @@ export function ProjectDetail() {
|
||||
}, [project, routeProjectRef, canonicalProjectRef, activeTab, filter, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (project) {
|
||||
openPanel(<ProjectProperties project={project} onUpdate={(data) => updateProject.mutate(data)} />);
|
||||
}
|
||||
closePanel();
|
||||
return () => closePanel();
|
||||
}, [project]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [closePanel]);
|
||||
|
||||
// Redirect bare /projects/:id to /projects/:id/issues
|
||||
if (routeProjectRef && activeTab === null) {
|
||||
@@ -294,6 +294,8 @@ export function ProjectDetail() {
|
||||
const handleTabChange = (tab: ProjectTab) => {
|
||||
if (tab === "overview") {
|
||||
navigate(`/projects/${canonicalProjectRef}/overview`);
|
||||
} else if (tab === "configuration") {
|
||||
navigate(`/projects/${canonicalProjectRef}/configuration`);
|
||||
} else {
|
||||
navigate(`/projects/${canonicalProjectRef}/issues`);
|
||||
}
|
||||
@@ -314,54 +316,20 @@ export function ProjectDetail() {
|
||||
as="h2"
|
||||
className="text-xl font-bold"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
className="ml-auto md:hidden shrink-0"
|
||||
onClick={() => setMobilePropsOpen(true)}
|
||||
title="Properties"
|
||||
>
|
||||
<SlidersHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
className={cn(
|
||||
"shrink-0 ml-auto transition-opacity duration-200 hidden md:flex",
|
||||
panelVisible ? "opacity-0 pointer-events-none w-0 overflow-hidden" : "opacity-100",
|
||||
)}
|
||||
onClick={() => setPanelVisible(true)}
|
||||
title="Show properties"
|
||||
>
|
||||
<SlidersHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Top-level project tabs */}
|
||||
<div className="flex items-center gap-1 border-b border-border">
|
||||
<button
|
||||
className={`px-3 py-2 text-sm font-medium transition-colors border-b-2 ${
|
||||
activeTab === "overview"
|
||||
? "border-foreground text-foreground"
|
||||
: "border-transparent text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
onClick={() => handleTabChange("overview")}
|
||||
>
|
||||
Overview
|
||||
</button>
|
||||
<button
|
||||
className={`px-3 py-2 text-sm font-medium transition-colors border-b-2 ${
|
||||
activeTab === "list"
|
||||
? "border-foreground text-foreground"
|
||||
: "border-transparent text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
onClick={() => handleTabChange("list")}
|
||||
>
|
||||
List
|
||||
</button>
|
||||
</div>
|
||||
<Tabs value={activeTab ?? "list"} onValueChange={(value) => handleTabChange(value as ProjectTab)}>
|
||||
<PageTabBar
|
||||
items={[
|
||||
{ value: "overview", label: "Overview" },
|
||||
{ value: "list", label: "List" },
|
||||
{ value: "configuration", label: "Configuration" },
|
||||
]}
|
||||
value={activeTab ?? "list"}
|
||||
onValueChange={(value) => handleTabChange(value as ProjectTab)}
|
||||
/>
|
||||
</Tabs>
|
||||
|
||||
{/* Tab content */}
|
||||
{activeTab === "overview" && (
|
||||
<OverviewContent
|
||||
project={project}
|
||||
@@ -377,19 +345,9 @@ export function ProjectDetail() {
|
||||
<ProjectIssuesList projectId={project.id} companyId={resolvedCompanyId} />
|
||||
)}
|
||||
|
||||
{/* Mobile properties drawer */}
|
||||
<Sheet open={mobilePropsOpen} onOpenChange={setMobilePropsOpen}>
|
||||
<SheetContent side="bottom" className="max-h-[85dvh] pb-[env(safe-area-inset-bottom)]">
|
||||
<SheetHeader>
|
||||
<SheetTitle className="text-sm">Properties</SheetTitle>
|
||||
</SheetHeader>
|
||||
<ScrollArea className="flex-1 overflow-y-auto">
|
||||
<div className="px-4 pb-4">
|
||||
<ProjectProperties project={project} onUpdate={(data) => updateProject.mutate(data)} />
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
{activeTab === "configuration" && (
|
||||
<ProjectProperties project={project} onUpdate={(data) => updateProject.mutate(data)} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user