project switch

This commit is contained in:
wass08
2026-03-10 12:13:04 +01:00
parent 433eaa2e02
commit 52bc82d6e1
+138 -14
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,23 +118,31 @@ 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 <div
className={cn( className={cn(
"flex h-full w-11 flex-col items-center gap-1 border-border/50 border-r py-2", "flex h-full w-11 flex-col items-center gap-1 border-border/50 border-r py-2",
className, className,
)} )}
> >
{/* Pascal logo - link to the home page */} {/* Pascal logo — app menu */}
<Tooltip> <Popover open={isMenuOpen} onOpenChange={setIsMenuOpen}>
<TooltipTrigger asChild> <PopoverTrigger asChild>
<Link <button
href="/" type="button"
className="flex h-9 w-9 items-center justify-center rounded-lg transition-all hover:bg-accent" className="flex h-9 w-9 items-center justify-center rounded-lg transition-all hover:bg-accent"
> >
<Image <Image
@@ -59,10 +152,40 @@ export function IconRail({
height={24} height={24}
className="h-6 w-6 dark:invert" className="h-6 w-6 dark:invert"
/> />
</button>
</PopoverTrigger>
<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> </Link>
</TooltipTrigger> <button
<TooltipContent side="right">Back to Pascal Editor</TooltipContent> type="button"
</Tooltip> 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" />
@@ -75,9 +198,7 @@ export function IconRail({
<button <button
className={cn( className={cn(
"flex h-9 w-9 items-center justify-center rounded-lg transition-all", "flex h-9 w-9 items-center justify-center rounded-lg transition-all",
isActive isActive ? "bg-accent" : "hover:bg-accent",
? "bg-accent"
: "hover:bg-accent",
)} )}
onClick={() => onPanelChange(panel.id)} onClick={() => onPanelChange(panel.id)}
type="button" type="button"
@@ -106,16 +227,19 @@ export function IconRail({
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <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" 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')} onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
type="button" type="button"
> >
{theme === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />} {theme === "dark" ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="right">Toggle theme</TooltipContent> <TooltipContent side="right">Toggle theme</TooltipContent>
</Tooltip> </Tooltip>
)} )}
</div> </div>
<OpenProjectModal open={isOpenProjectOpen} onOpenChange={setIsSwitchProjectOpen} />
</>
); );
} }