project switch

This commit is contained in:
wass08
2026-03-10 12:13:04 +01:00
parent 433eaa2e02
commit 52bc82d6e1
+192 -68
View File
@@ -1,16 +1,101 @@
"use client"; "use client";
import { Moon, Sun } from "lucide-react"; import { ArrowLeft, Command, FolderOpen, Moon, Search, Sun } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/primitives/tooltip"; } from "@/components/ui/primitives/tooltip";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/primitives/popover";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/primitives/dialog";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { useViewer } from "@pascal-app/viewer"; import { useViewer } from "@pascal-app/viewer";
import { useProjectStore } from "@/features/community/lib/projects/store";
function OpenProjectModal({
open,
onOpenChange,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
}) {
const router = useRouter();
const projects = useProjectStore((s) => s.projects);
const activeProject = useProjectStore((s) => s.activeProject);
const fetchProjects = useProjectStore((s) => s.fetchProjects);
useEffect(() => {
if (open && projects.length === 0) {
fetchProjects();
}
}, [open, projects.length, fetchProjects]);
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-sm p-0 gap-0 overflow-hidden">
<DialogHeader className="px-4 pt-4 pb-3 border-b border-border/50">
<DialogTitle className="text-sm font-medium">Open project</DialogTitle>
</DialogHeader>
<div className="max-h-80 overflow-y-auto p-1.5">
{projects.length === 0 ? (
<p className="px-3 py-6 text-sm text-muted-foreground text-center">
No projects found
</p>
) : (
projects.map((project) => {
const isActive = project.id === activeProject?.id;
return (
<button
key={project.id}
type="button"
className={cn(
"flex w-full items-center gap-3 rounded-md px-3 py-2 text-left transition-colors hover:bg-accent",
isActive && "bg-accent/50"
)}
onClick={() => {
onOpenChange(false);
router.push(`/editor/${project.id}`);
}}
>
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded bg-muted overflow-hidden">
{project.thumbnail_url ? (
<img
src={project.thumbnail_url}
alt=""
className="h-full w-full object-cover"
/>
) : (
<FolderOpen className="h-4 w-4 text-muted-foreground" />
)}
</div>
<p className="flex-1 min-w-0 truncate text-sm font-medium">
{project.name}
</p>
{isActive && (
<div className="h-1.5 w-1.5 shrink-0 rounded-full bg-primary" />
)}
</button>
);
})
)}
</div>
</DialogContent>
</Dialog>
);
}
export type PanelId = "site" | "settings"; export type PanelId = "site" | "settings";
@@ -33,89 +118,128 @@ export function IconRail({
const theme = useViewer((state) => state.theme); const theme = useViewer((state) => state.theme);
const setTheme = useViewer((state) => state.setTheme); const setTheme = useViewer((state) => state.setTheme);
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isOpenProjectOpen, setIsSwitchProjectOpen] = useState(false);
useEffect(() => { useEffect(() => {
setMounted(true); setMounted(true);
}, []); }, []);
const handleOpenProject = () => {
setIsMenuOpen(false);
setIsSwitchProjectOpen(true);
};
return ( return (
<div <>
className={cn( <div
"flex h-full w-11 flex-col items-center gap-1 border-border/50 border-r py-2", className={cn(
className, "flex h-full w-11 flex-col items-center gap-1 border-border/50 border-r py-2",
)} className,
> )}
{/* Pascal logo - link to the home page */} >
<Tooltip> {/* Pascal logo — app menu */}
<TooltipTrigger asChild> <Popover open={isMenuOpen} onOpenChange={setIsMenuOpen}>
<Link <PopoverTrigger asChild>
href="/" <button
className="flex h-9 w-9 items-center justify-center rounded-lg transition-all hover:bg-accent" type="button"
> className="flex h-9 w-9 items-center justify-center rounded-lg transition-all hover:bg-accent"
<Image >
src="/pascal-logo-shape.svg" <Image
alt="Pascal" src="/pascal-logo-shape.svg"
width={24} alt="Pascal"
height={24} width={24}
className="h-6 w-6 dark:invert" height={24}
/> className="h-6 w-6 dark:invert"
</Link> />
</TooltipTrigger> </button>
<TooltipContent side="right">Back to Pascal Editor</TooltipContent> </PopoverTrigger>
</Tooltip> <PopoverContent side="right" align="start" className="w-52 p-1" sideOffset={8}>
<Link
href="/"
className="flex items-center gap-2.5 rounded-md px-2.5 py-2 text-sm text-foreground transition-colors hover:bg-accent"
onClick={() => setIsMenuOpen(false)}
>
<ArrowLeft className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
Back to community
</Link>
<button
type="button"
className="flex w-full items-center gap-2.5 rounded-md px-2.5 py-2 text-sm text-foreground transition-colors hover:bg-accent"
onClick={handleOpenProject}
>
<FolderOpen className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
Open project
</button>
<div className="my-1 h-px bg-border/50" />
<button
type="button"
className="flex w-full items-center gap-2.5 rounded-md px-2.5 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
onClick={() => setIsMenuOpen(false)}
>
<Search className="h-3.5 w-3.5 shrink-0" />
<span className="flex-1 text-left">Actions...</span>
<span className="flex items-center gap-0.5 rounded border border-border/60 bg-muted/60 px-1 py-0.5 text-[10px] leading-none text-muted-foreground">
<Command className="h-2.5 w-2.5" />
K
</span>
</button>
</PopoverContent>
</Popover>
{/* Divider */} {/* Divider */}
<div className="w-8 h-px bg-border/50 mb-1" /> <div className="w-8 h-px bg-border/50 mb-1" />
{panels.map((panel) => { {panels.map((panel) => {
const isActive = activePanel === panel.id; const isActive = activePanel === panel.id;
return ( return (
<Tooltip key={panel.id}> <Tooltip key={panel.id}>
<TooltipTrigger asChild>
<button
className={cn(
"flex h-9 w-9 items-center justify-center rounded-lg transition-all",
isActive ? "bg-accent" : "hover:bg-accent",
)}
onClick={() => onPanelChange(panel.id)}
type="button"
>
<img
src={panel.iconSrc}
alt={panel.label}
className={cn(
"h-6 w-6 transition-all object-contain",
!isActive && "opacity-50 saturate-0"
)}
/>
</button>
</TooltipTrigger>
<TooltipContent side="right">{panel.label}</TooltipContent>
</Tooltip>
);
})}
{/* Spacer */}
<div className="flex-1" />
{/* Theme Toggle */}
{mounted && (
<Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
className={cn( className="flex h-9 w-9 items-center justify-center rounded-lg transition-all text-muted-foreground hover:bg-accent hover:text-accent-foreground mb-2"
"flex h-9 w-9 items-center justify-center rounded-lg transition-all", onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
isActive
? "bg-accent"
: "hover:bg-accent",
)}
onClick={() => onPanelChange(panel.id)}
type="button" type="button"
> >
<img {theme === "dark" ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
src={panel.iconSrc}
alt={panel.label}
className={cn(
"h-6 w-6 transition-all object-contain",
!isActive && "opacity-50 saturate-0"
)}
/>
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="right">{panel.label}</TooltipContent> <TooltipContent side="right">Toggle theme</TooltipContent>
</Tooltip> </Tooltip>
); )}
})} </div>
{/* Spacer */} <OpenProjectModal open={isOpenProjectOpen} onOpenChange={setIsSwitchProjectOpen} />
<div className="flex-1" /> </>
{/* Theme Toggle */}
{mounted && (
<Tooltip>
<TooltipTrigger asChild>
<button
className="flex h-9 w-9 items-center justify-center rounded-lg transition-all text-muted-foreground hover:bg-accent hover:text-accent-foreground mb-2"
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
type="button"
>
{theme === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
</button>
</TooltipTrigger>
<TooltipContent side="right">Toggle theme</TooltipContent>
</Tooltip>
)}
</div>
); );
} }