Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
agnlez committed Oct 17, 2024
1 parent 291c204 commit 2789cf6
Show file tree
Hide file tree
Showing 14 changed files with 629 additions and 26 deletions.
3 changes: 3 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
"@radix-ui/react-separator": "1.1.0",
"@radix-ui/react-slot": "1.1.0",
"@radix-ui/react-toast": "1.2.2",
"@radix-ui/react-tooltip": "1.1.3",
"@tanstack/react-query": "5.59.0",
"@ts-rest/react-query": "3.51.0",
"class-variance-authority": "0.7.0",
"clsx": "2.1.1",
"framer-motion": "11.11.9",
"jotai": "2.10.1",
"lucide-react": "0.447.0",
"next": "14.2.10",
"next-auth": "4.24.8",
Expand Down
4 changes: 4 additions & 0 deletions client/src/app/(projects)/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const LAYOUT_TRANSITIONS = {
duration: 0.4,
ease: "easeInOut",
};
18 changes: 18 additions & 0 deletions client/src/app/(projects)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client";

import { PropsWithChildren } from "react";

import { LayoutGroup } from "framer-motion";

import MainNav from "@/containers/nav";

export default function BlueCarbonCostLayout({ children }: PropsWithChildren) {
return (
<main className="flex h-dvh overflow-hidden">
<LayoutGroup>
<MainNav />
{children}
</LayoutGroup>
</main>
);
}
84 changes: 84 additions & 0 deletions client/src/app/(projects)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"use client";

import { motion } from "framer-motion";
import { useAtomValue } from "jotai";

import { LAYOUT_TRANSITIONS } from "@/app/(projects)/constants";
import { projectsUIState } from "@/app/(projects)/store";

import ProjectsFilters from "@/containers/projects/filters";
import ProjectsHeader from "@/containers/projects/header";
import ProjectsMap from "@/containers/projects/map";
import ProjectsTable from "@/containers/projects/table";

export default function Projects() {
const { navOpen, filtersOpen, mapExpanded, tableExpanded } =
useAtomValue(projectsUIState);

return (
<motion.div
layout
layoutDependency={navOpen}
className="flex flex-1"
transition={LAYOUT_TRANSITIONS}
>
<motion.aside
layout
initial={filtersOpen ? "open" : "closed"}
animate={filtersOpen ? "open" : "closed"}
variants={{
open: {
width: 450,
},
closed: {
width: 0,
},
}}
transition={LAYOUT_TRANSITIONS}
className="overflow-hidden"
>
<ProjectsFilters />
</motion.aside>
<div className="flex flex-1 flex-col">
<ProjectsHeader />
<div className="grid flex-grow grid-rows-2">
<motion.section
layout
// initial={mapExpanded ? "expanded" : "collapsed"}
initial={{
height: "auto",
}}
animate={mapExpanded ? "expanded" : "collapsed"}
variants={{
expanded: {
height: "100%",
},
collapsed: {
height: 0,
},
}}
transition={LAYOUT_TRANSITIONS}
>
<ProjectsMap />
</motion.section>
<motion.section
layout
initial={tableExpanded ? "expanded" : "collapsed"}
animate={tableExpanded ? "expanded" : "collapsed"}
variants={{
expanded: {
height: "100%",
},
collapsed: {
height: 0,
},
}}
transition={LAYOUT_TRANSITIONS}
>
<ProjectsTable />
</motion.section>
</div>
</div>
</motion.div>
);
}
14 changes: 14 additions & 0 deletions client/src/app/(projects)/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { atom } from "jotai";

export const mainNavOpenAtom = atom(false);
export const filtersProjectOpenAtom = atom(false);

export const mapExpandedAtom = atom(false);
export const tableExpandedAtom = atom(false);

export const projectsUIState = atom({
navOpen: false,
filtersOpen: false,
mapExpanded: false,
tableExpanded: false,
});
23 changes: 0 additions & 23 deletions client/src/app/page.tsx

This file was deleted.

10 changes: 7 additions & 3 deletions client/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { SessionProvider } from "next-auth/react";

import { makeQueryClient } from "@/lib/query-client";

import { TooltipProvider } from "@/components/ui/tooltip";

let browserQueryClient: QueryClient | undefined = undefined;

function getQueryClient() {
Expand All @@ -37,9 +39,11 @@ export default function LayoutProviders({
return (
<>
<SessionProvider session={session} basePath="/auth/api">
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
<TooltipProvider>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</TooltipProvider>
</SessionProvider>
</>
);
Expand Down
109 changes: 109 additions & 0 deletions client/src/containers/nav/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"use client";

import Link from "next/link";

import { motion } from "framer-motion";
import { useAtom } from "jotai";
import { ActivityIcon, ChevronRight, ChevronLeft } from "lucide-react";

import { LAYOUT_TRANSITIONS } from "@/app/(projects)/constants";
import { projectsUIState } from "@/app/(projects)/store";

import { MainNavItem } from "@/containers/nav/item";

export default function MainNav() {
const [{ navOpen }, setUIState] = useAtom(projectsUIState);

return (
<motion.nav
layout="size"
layoutDependency={navOpen}
transition={LAYOUT_TRANSITIONS}
className="pointer-events-auto relative z-10 h-full overflow-hidden"
>
<div className="relative z-20 flex h-full flex-col justify-between bg-white px-4 py-6">
<motion.div
layout="position"
className="divide-navy-100 flex flex-col divide-y"
>
<div className="flex flex-col items-start pb-3">
<Link href="/" className="flex flex-col items-center space-y-1">
<div className="relative flex h-10 w-10 flex-col items-center space-y-1">
<motion.span
initial={"initial"}
animate={navOpen ? "animate" : "initial"}
variants={{
initial: { opacity: 0 },
animate: { opacity: 1 },
}}
className="absolute -top-1 left-full block translate-x-0.5 text-[7px] font-bold uppercase leading-[8px] opacity-0"
>
Blue Carbon Cost
</motion.span>
</div>
</Link>
</div>

<MainNavItem href="/" label="High level projects" index={0}>
<ActivityIcon />
</MainNavItem>

<ul className="space-y-3 py-3">
<li>
<MainNavItem
href="/projects/new"
label="New custom projects"
index={1}
>
<ActivityIcon />
</MainNavItem>
</li>
<ul>
<li>
<MainNavItem
href="/projects/custom"
label="Custom projects"
index={2}
>
<ActivityIcon />
</MainNavItem>
</li>
</ul>
</ul>
</motion.div>
<motion.div
layout="position"
className="divide-navy-100 flex flex-col divide-y"
>
<ul className="space-y-3 py-3">
<MainNavItem href="/atlas/login" label="User profile" index={6}>
<ActivityIcon className="h-5 w-5" />
</MainNavItem>
</ul>

<ul className="space-y-3 py-3">
<MainNavItem label="Help center" href="/methodology" index={7}>
<ActivityIcon className="h-5 w-5" />
</MainNavItem>
</ul>

<ul className="space-y-3 py-3">
<MainNavItem
label={navOpen ? "Collapse side menu" : "Expand side menu"}
onClick={() =>
setUIState((prev) => ({
...prev,
navOpen: !prev.navOpen,
}))
}
index={8}
>
{!navOpen && <ChevronRight className="h-5 w-5" />}
{navOpen && <ChevronLeft className="h-5 w-5" />}
</MainNavItem>
</ul>
</motion.div>
</div>
</motion.nav>
);
}
100 changes: 100 additions & 0 deletions client/src/containers/nav/item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"use client";
import { ButtonHTMLAttributes } from "react";

import Link from "next/link";
import { usePathname } from "next/navigation";

import { TooltipPortal } from "@radix-ui/react-tooltip";
import { motion } from "framer-motion";
import { useAtomValue } from "jotai";

import { cn } from "@/lib/utils";

import { mainNavOpenAtom } from "@/app/(projects)/store";

import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";

export const MainNavItem = ({
index,
label,
href,
children,
...props
}: ButtonHTMLAttributes<HTMLButtonElement> & {
index: number;
label: string;
href?: string;
children: React.ReactNode;
}) => {
// const searchParams = useSyncSearchParams();

const navOpen = useAtomValue(mainNavOpenAtom);
// const [sidebarOpen, setSidebarOpen] = useAtom(sidebarOpenAtom);
const pathname = usePathname();

return (
<Tooltip delayDuration={100} open={navOpen ? false : undefined}>
{href && (
<TooltipTrigger asChild>
<Link
// href={`${href}${searchParams}`}
href={href}
prefetch
className={cn({
"flex items-center gap-3 rounded-sm p-2.5": true,
"text-navy-500 hover:bg-lightblue-50": pathname !== href,
"bg-lightblue-100 text-navy-700":
(href !== "/" && pathname.includes(href)) || pathname === href,
})}
// onClick={() => {
// const open = pathname !== href ? true : !sidebarOpen;
// setSidebarOpen(open);
// }}
>
{children}

{navOpen && (
<motion.span
className="text-nowrap text-sm leading-none"
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.2, delay: index * 0.05 }}
>
{label}
</motion.span>
)}
</Link>
</TooltipTrigger>
)}

{!href && (
<TooltipTrigger asChild>
<button
className="flex items-center gap-4 rounded-sm p-2.5"
{...props}
>
{children}

{navOpen && (
<motion.span
className="text-nowrap text-sm leading-none"
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.2, delay: index * 0.05 }}
>
{label}
</motion.span>
)}
</button>
</TooltipTrigger>
)}
<TooltipPortal>
<TooltipContent side="right">{label}</TooltipContent>
</TooltipPortal>
</Tooltip>
);
};
Loading

0 comments on commit 2789cf6

Please sign in to comment.