feat(frontend): add database management surface
This commit is contained in:
@@ -15,6 +15,7 @@ import { DeleteConfirmDialog } from "./DeleteConfirmDialog";
|
||||
import { StopConfirmDialog } from "./StopConfirmDialog";
|
||||
import { SteerInput, ComposerFooter } from "./SteerInput";
|
||||
import { WorkflowBar } from "./WorkflowBar";
|
||||
import { DatabaseManagementPage } from "./DatabaseManagementPage";
|
||||
import { Pencil, ArrowLeft, ArrowRight, Trash2 } from "lucide-react";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { Checkbox } from "../components/ui/checkbox";
|
||||
@@ -39,6 +40,8 @@ interface AppShellProps {
|
||||
canLogout: boolean;
|
||||
}
|
||||
|
||||
type ActiveSurface = "core" | "database-management";
|
||||
|
||||
export function AppShell({ canLogout }: AppShellProps) {
|
||||
const authenticatedUser = useAuthUser();
|
||||
const [panelSession, setPanelSession] = useState<SessionSummary | null>(null);
|
||||
@@ -118,6 +121,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const [showActivity, setShowActivity] = useState(false);
|
||||
const [activeSurface, setActiveSurface] = useState<ActiveSurface>("core");
|
||||
const [workspaceManagerOpen, setWorkspaceManagerOpen] = useState(false);
|
||||
const [piManagementOpen, setPiManagementOpen] = useState(false);
|
||||
const [activeOpen, setActiveOpen] = useState(true);
|
||||
@@ -195,6 +199,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
function openPanel(id: string) {
|
||||
const s = sessions.find((x) => x.id === id);
|
||||
if (!s) return;
|
||||
setActiveSurface("core");
|
||||
// A session with a live Pi runtime opens straight into its live view: doResume
|
||||
// reconnects to the already-active runtime and replays its pending gate, so an
|
||||
// in-progress session never shows an empty screen that reads as "stopped". Cold or
|
||||
@@ -214,6 +219,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
});
|
||||
}
|
||||
async function doResume(id: string) {
|
||||
setActiveSurface("core");
|
||||
const guard = captureAuthOperation({ sessionId: id, disposalEpoch: operationEpochRef.current });
|
||||
if (!guard) return;
|
||||
const token = ++resumeInvocationRef.current;
|
||||
@@ -495,6 +501,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
}, [lastSystemEvent]);
|
||||
|
||||
function startNewSession() {
|
||||
setActiveSurface("core");
|
||||
invalidateResumeIntent();
|
||||
newSessionOperationRef.current = null;
|
||||
resetSession();
|
||||
@@ -580,9 +587,10 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
onClose={() => setPanelSession(null)}
|
||||
onResume={doResume}
|
||||
desktopSplit={sessionDesktopSplit}
|
||||
hidden={activeSurface !== "core"}
|
||||
/>
|
||||
)}
|
||||
{panelSession && sessionDesktopSplit && (
|
||||
{activeSurface === "core" && panelSession && sessionDesktopSplit && (
|
||||
<div className="relative w-0 shrink-0">
|
||||
<div
|
||||
{...sessionSeparatorProps}
|
||||
@@ -594,8 +602,8 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{showActivity && <ModelActivityPanel desktopSplit={desktopSplit} onClose={() => setShowActivity(false)} onOpenWorkspaceManager={() => setWorkspaceManagerOpen(true)} />}
|
||||
{showActivity && desktopSplit && (
|
||||
{showActivity && <ModelActivityPanel desktopSplit={desktopSplit} onClose={() => setShowActivity(false)} onOpenWorkspaceManager={() => setWorkspaceManagerOpen(true)} hidden={activeSurface !== "core"} />}
|
||||
{activeSurface === "core" && showActivity && desktopSplit && (
|
||||
<div className="relative w-0 shrink-0">
|
||||
<div
|
||||
{...activitySeparatorProps}
|
||||
@@ -610,6 +618,11 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
|
||||
{/* Conversation column */}
|
||||
<div data-testid="conversation-column" className="flex min-w-0 flex-1 flex-col">
|
||||
{activeSurface === "database-management" && <DatabaseManagementPage />}
|
||||
<div
|
||||
aria-hidden={activeSurface !== "core"}
|
||||
className={activeSurface === "core" ? "contents" : "hidden"}
|
||||
>
|
||||
{activeSessionId && (
|
||||
<div className="relative shrink-0 border-b border-border/70 bg-background/80 px-6 py-2.5 backdrop-blur">
|
||||
{/* Arrow toggle for the left Model-activity panel: → opens it, ← closes it. */}
|
||||
@@ -686,6 +699,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right session rail */}
|
||||
@@ -731,6 +745,22 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
>
|
||||
Workspace management
|
||||
</Button>
|
||||
{canManageWorkspace && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={[
|
||||
"w-full justify-start hover:bg-sidebar-accent",
|
||||
activeSurface === "database-management"
|
||||
? "bg-sidebar-accent font-bold text-sidebar-accent-foreground"
|
||||
: "",
|
||||
].join(" ")}
|
||||
aria-current={activeSurface === "database-management" ? "page" : undefined}
|
||||
onClick={() => setActiveSurface("database-management")}
|
||||
>
|
||||
Database management
|
||||
</Button>
|
||||
)}
|
||||
{canManagePi && (
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
Reference in New Issue
Block a user