diff --git a/client/package.json b/client/package.json index 13260dce..a6f53303 100644 --- a/client/package.json +++ b/client/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev --turbo", "build": "next build", "start": "next start", "lint": "next lint" @@ -15,6 +15,8 @@ "@radix-ui/react-dialog": "1.1.2", "@radix-ui/react-icons": "1.3.0", "@radix-ui/react-label": "2.1.0", + "@radix-ui/react-popover": "1.1.2", + "@radix-ui/react-scroll-area": "1.2.0", "@radix-ui/react-select": "2.1.2", "@radix-ui/react-separator": "1.1.0", "@radix-ui/react-slot": "1.1.0", diff --git a/client/src/app/(projects)/page.tsx b/client/src/app/(projects)/page.tsx index 6f11e548..e5c61120 100644 --- a/client/src/app/(projects)/page.tsx +++ b/client/src/app/(projects)/page.tsx @@ -61,7 +61,7 @@ export default function Projects() { - + diff --git a/client/src/app/(projects)/store.ts b/client/src/app/(projects)/store.ts index 79ed845d..a16ecad8 100644 --- a/client/src/app/(projects)/store.ts +++ b/client/src/app/(projects)/store.ts @@ -9,9 +9,3 @@ export const projectsUIState = atom<{ mapExpanded: "default", tableExpanded: "default", }); - -export const projectsMapState = atom<{ - legendOpen: boolean; -}>({ - legendOpen: true, -}); diff --git a/client/src/app/fonts/GeistMonoVF.woff b/client/src/app/fonts/GeistMonoVF.woff deleted file mode 100644 index f2ae185c..00000000 Binary files a/client/src/app/fonts/GeistMonoVF.woff and /dev/null differ diff --git a/client/src/app/fonts/GeistVF.woff b/client/src/app/fonts/GeistVF.woff deleted file mode 100644 index 1b62daac..00000000 Binary files a/client/src/app/fonts/GeistVF.woff and /dev/null differ diff --git a/client/src/app/globals.css b/client/src/app/globals.css index 83897b93..9da53cda 100644 --- a/client/src/app/globals.css +++ b/client/src/app/globals.css @@ -17,73 +17,39 @@ body { @layer base { :root { - --background: 0 0% 100%; - --foreground: 0 0% 3.9%; - --card: 0 0% 100%; - --card-foreground: 0 0% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 0 0% 9%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 96.1%; - --secondary-foreground: 0 0% 9%; - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 0 0% 3.9%; + --background: 214, 58%, 14%; + --foreground: 213, 73%, 97%; + --card: 213, 58%, 18%; + --card-foreground: 213, 73%, 97%; + --popover: 213, 58%, 18%; + --popover-foreground: 213, 73%, 97%; + --primary: 192, 86%, 69%; + --primary-foreground: 213, 73%, 97%; + --secondary: 213, 73%, 97%; + --secondary-foreground: 213, 58%, 18%; + --muted: 210, 64%, 27%; + --muted-foreground: 211, 69%, 86%; + --accent: 192, 86%, 69%; + --accent-foreground: 212, 57%, 24%; + --destructive: 350, 89%, 60%; + --destructive-foreground: 0 0% 100%; + --border: 201, 59%, 24%; + --input: 213, 73%, 97%; + --ring: 212, 57%, 24%; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --radius: 0.5rem; - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-background: 214, 58%, 14%; + --sidebar-foreground: 213, 73%, 97%; --sidebar-primary: 240 5.9% 10%; --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-accent: 212, 57%, 24%; + --sidebar-accent-foreground: 192, 86%, 69%; --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } - .dark { - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 0 0% 9%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 0 0% 83.1%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; + --sidebar-ring: 212, 57%, 24%; } } @@ -92,6 +58,6 @@ body { @apply border-border; } body { - @apply bg-background text-foreground; + @apply bg-background text-foreground antialiased; } } diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx index 435b50c9..e9d00824 100644 --- a/client/src/app/layout.tsx +++ b/client/src/app/layout.tsx @@ -1,6 +1,6 @@ import { PropsWithChildren } from "react"; -import { Inter } from "next/font/google"; +import { Spline_Sans } from "next/font/google"; import type { Metadata } from "next"; import "@/app/globals.css"; @@ -13,7 +13,11 @@ import Toaster from "@/components/ui/toast/toaster"; import LayoutProviders from "./providers"; -const inter = Inter({ subsets: ["latin"] }); +const inter = Spline_Sans({ + subsets: ["latin"], + weight: ["300", "400", "500", "600", "700"], + variable: "--font-spline-sans", +}); export const metadata: Metadata = { title: "Blue Carbon Cost", diff --git a/client/src/components/map/constants.ts b/client/src/components/map/constants.ts index 46524536..5b22a3bd 100644 --- a/client/src/components/map/constants.ts +++ b/client/src/components/map/constants.ts @@ -6,4 +6,5 @@ export const DEFAULT_VIEW_STATE: Partial = { longitude: 0, }; -export const MAPBOX_STYLE = "mapbox://styles/mapbox/standard-satellite"; +export const MAPBOX_STYLE = + "mapbox://styles/vizzualitybluecarbon/cm37685r0000h01mfhl92a2p1"; diff --git a/client/src/components/map/index.tsx b/client/src/components/map/index.tsx index 50719832..c9154875 100644 --- a/client/src/components/map/index.tsx +++ b/client/src/components/map/index.tsx @@ -152,6 +152,8 @@ export default function Map({ onMove={handleMapMove} onLoad={handleMapLoad} mapStyle={MAPBOX_STYLE} + maxZoom={10} + minZoom={0} {...mapboxProps} {...localViewState} > diff --git a/client/src/components/ui/button.tsx b/client/src/components/ui/button.tsx index 0d7d377d..f3095781 100644 --- a/client/src/components/ui/button.tsx +++ b/client/src/components/ui/button.tsx @@ -6,26 +6,26 @@ import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { default: - "bg-primary text-primary-foreground shadow hover:bg-primary/90", + "bg-primary text-big-stone-900 shadow hover:bg-sky-blue-200 focus-visible:ring-sky-blue-300/40", + secondary: + "bg-secondary text-accent-foreground shadow-sm hover:bg-big-stone-200 focus-visible:ring-sky-blue-300/40", destructive: - "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + "bg-destructive text-destructive-foreground shadow-sm hover:bg-rose-700 focus-visible:ring-destructive", outline: - "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", - secondary: - "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", + "border border-border bg-background shadow-sm hover:bg-big-stone-900 focus-visible:ring-sky-blue-300/40", + link: "text-primary-foreground underline-offset-4 hover:underline focus-visible:ring-sky-blue-900", + ghost: "text-foreground hover:bg-big-stone-900", }, size: { default: "h-9 px-4 py-2", sm: "h-8 rounded-md px-3 text-xs", lg: "h-10 rounded-md px-8", - icon: "h-9 w-9", + icon: "h-10 w-10", }, }, defaultVariants: { diff --git a/client/src/components/ui/input.tsx b/client/src/components/ui/input.tsx index ef29a4ac..41241808 100644 --- a/client/src/components/ui/input.tsx +++ b/client/src/components/ui/input.tsx @@ -1,19 +1,35 @@ import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + import { cn } from "@/lib/utils"; +const inputVariants = cva( + "flex h-9 w-full rounded-full border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", + { + variants: { + variant: { + default: + "bg-big-stone-950 text-big-stone-900 shadow border-border focus-visible:border-primary focus-visible:ring-sky-blue-300/40", + ghost: "text-foreground border-0 focus-visible:ring-sky-blue-300/40", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + export interface InputProps - extends React.InputHTMLAttributes {} + extends React.InputHTMLAttributes, + VariantProps {} const Input = React.forwardRef( - ({ className, type, ...props }, ref) => { + ({ className, variant, type, ...props }, ref) => { return ( diff --git a/client/src/components/ui/pagination.tsx b/client/src/components/ui/pagination.tsx index 433bea64..e6a3efae 100644 --- a/client/src/components/ui/pagination.tsx +++ b/client/src/components/ui/pagination.tsx @@ -65,8 +65,8 @@ PaginationButton.displayName = "PaginationButton"; const PaginationFirst = ({ className, ...props }: ButtonProps) => ( @@ -77,8 +77,8 @@ PaginationFirst.displayName = "PaginationFirst"; const PaginationPrevious = ({ className, ...props }: ButtonProps) => ( @@ -92,8 +92,8 @@ const PaginationNext = ({ }: React.ComponentProps) => ( @@ -104,8 +104,8 @@ PaginationNext.displayName = "PaginationNext"; const PaginationLast = ({ className, ...props }: ButtonProps) => ( diff --git a/client/src/components/ui/popover.tsx b/client/src/components/ui/popover.tsx new file mode 100644 index 00000000..12608ada --- /dev/null +++ b/client/src/components/ui/popover.tsx @@ -0,0 +1,34 @@ +"use client"; + +import * as React from "react"; + +import * as PopoverPrimitive from "@radix-ui/react-popover"; + +import { cn } from "@/lib/utils"; + +const Popover = PopoverPrimitive.Root; + +const PopoverTrigger = PopoverPrimitive.Trigger; + +const PopoverAnchor = PopoverPrimitive.Anchor; + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; + +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }; diff --git a/client/src/components/ui/resizable.tsx b/client/src/components/ui/resizable.tsx index 9fc637a4..7d25552a 100644 --- a/client/src/components/ui/resizable.tsx +++ b/client/src/components/ui/resizable.tsx @@ -30,14 +30,14 @@ const ResizableHandle = ({ }) => ( div]:rotate-90", + "relative flex w-px items-center justify-center bg-transparent after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-0 data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90", className, )} {...props} > {withHandle && ( -
- +
+
)} diff --git a/client/src/components/ui/scroll-area.tsx b/client/src/components/ui/scroll-area.tsx new file mode 100644 index 00000000..5ea3aa6f --- /dev/null +++ b/client/src/components/ui/scroll-area.tsx @@ -0,0 +1,49 @@ +"use client"; + +import * as React from "react"; + +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; + +import { cn } from "@/lib/utils"; + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)); +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)); +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; + +export default { ScrollArea, ScrollBar }; diff --git a/client/src/components/ui/search.tsx b/client/src/components/ui/search.tsx index 9194ef10..dfe9461b 100644 --- a/client/src/components/ui/search.tsx +++ b/client/src/components/ui/search.tsx @@ -8,6 +8,8 @@ import { useDebounce } from "rooks"; import { cn } from "@/lib/utils"; +import { Input } from "@/components/ui/input"; + export default function Search({ placeholder, onChange, @@ -30,13 +32,12 @@ export default function Search({ }, 150); return ( -
+
- { setValue(e.target.value); diff --git a/client/src/components/ui/select.tsx b/client/src/components/ui/select.tsx index 66888ba1..562aa2b3 100644 --- a/client/src/components/ui/select.tsx +++ b/client/src/components/ui/select.tsx @@ -2,13 +2,8 @@ import * as React from "react"; -import { - CaretSortIcon, - CheckIcon, - ChevronDownIcon, - ChevronUpIcon, -} from "@radix-ui/react-icons"; import * as SelectPrimitive from "@radix-ui/react-select"; +import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"; import { cn } from "@/lib/utils"; @@ -25,14 +20,14 @@ const SelectTrigger = React.forwardRef< span]:line-clamp-1", + "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-3xl border border-border bg-secondary-foreground px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", className, )} {...props} > {children} - + )); @@ -124,14 +119,14 @@ const SelectItem = React.forwardRef< - + - + {children} diff --git a/client/src/components/ui/sidebar.tsx b/client/src/components/ui/sidebar.tsx index 450a005d..b7e11960 100644 --- a/client/src/components/ui/sidebar.tsx +++ b/client/src/components/ui/sidebar.tsx @@ -4,7 +4,7 @@ import * as React from "react"; import { Slot } from "@radix-ui/react-slot"; import { VariantProps, cva } from "class-variance-authority"; -import { PanelLeft } from "lucide-react"; +import { PanelLeftCloseIcon, PanelLeftOpenIcon } from "lucide-react"; import { cn } from "@/lib/utils"; @@ -26,7 +26,7 @@ const SIDEBAR_COOKIE_NAME = "sidebar:state"; const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; const SIDEBAR_WIDTH = "16rem"; const SIDEBAR_WIDTH_MOBILE = "18rem"; -const SIDEBAR_WIDTH_ICON = "3rem"; +const SIDEBAR_WIDTH_ICON = "4rem"; const SIDEBAR_KEYBOARD_SHORTCUT = "b"; type SidebarContext = { @@ -271,26 +271,32 @@ const Sidebar = React.forwardRef< ); Sidebar.displayName = "Sidebar"; +const PANEL_ICON_CLASSES = "h-6 w-6 stroke-[1.5]"; + const SidebarTrigger = React.forwardRef< React.ElementRef, React.ComponentProps >(({ className, onClick, ...props }, ref) => { - const { toggleSidebar } = useSidebar(); + const { toggleSidebar, open } = useSidebar(); return ( ); @@ -370,7 +376,10 @@ const SidebarHeader = React.forwardRef<
); @@ -504,7 +513,7 @@ const SidebarMenu = React.forwardRef<
    )); @@ -517,14 +526,14 @@ const SidebarMenuItem = React.forwardRef<
  • )); SidebarMenuItem.displayName = "SidebarMenuItem"; const sidebarMenuButtonVariants = cva( - "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", + "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-full py-2 px-4 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", { variants: { variant: { @@ -533,7 +542,7 @@ const sidebarMenuButtonVariants = cva( "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", }, size: { - default: "h-8 text-sm", + default: "h-10 text-sm", sm: "h-7 text-xs", lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0", }, diff --git a/client/src/components/ui/table.tsx b/client/src/components/ui/table.tsx index 271acba3..c8aeb781 100644 --- a/client/src/components/ui/table.tsx +++ b/client/src/components/ui/table.tsx @@ -58,7 +58,7 @@ const TableRow = React.forwardRef< [role=checkbox]]:translate-y-[2px]", + "h-10 bg-big-stone-950 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className, )} {...props} diff --git a/client/src/components/ui/tabs.tsx b/client/src/components/ui/tabs.tsx index b2443993..501e3458 100644 --- a/client/src/components/ui/tabs.tsx +++ b/client/src/components/ui/tabs.tsx @@ -15,7 +15,7 @@ const TabsList = React.forwardRef< - {open ? "Blue Carbon Cost" : "BCC"} + {open ? "Blue Carbon Cost Tool" : "BCCT"} - {navItems.overview.map((item) => ( - - - - {item.icon && } - {item.title} - - - - ))} - - - - - {navItems.projects.label} - - - {navItems.projects.items.map((item) => ( + {navItems.main.map((item) => ( - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec pur - us. Ut sit amet libero auctor, tincidunt mauris sit amet, ultricies - turpis. Donec nec nulla in purus Lorem ipsum dolor sit amet, consectetur - adipiscing elit. Nullam nec pur us. Ut sit amet libero auctor, tincidunt - mauris sit amet, ultricies turpis. Donec nec nulla in purus Lorem ipsum - dolor sit amet, consectetur adipiscing elit. Nullam nec pur us. Ut sit - amet libero auctor, tincidunt mauris sit amet, ultricies turpis. Donec nec - nulla in purus Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Nullam nec pur us. Ut sit amet libero auctor, tincidunt mauris sit amet, - ultricies turpis. Donec nec nulla in purus Lorem ipsum dolor sit amet, - consectetur adipiscing elit. Nullam nec pur us. Ut sit amet libero auctor, - tincidunt mauris sit amet, ultricies turpis. Donec nec nulla in purus - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec pur - us. Ut sit amet libero auctor, tincidunt mauris sit amet, ultricies - turpis. Donec nec nulla in purus +
    + +
    ); } diff --git a/client/src/containers/projects/header/index.tsx b/client/src/containers/projects/header/index.tsx index 1e5686ff..db8d873b 100644 --- a/client/src/containers/projects/header/index.tsx +++ b/client/src/containers/projects/header/index.tsx @@ -1,7 +1,7 @@ "use client"; import { useSetAtom } from "jotai"; -import { PlusIcon } from "lucide-react"; +import { Settings2Icon } from "lucide-react"; import { projectsUIState } from "@/app/(projects)/store"; @@ -18,10 +18,10 @@ export default function ProjectsHeader() {
    -

    Overview

    +

    Projects Overview

    diff --git a/client/src/containers/projects/header/parameters/index.tsx b/client/src/containers/projects/header/parameters/index.tsx index 2ed5dc9f..d5490040 100644 --- a/client/src/containers/projects/header/parameters/index.tsx +++ b/client/src/containers/projects/header/parameters/index.tsx @@ -21,6 +21,7 @@ export const PROJECT_PARAMETERS = [ { key: FILTER_KEYS[1], label: "Project size", + className: "w-[125px]", options: [ { label: "Small", @@ -39,6 +40,7 @@ export const PROJECT_PARAMETERS = [ { key: FILTER_KEYS[2], label: "Carbon pricing type", + className: "w-[195px]", options: [ { label: "Market price", @@ -53,6 +55,7 @@ export const PROJECT_PARAMETERS = [ { key: FILTER_KEYS[3], label: "Cost", + className: "w-[85px]", options: [ { label: "Total", @@ -88,7 +91,7 @@ export default function ParametersProjects() { handleParameters(v, parameter.key); }} > - + diff --git a/client/src/containers/projects/map/controls/legend/index.tsx b/client/src/containers/projects/map/controls/legend/index.tsx deleted file mode 100644 index d613699e..00000000 --- a/client/src/containers/projects/map/controls/legend/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useSetAtom } from "jotai"; -import { Layers } from "lucide-react"; - -import { cn } from "@/lib/utils"; - -import { projectsMapState } from "@/app/(projects)/store"; - -const BUTTON_CLASSES = { - default: - "flex h-8 w-8 items-center justify-center rounded-full border border-white bg-white text-black shadow-md transition-colors", - hover: "hover:border-gray-400 active:border-gray-400", -}; - -export default function LegendControl() { - const setProjectsMapState = useSetAtom(projectsMapState); - const handleMapLegend = () => { - setProjectsMapState((prev) => ({ - ...prev, - legendOpen: !prev.legendOpen, - })); - }; - - return ( - - ); -} diff --git a/client/src/containers/projects/map/controls/zoom/index.tsx b/client/src/containers/projects/map/controls/zoom/index.tsx index daa450ed..30214c4b 100644 --- a/client/src/containers/projects/map/controls/zoom/index.tsx +++ b/client/src/containers/projects/map/controls/zoom/index.tsx @@ -1,17 +1,15 @@ -import { useCallback, MouseEvent } from "react"; +import { useCallback, MouseEvent, useEffect, useState } from "react"; import { useMap, MapRef } from "react-map-gl"; -import { Plus, Minus } from "lucide-react"; +import { PlusIcon, MinusIcon } from "lucide-react"; import { cn } from "@/lib/utils"; -const BUTTON_CLASSES = { - default: - "flex h-8 w-8 items-center justify-center rounded-full border border-white bg-white text-black shadow-md transition-colors", - hover: "hover:border-gray-400 active:border-gray-400", - disabled: "opacity-50 cursor-default", -}; +import { Button } from "@/components/ui/button"; + +const BUTTON_CLASSES = "h-8 w-8"; +const ICON_CLASSES = "h-6 w-6 text-big-stone-50"; export default function ZoomControl({ id = "default", @@ -22,14 +20,33 @@ export default function ZoomControl({ }) { const { [id]: mapRef } = useMap(); - const zoom = mapRef?.getZoom() as NonNullable>; - const minZoom = mapRef?.getMinZoom() as NonNullable< + const initialZoom = mapRef?.getMap().getZoom() as NonNullable< + ReturnType + >; + const minZoom = mapRef?.getMap().getMinZoom() as NonNullable< ReturnType >; - const maxZoom = mapRef?.getMaxZoom() as NonNullable< + const maxZoom = mapRef?.getMap().getMaxZoom() as NonNullable< ReturnType >; + const [zoomState, setZoomState] = + useState>(initialZoom); + + useEffect(() => { + if (!mapRef) return; + + const onZoomEnd = () => { + setZoomState(mapRef.getMap().getZoom()); + }; + + mapRef?.on("zoomend", onZoomEnd); + + return () => { + mapRef.off("zoomend", onZoomEnd); + }; + }, [mapRef]); + const increaseZoom = useCallback( (e: MouseEvent) => { e.stopPropagation(); @@ -51,32 +68,33 @@ export default function ZoomControl({ ); return ( -
    - - - + + +
    ); } diff --git a/client/src/containers/projects/map/index.tsx b/client/src/containers/projects/map/index.tsx index c8a8adb2..1937056f 100644 --- a/client/src/containers/projects/map/index.tsx +++ b/client/src/containers/projects/map/index.tsx @@ -1,24 +1,23 @@ -"use client"; +import { ComponentProps, useState } from "react"; -import { ComponentProps } from "react"; - -import { useAtomValue } from "jotai"; - -import { projectsMapState } from "@/app/(projects)/store"; +import { LayersIcon } from "lucide-react"; import Controls from "@/containers/projects/map/controls"; -import LegendControl from "@/containers/projects/map/controls/legend"; import ZoomControl from "@/containers/projects/map/controls/zoom"; import ProjectsLayer from "@/containers/projects/map/layers/projects"; import { MATRIX_COLORS } from "@/containers/projects/map/layers/projects/utils"; -import Legend from "@/containers/projects/map/legend"; import MatrixLegend from "@/containers/projects/map/legend/types/matrix"; import Map from "@/components/map"; +import { Button } from "@/components/ui/button"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; export default function ProjectsMap() { - const { legendOpen } = useAtomValue(projectsMapState); - + const [isLegendOpen, setIsLegendOpen] = useState(false); const matrixItems: ComponentProps["intersections"] = Object.keys(MATRIX_COLORS).map((key, index) => ({ color: key, @@ -26,19 +25,35 @@ export default function ProjectsMap() { })); return ( -
    +
    - + + + + + { + e.preventDefault(); + }} + > + + + - {legendOpen && ( - - - - )} +
    diff --git a/client/src/containers/projects/map/layers/projects/index.tsx b/client/src/containers/projects/map/layers/projects/index.tsx index 2fc40ffb..2e245187 100644 --- a/client/src/containers/projects/map/layers/projects/index.tsx +++ b/client/src/containers/projects/map/layers/projects/index.tsx @@ -50,6 +50,7 @@ export default function ProjectsLayer() { id: "cost-abatement-layer", type: "fill", source: costAbatementSource.id, + paint: { "fill-color": [ "match", @@ -75,14 +76,14 @@ export default function ProjectsLayer() { ...colors, "#FFF", ], - "fill-opacity": 1, + "fill-opacity": 0.8, }, }; return ( <> - + ); } diff --git a/client/src/containers/projects/map/layers/projects/utils.ts b/client/src/containers/projects/map/layers/projects/utils.ts index 8282d577..5155e82a 100644 --- a/client/src/containers/projects/map/layers/projects/utils.ts +++ b/client/src/containers/projects/map/layers/projects/utils.ts @@ -1,13 +1,20 @@ export const MATRIX_COLORS: Record = { - "#e8e8e8": ["00"], - "#e4acac": ["01"], - "#c85a5a": ["02"], - "#b0d5df": ["10"], - "#ad9ea5": ["11"], - "#985356": ["12"], - "#64acbe": ["20"], - "#627f8c": ["21"], - "#574249": ["22"], + "#FFFFFF": ["00"], + "#FFF6CC": ["01"], + "#FFEF9C": ["02"], + "#EACD3F": ["03"], + "#CCF5FF": ["10"], + "#DAF1C5": ["11"], + "#CAE394": ["12"], + "#C4C63C": ["13"], + "#9EE8FA": ["20"], + "#A4DDA6": ["21"], + "#A0D171": ["22"], + "#93BE37": ["23"], + "#6CD9F4": ["30"], + "#6FCEB6": ["31"], + "#72C476": ["32"], + "#75B935": ["33"], }; export const generateColorRamp = function ( diff --git a/client/src/containers/projects/map/legend/index.tsx b/client/src/containers/projects/map/legend/index.tsx deleted file mode 100644 index 964b1546..00000000 --- a/client/src/containers/projects/map/legend/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { PropsWithChildren } from "react"; - -import { cn } from "@/lib/utils"; - -export default function Legend({ - children, - className, -}: PropsWithChildren<{ - className?: HTMLDivElement["className"]; -}>) { - return ( -
    - {children} -
    - ); -} diff --git a/client/src/containers/projects/map/legend/types/matrix.tsx b/client/src/containers/projects/map/legend/types/matrix.tsx index 4f9cf8b7..c9b7fbb5 100644 --- a/client/src/containers/projects/map/legend/types/matrix.tsx +++ b/client/src/containers/projects/map/legend/types/matrix.tsx @@ -17,19 +17,19 @@ export default function MatrixLegend({ }: LegendTypeProps & LegendMatrixIntersectionsProps & { colorNumber?: number }) { return ( -
    +
    - Abatement potential and cost $/tc02 + Abatement potential and cost
    -
    -
    +
    +

    Cost

    -

    +

    Abatement

    @@ -47,98 +47,9 @@ export default function MatrixLegend({ /> ))}
    - - {/*
    */} - {/* */} - {/* */} - {/* */} - {/* 10*/} - {/* */} - {/*
    */} - {/* */} - {/* */} - {/* */} - {/* 50*/} - {/* */} - {/*
    */} - {/* */} - {/* */} - {/* */} - {/* 100*/} - {/* */} - {/*
    */} - {/*
    */} - - {/*
    */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* 10*/} - {/* */} - {/* */} - {/*
    */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* 50*/} - {/* */} - {/* */} - {/*
    */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* 100*/} - {/* */} - {/* */} - {/*
    */} - {/*
    */}
- - {/**/} - {/*
    */} - {/* {items.map(({ value, color }) => (*/} - {/* */} - {/* */} - {/*
    {value}
    */} - {/* */} - {/* ))}*/} - {/*
*/} - {/*
*/}
); diff --git a/client/src/containers/projects/table/index.tsx b/client/src/containers/projects/table/index.tsx index 81196f3b..6cfaf042 100644 --- a/client/src/containers/projects/table/index.tsx +++ b/client/src/containers/projects/table/index.tsx @@ -9,15 +9,15 @@ export default function TableVisualization() { const [tableView] = useTableView(); return ( - <> +
-
+
{tableView === "overview" && } {tableView === "scorecard-prioritization" && ( )} {tableView === "key-costs" && }
- +
); } diff --git a/client/src/containers/projects/table/pagination/index.tsx b/client/src/containers/projects/table/pagination/index.tsx index 42ddbc86..58a11a18 100644 --- a/client/src/containers/projects/table/pagination/index.tsx +++ b/client/src/containers/projects/table/pagination/index.tsx @@ -38,7 +38,7 @@ export default function TablePagination({
-
+
{pagination.pageIndex * pagination.pageSize + 1} -{" "} {Math.min( (pagination.pageIndex + 1) * pagination.pageSize, diff --git a/client/src/containers/projects/table/toolbar/index.tsx b/client/src/containers/projects/table/toolbar/index.tsx index f3250c44..64c4ff84 100644 --- a/client/src/containers/projects/table/toolbar/index.tsx +++ b/client/src/containers/projects/table/toolbar/index.tsx @@ -4,7 +4,7 @@ import TabsProjectsTable from "@/containers/projects/table/toolbar/table-selecto export default function ToolbarProjectsTable() { return ( -
+
diff --git a/client/src/containers/projects/table/toolbar/info-download/index.tsx b/client/src/containers/projects/table/toolbar/info-download/index.tsx index 57c41a51..61d03c60 100644 --- a/client/src/containers/projects/table/toolbar/info-download/index.tsx +++ b/client/src/containers/projects/table/toolbar/info-download/index.tsx @@ -16,14 +16,11 @@ export default function InfoDownloadProjectsTable() { }; return ( -
+
- diff --git a/client/src/containers/projects/table/toolbar/search/index.tsx b/client/src/containers/projects/table/toolbar/search/index.tsx index 8241aa66..3029bb86 100644 --- a/client/src/containers/projects/table/toolbar/search/index.tsx +++ b/client/src/containers/projects/table/toolbar/search/index.tsx @@ -13,5 +13,11 @@ export default function SearchProjectsTable() { await setFilters((prev) => ({ ...prev, keyword: v })); }; - return ; + return ( + + ); } diff --git a/client/src/containers/projects/table/view/key-costs/index.tsx b/client/src/containers/projects/table/view/key-costs/index.tsx index 202b67bc..6a64c546 100644 --- a/client/src/containers/projects/table/view/key-costs/index.tsx +++ b/client/src/containers/projects/table/view/key-costs/index.tsx @@ -57,8 +57,6 @@ export function KeyCostsTable() { pagination, }).queryKey; - console.log(TABLE_COLUMNS[0]); - const { data, isSuccess } = client.projects.getProjects.useQuery( queryKey, { diff --git a/client/src/containers/projects/table/view/overview/index.tsx b/client/src/containers/projects/table/view/overview/index.tsx index 513078f1..f40570f9 100644 --- a/client/src/containers/projects/table/view/overview/index.tsx +++ b/client/src/containers/projects/table/view/overview/index.tsx @@ -95,9 +95,9 @@ export function OverviewTable() { return ( <> - + {table.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => { return ( diff --git a/client/tailwind.config.ts b/client/tailwind.config.ts index 63413b00..d92d8263 100644 --- a/client/tailwind.config.ts +++ b/client/tailwind.config.ts @@ -5,7 +5,7 @@ const config: Config = { content: ["./src/**/*.{ts,tsx}"], theme: { container: { - center: "true", + center: true, }, extend: { colors: { @@ -59,12 +59,42 @@ const config: Config = { border: "hsl(var(--sidebar-border))", ring: "hsl(var(--sidebar-ring))", }, + "big-stone": { + 50: "#f2f7fd", + 100: "#e4edfa", + 200: "#c3dbf4", + 300: "#8fbeea", + 400: "#539ddd", + 500: "#2d80ca", + 600: "#1e64ab", + 700: "#19508b", + 800: "#194673", + 900: "#1a3b60", + 950: "#132a47", + 1000: "#0f2139", + }, + "sky-blue": { + 50: "#edfcfe", + 100: "#d1f5fc", + 200: "#a8eaf9", + 300: "#6cd9f4", + 400: "#29bee7", + 500: "#0da1cd", + 600: "#0e80ac", + 700: "#12678c", + 800: "#185572", + 900: "#194760", + 950: "#0a2d42", + }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, + fontFamily: { + sans: ["var(--font-spline-sans)"], + }, }, }, plugins: [require("tailwindcss-animate")], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 897c6e76..47359bd0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -294,6 +294,12 @@ importers: '@radix-ui/react-label': specifier: 2.1.0 version: 2.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popover': + specifier: 1.1.2 + version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-scroll-area': + specifier: 1.2.0 + version: 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-select': specifier: 2.1.2 version: 2.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2188,6 +2194,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popover@1.1.2': + resolution: {integrity: sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popper@1.2.0': resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==} peerDependencies: @@ -2253,6 +2272,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-scroll-area@1.2.0': + resolution: {integrity: sha512-q2jMBdsJ9zB7QG6ngQNzNwlvxLQqONyL58QbEGwuyRZZb/ARQwk3uQVbCF7GvQVOtV6EU/pDxAw3zRzJZI3rpQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-select@2.1.2': resolution: {integrity: sha512-rZJtWmorC7dFRi0owDmoijm6nSJH1tVw64QGiNIZ9PNLyBDtG+iAq+XGsya052At4BfarzY/Dhv9wrrUr6IMZA==} peerDependencies: @@ -10134,6 +10166,29 @@ snapshots: '@types/react': 18.3.5 '@types/react-dom': 18.3.0 + '@radix-ui/react-popover@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.5)(react@18.3.1) + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.6.0(@types/react@18.3.5)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + '@types/react-dom': 18.3.0 + '@radix-ui/react-popper@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -10198,6 +10253,23 @@ snapshots: '@types/react': 18.3.5 '@types/react-dom': 18.3.0 + '@radix-ui/react-scroll-area@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + '@types/react-dom': 18.3.0 + '@radix-ui/react-select@2.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/number': 1.1.0