diff --git a/client/package.json b/client/package.json
index af4d39cf..b2e4fbdf 100644
--- a/client/package.json
+++ b/client/package.json
@@ -7,11 +7,13 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
- "lint:fix": "next lint --fix"
+ "lint:fix": "next lint --fix",
+ "check-types": "tsc"
},
"dependencies": {
"@hookform/resolvers": "3.9.0",
"@lukemorales/query-key-factory": "1.3.4",
+ "@radix-ui/react-accordion": "1.2.1",
"@radix-ui/react-alert-dialog": "1.1.2",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-checkbox": "1.1.2",
@@ -20,6 +22,7 @@
"@radix-ui/react-icons": "1.3.0",
"@radix-ui/react-label": "2.1.0",
"@radix-ui/react-popover": "1.1.2",
+ "@radix-ui/react-radio-group": "1.2.1",
"@radix-ui/react-scroll-area": "1.2.0",
"@radix-ui/react-select": "2.1.2",
"@radix-ui/react-separator": "1.1.0",
diff --git a/client/src/app/auth/api/[...nextauth]/config.ts b/client/src/app/auth/api/[...nextauth]/config.ts
index f68bbb0e..e28316ce 100644
--- a/client/src/app/auth/api/[...nextauth]/config.ts
+++ b/client/src/app/auth/api/[...nextauth]/config.ts
@@ -1,4 +1,5 @@
import { cookies } from "next/headers";
+
import { UserWithAccessToken } from "@shared/dtos/users/user.dto";
import { LogInSchema } from "@shared/schemas/auth/login.schema";
import type {
diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx
index 737ab4fd..664f8596 100644
--- a/client/src/app/layout.tsx
+++ b/client/src/app/layout.tsx
@@ -39,9 +39,7 @@ export default async function RootLayout({
-
- {children}
-
+ {children}
diff --git a/client/src/app/projects/new/page.tsx b/client/src/app/projects/new/page.tsx
new file mode 100644
index 00000000..db5a03cf
--- /dev/null
+++ b/client/src/app/projects/new/page.tsx
@@ -0,0 +1,41 @@
+import { ACTIVITY } from "@shared/entities/activity.enum";
+import { ECOSYSTEM } from "@shared/entities/ecosystem.enum";
+import {
+ dehydrate,
+ HydrationBoundary,
+ QueryClient,
+} from "@tanstack/react-query";
+
+import { client } from "@/lib/query-client";
+import { queryKeys } from "@/lib/query-keys";
+
+import CreateCustomProject from "@/containers/projects/new";
+
+export default async function CreateCustomProjectPage() {
+ const queryClient = new QueryClient();
+
+ await queryClient.prefetchQuery({
+ queryKey: queryKeys.customProjects.countries.queryKey,
+ queryFn: () => client.customProjects.getAvailableCountries.query(),
+ });
+
+ const defaultActivity = ACTIVITY.CONSERVATION;
+ const defaultEcosystem = ECOSYSTEM.SEAGRASS;
+
+ await queryClient.prefetchQuery({
+ queryKey: queryKeys.customProjects.assumptions({
+ ecosystem: defaultEcosystem,
+ activity: defaultActivity,
+ }).queryKey,
+ queryFn: () =>
+ client.customProjects.getDefaultAssumptions.query({
+ query: { ecosystem: defaultEcosystem, activity: defaultActivity },
+ }),
+ });
+
+ return (
+
+
+
+ );
+}
diff --git a/client/src/app/providers.tsx b/client/src/app/providers.tsx
index 26b8b313..c7616ed2 100644
--- a/client/src/app/providers.tsx
+++ b/client/src/app/providers.tsx
@@ -17,7 +17,7 @@ import { TooltipProvider } from "@/components/ui/tooltip";
let browserQueryClient: QueryClient | undefined = undefined;
-function getQueryClient() {
+export function getQueryClient() {
if (isServer) {
// Server: always make a new query client
return makeQueryClient();
diff --git a/client/src/components/ui/accordion.tsx b/client/src/components/ui/accordion.tsx
new file mode 100644
index 00000000..6e61c328
--- /dev/null
+++ b/client/src/components/ui/accordion.tsx
@@ -0,0 +1,58 @@
+"use client";
+
+import * as React from "react";
+
+import * as AccordionPrimitive from "@radix-ui/react-accordion";
+import { ChevronsDownIcon } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+
+const Accordion = AccordionPrimitive.Root;
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AccordionItem.displayName = "AccordionItem";
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ svg]:rotate-180",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+));
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+));
+AccordionContent.displayName = AccordionPrimitive.Content.displayName;
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
diff --git a/client/src/components/ui/form.tsx b/client/src/components/ui/form.tsx
index d5fcbe2e..20125513 100644
--- a/client/src/components/ui/form.tsx
+++ b/client/src/components/ui/form.tsx
@@ -16,6 +16,7 @@ import { Slot } from "@radix-ui/react-slot";
import { cn } from "@/lib/utils";
+import InfoButton from "@/components/ui/info-button";
import { Label } from "@/components/ui/label";
const Form = FormProvider;
@@ -91,10 +92,29 @@ FormItem.displayName = "FormItem";
const FormLabel = React.forwardRef<
React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => {
+ React.ComponentPropsWithoutRef & {
+ tooltip?: {
+ title: string;
+ content: React.ReactNode;
+ };
+ }
+>(({ className, tooltip, ...props }, ref) => {
const { error, formItemId } = useFormField();
+ if (tooltip) {
+ return (
+
+
+ {tooltip.content}
+
+ );
+ }
+
return (