From 34e70ec258119224e109eb70a6fab9f6e975adb8 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 19 Feb 2025 14:47:24 +0100
Subject: [PATCH 01/48] created a basic category context, provider and turned
 the landing page category component into a consumer

---
 src/app/[locale]/layout.tsx                |  5 +-
 src/components/app/Categories.tsx          | 40 +++---------
 src/components/app/results/CategoryBar.tsx |  7 +-
 src/contexts/categoryContext.tsx           | 75 ++++++++++++++++++++++
 4 files changed, 93 insertions(+), 34 deletions(-)
 create mode 100644 src/contexts/categoryContext.tsx

diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index bfb3531..755b785 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -6,6 +6,7 @@ import React from "react";
 import Banner from "@/components/layout/Banner";
 import Footer from "@/components/layout/Footer";
 import Header from "@/components/layout/Header";
+import { CategoryProvider } from "@/contexts/categoryContext";
 
 const env = process.env.NEXT_PUBLIC_SHOW_BANNER;
 const showBanner = env && (env.toLowerCase() === "true" || env === "1");
@@ -55,7 +56,9 @@ export default function RootLayout({
           </header>
 
           <NextIntlClientProvider locale={locale} messages={messages}>
-            <div className="grow flex flex-col">{children}</div>
+            <CategoryProvider>
+              <div className="grow flex flex-col">{children}</div>
+            </CategoryProvider>
           </NextIntlClientProvider>
 
           <div className="sticky bottom-0">
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index e9168c7..b73bf8a 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -2,46 +2,22 @@
 
 import Image from "next/image";
 import { useTranslations } from "next-intl";
-import { useEffect, useState } from "react";
+import { useState, useContext } from "react";
 
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
+import { CategoryContext } from "@/contexts/categoryContext";
 import { Link } from "@/i18n";
-import type { Category } from "@/types/types";
-import { getAllCategories } from "@/utils/apiCall";
 
 const Categories = () => {
   const t = useTranslations("Categories");
-  const [allCategories, setCategories] = useState<Category[]>([]);
-  const [isLoading, setIsLoading] = useState<boolean>(true);
-  const [hasError, setHasError] = useState<boolean>(false);
-
-  useEffect(() => {
-    const updateCategories = async () => {
-      try {
-        setIsLoading(true);
-        setHasError(false);
-        const categories = await getAllCategories();
-        for (const category of categories) {
-          let iconImport;
+  const { viewableCategories } = useContext(CategoryContext);
 
-          try {
-            iconImport = await import("@/resources/images/category/" + category.id + ".png");
-          } catch {
-            iconImport = await import("@/resources/images/category/software.png");
-          }
+  // TODO: handle loading and error states
+  const [isLoading, setIsLoading] = useState<boolean>(false);
+  const [hasError, setHasError] = useState<boolean>(false);
 
-          category.icon = iconImport.default;
-        }
-        setCategories(categories);
-        setIsLoading(false);
-      } catch (error) {
-        setIsLoading(false);
-        setHasError(true);
-      }
-    };
-    updateCategories();
-  }, []);
+  console.log("context categories via API in Categories.tsx:", viewableCategories);
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
   /* traditional JS module that makes the badge numbers appear as strings representing exponents
@@ -60,7 +36,7 @@ const Categories = () => {
           </div>
           {/* grid of categories */}
           <div className="px-10 grid grid-cols-1  md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 gap-y-28 md:gap-x-16 lg:gap-x-32 xl:gap-x-46 content-center justify-center">
-            {allCategories.map((o) => {
+            {viewableCategories.map((o) => {
               return (
                 <Link
                   key={o.id}
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index 44565b8..3ecbe02 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -2,8 +2,9 @@
 
 import { useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { Dispatch, SetStateAction, Suspense, useEffect, useState, useRef } from "react";
+import { Dispatch, SetStateAction, Suspense, useEffect, useState, useRef, useContext } from "react";
 
+import { CategoryContext } from "@/contexts/categoryContext";
 import { Link } from "@/i18n";
 import type { Category } from "@/types/types";
 import { getAllCategories, getCategoryCounts } from "@/utils/apiCall";
@@ -45,6 +46,10 @@ const CategoryBarComponent = ({
   const [categories, setCategories] = useState<Category[]>([]);
   const [baseCategories, setBaseCategories] = useState<Category[]>([]);
 
+  const { viewableCategories } = useContext(CategoryContext);
+
+  console.log("context categories via API:", viewableCategories);
+
   const firstRender = useRef(true);
 
   useEffect(() => {
diff --git a/src/contexts/categoryContext.tsx b/src/contexts/categoryContext.tsx
new file mode 100644
index 0000000..167adcd
--- /dev/null
+++ b/src/contexts/categoryContext.tsx
@@ -0,0 +1,75 @@
+"use client";
+import { createContext, useState, useEffect } from "react";
+
+import { Category } from "@/types/types";
+import { getAllCategories } from "@/utils/apiCall";
+
+// Defining the type of the context
+// TODO: move this to types.ts
+export interface CategoryContextType {
+  viewableCategories: Category[];
+  setViewableCategories: (categories: Category[]) => void;
+  primaryActiveCategory: Category | null;
+  setPrimaryActiveCategory: (category: Category) => void;
+  relatedActiveCategory: Category | null;
+  setRelatedActiveCategory: (category: Category) => void;
+}
+
+const defaultCategoryContext: CategoryContextType = {
+  viewableCategories: [],
+  setViewableCategories: () => {},
+  primaryActiveCategory: null,
+  setPrimaryActiveCategory: () => {},
+  relatedActiveCategory: null,
+  setRelatedActiveCategory: () => {},
+};
+
+// Creating a context for the category
+export const CategoryContext = createContext<CategoryContextType>(defaultCategoryContext);
+
+// Creating a provider for the category
+export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
+  // The state that will be shared
+  const [viewableCategories, setViewableCategories] = useState<Category[]>([]);
+  const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
+  const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
+
+  useEffect(() => {
+    const fetchCategoriesForLandingPage = async () => {
+      console.log("Fetching categories in context");
+      try {
+        const baseCategories = await getAllCategories();
+        for (const category of baseCategories) {
+          let iconImport;
+
+          try {
+            iconImport = await import("@/resources/images/category/" + category.id + ".png");
+          } catch {
+            iconImport = await import("@/resources/images/category/software.png");
+          }
+
+          category.icon = iconImport.default;
+        }
+        setViewableCategories(baseCategories);
+      } catch (error) {
+        console.log("Error fetching categories", error);
+      }
+    };
+    fetchCategoriesForLandingPage();
+  }, []);
+
+  return (
+    <CategoryContext.Provider
+      value={{
+        viewableCategories,
+        setViewableCategories,
+        primaryActiveCategory,
+        setPrimaryActiveCategory,
+        relatedActiveCategory,
+        setRelatedActiveCategory,
+      }}
+    >
+      {children}
+    </CategoryContext.Provider>
+  );
+};
-- 
GitLab


From 840acb0f4742598a360272ec8e9e889c9cf99ca5 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 19 Feb 2025 16:46:15 +0100
Subject: [PATCH 02/48] making categories available on category bar alongside
 primaryActiveCategory selection in results page

---
 src/app/[locale]/layout.tsx                |   2 +-
 src/app/[locale]/results/page.tsx          |  12 +-
 src/components/app/Categories.tsx          |   5 +-
 src/components/app/results/CategoryBar.tsx | 113 ++++++++---------
 src/contexts/CategoryContext.tsx           | 141 +++++++++++++++++++++
 src/contexts/categoryContext.tsx           |  75 -----------
 6 files changed, 211 insertions(+), 137 deletions(-)
 create mode 100644 src/contexts/CategoryContext.tsx
 delete mode 100644 src/contexts/categoryContext.tsx

diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index 755b785..3a73ca3 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -6,7 +6,7 @@ import React from "react";
 import Banner from "@/components/layout/Banner";
 import Footer from "@/components/layout/Footer";
 import Header from "@/components/layout/Header";
-import { CategoryProvider } from "@/contexts/categoryContext";
+import { CategoryProvider } from "@/contexts/CategoryContext";
 
 const env = process.env.NEXT_PUBLIC_SHOW_BANNER;
 const showBanner = env && (env.toLowerCase() === "true" || env === "1");
diff --git a/src/app/[locale]/results/page.tsx b/src/app/[locale]/results/page.tsx
index cd67949..6a33e04 100644
--- a/src/app/[locale]/results/page.tsx
+++ b/src/app/[locale]/results/page.tsx
@@ -1,6 +1,6 @@
 "use client";
 
-import React, { Suspense, useState } from "react";
+import React, { Suspense, useState, useContext, useEffect } from "react";
 
 import BackBar from "@/components/app/results/BackBar";
 import CategoryBar from "@/components/app/results/CategoryBar";
@@ -8,15 +8,23 @@ import ListResults from "@/components/app/results/ListResults";
 import Error from "@/components/layout/Error";
 import MobileSearch from "@/components/layout/MobileSearch";
 import Spinner from "@/components/layout/Spinner";
+import { CategoryContext } from "@/contexts/CategoryContext";
 import type { Category } from "@/types/types";
 
 const ResultsComponent = () => {
+  const { primaryActiveCategory } = useContext(CategoryContext);
   const [hasBcError, setHasBcError] = useState(false);
   const [hasSearchError, setHasSearchError] = useState(false);
-  const [categoryLoading, setCategoryLoading] = useState(true);
+  const [categoryLoading, setCategoryLoading] = useState(false);
   const [resultsLoading, setResultsLoading] = useState(true);
   const [activeCategory, setActiveCategory] = useState<Category>();
 
+  useEffect(() => {
+    if (primaryActiveCategory !== null) {
+      setActiveCategory(primaryActiveCategory);
+    }
+  }, [primaryActiveCategory]);
+
   return (
     <div className="grow flex flex-col justify-start">
       <>
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index b73bf8a..ab4c1e8 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -6,12 +6,12 @@ import { useState, useContext } from "react";
 
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
-import { CategoryContext } from "@/contexts/categoryContext";
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n";
 
 const Categories = () => {
   const t = useTranslations("Categories");
-  const { viewableCategories } = useContext(CategoryContext);
+  const { viewableCategories, setPrimaryActiveCategory } = useContext(CategoryContext);
 
   // TODO: handle loading and error states
   const [isLoading, setIsLoading] = useState<boolean>(false);
@@ -42,6 +42,7 @@ const Categories = () => {
                   key={o.id}
                   href={`/results?category=${o.id}`}
                   className="group flex place-content-center transition ease-in-out hover:-translate-y-1 hover:scale-110"
+                  onClick={() => setPrimaryActiveCategory(o)}
                 >
                   {/* icon for category */}
                   <Image
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index 3ecbe02..2dd555c 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -4,10 +4,9 @@ import { useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
 import { Dispatch, SetStateAction, Suspense, useEffect, useState, useRef, useContext } from "react";
 
-import { CategoryContext } from "@/contexts/categoryContext";
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n";
 import type { Category } from "@/types/types";
-import { getAllCategories, getCategoryCounts } from "@/utils/apiCall";
 
 type Props = {
   activeCategory?: Category;
@@ -46,28 +45,28 @@ const CategoryBarComponent = ({
   const [categories, setCategories] = useState<Category[]>([]);
   const [baseCategories, setBaseCategories] = useState<Category[]>([]);
 
-  const { viewableCategories } = useContext(CategoryContext);
+  const { viewableCategories, primaryActiveCategory } = useContext(CategoryContext);
 
-  console.log("context categories via API:", viewableCategories);
+  console.log("context categories via API:", viewableCategories, primaryActiveCategory);
 
   const firstRender = useRef(true);
 
-  useEffect(() => {
-    const loadCategories = async () => {
-      try {
-        const allCategories = await getAllCategories();
-        if (inDetailsView && currentlyActivecategory) {
-          setBaseCategories(allCategories.filter((cat) => cat.id !== currentlyActivecategory));
-        } else {
-          setBaseCategories(allCategories);
-        }
-      } catch (error) {
-        setHasBcError(true);
-      }
-    };
-
-    loadCategories().catch(console.error);
-  }, [inDetailsView, currentlyActivecategory, setHasBcError]);
+  // useEffect(() => {
+  //   const loadCategories = async () => {
+  //     try {
+  //       const allCategories = await getAllCategories();
+  //       if (inDetailsView && currentlyActivecategory) {
+  //         setBaseCategories(allCategories.filter((cat) => cat.id !== currentlyActivecategory));
+  //       } else {
+  //         setBaseCategories(allCategories);
+  //       }
+  //     } catch (error) {
+  //       setHasBcError(true);
+  //     }
+  //   };
+
+  //   loadCategories().catch(console.error);
+  // }, [inDetailsView, currentlyActivecategory, setHasBcError]);
 
   useEffect(() => {
     if (!activeCategory && categories.length) {
@@ -82,48 +81,48 @@ const CategoryBarComponent = ({
     inDetailsView && setActiveCategory(categories[0]);
   }, [currentlyActivecategory, categories, inDetailsView, setActiveCategory]);
 
-  useEffect(() => {
-    const updateCategoryCount = async () => {
-      try {
-        setHasSearchError(false);
-        const updatedSearchText =
-          searchText || detailsText
-            ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
-            : undefined;
-
-        const categoryCounts = await getCategoryCounts(updatedSearchText);
-        const categories = structuredClone(baseCategories);
-
-        categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
-        setCategories(categories);
-      } catch (error) {
-        console.error("Failed to fetch category counts", error);
-        setHasSearchError(true);
-      } finally {
-        setCategoryLoading(false);
-        // console.log("here", categories);
-      }
-    };
-
-    if (firstRender.current) {
-      firstRender.current = false;
-    } else {
-      updateCategoryCount().catch(console.error);
-    }
-  }, [
-    baseCategories,
-    searchText,
-    detailsText,
-    setCategoryLoading,
-    setHasSearchError,
-    setHasBcError,
-  ]);
+  // useEffect(() => {
+  //   const updateCategoryCount = async () => {
+  //     try {
+  //       setHasSearchError(false);
+  //       const updatedSearchText =
+  //         searchText || detailsText
+  //           ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
+  //           : undefined;
+
+  //       const categoryCounts = await getCategoryCounts(updatedSearchText);
+  //       const categories = structuredClone(baseCategories);
+
+  //       categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
+  //       setCategories(categories);
+  //     } catch (error) {
+  //       console.error("Failed to fetch category counts", error);
+  //       setHasSearchError(true);
+  //     } finally {
+  //       setCategoryLoading(false);
+  //       // console.log("here", categories);
+  //     }
+  //   };
+
+  //   if (firstRender.current) {
+  //     firstRender.current = false;
+  //   } else {
+  //     updateCategoryCount().catch(console.error);
+  //   }
+  // }, [
+  //   baseCategories,
+  //   searchText,
+  //   detailsText,
+  //   setCategoryLoading,
+  //   setHasSearchError,
+  //   setHasBcError,
+  // ]);
 
   return (
     <>
       {categoryLoading || hasBcError || hasSearchError ? null : (
         <div className="bg-primary-helmholtz-dunkelblau text-primary-helmholtz-weiss grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:flex gap-y-2 gap-x-2 xl:gap-x-6 px-4 pt-2 pb-3 md:px-10 lg:px-16 xl:pt-4 xl:min-h-24">
-          {categories.map((o, idx) => (
+          {viewableCategories.map((o, idx) => (
             <Link
               href={
                 inDetailsView
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
new file mode 100644
index 0000000..c8710d5
--- /dev/null
+++ b/src/contexts/CategoryContext.tsx
@@ -0,0 +1,141 @@
+"use client";
+import { useSearchParams } from "next/navigation";
+import { createContext, useState, useEffect } from "react";
+
+import { Category } from "@/types/types";
+import { getAllCategories, getCategoryCounts } from "@/utils/apiCall";
+
+// Defining the type of the context
+// TODO: move this to types.ts
+export interface CategoryContextType {
+  viewableCategories: Category[];
+  setViewableCategories: (categories: Category[]) => void;
+  primaryActiveCategory: Category | null;
+  setPrimaryActiveCategory: (category: Category) => void;
+  relatedActiveCategory: Category | null;
+  setRelatedActiveCategory: (category: Category) => void;
+}
+
+const defaultCategoryContext: CategoryContextType = {
+  viewableCategories: [],
+  setViewableCategories: () => {},
+  primaryActiveCategory: null,
+  setPrimaryActiveCategory: () => {},
+  relatedActiveCategory: null,
+  setRelatedActiveCategory: () => {},
+};
+
+// Creating a context for the category
+export const CategoryContext = createContext<CategoryContextType>(defaultCategoryContext);
+
+// Creating a provider for the category
+export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
+  // The state that will be shared
+  const [viewableCategories, setViewableCategories] = useState<Category[]>([]);
+  const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
+  const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
+
+  // State for processing
+  const resultParams = useSearchParams();
+  // const currentlyActivecategory = resultParams.get("category") ?? undefined;
+  const searchText = resultParams.get("searchText") ?? undefined;
+  const detailsText = resultParams.get("detailsText") ?? undefined;
+  const [baseCategories, setBaseCategories] = useState<Category[]>([]);
+  const [inDetailsView, setInDetailsView] = useState<boolean>(false);
+
+  const setCategoryImages = async (categories: Category[]) => {
+    const categoriesClone = structuredClone(categories);
+    for (const category of categoriesClone) {
+      let iconImport;
+
+      try {
+        iconImport = await import("@/resources/images/category/" + category.id + ".png");
+      } catch {
+        iconImport = await import("@/resources/images/category/software.png");
+      }
+
+      category.icon = iconImport.default;
+    }
+    return categoriesClone;
+  };
+
+  useEffect(() => {
+    const fetchCategoriesForLandingPage = async () => {
+      console.log("Fetching categories in context");
+      try {
+        const baseCategories = await getAllCategories();
+        const categories = await setCategoryImages(baseCategories);
+        setViewableCategories(categories);
+      } catch (error) {
+        console.error("Error fetching categories for landing page", error);
+      }
+    };
+    fetchCategoriesForLandingPage();
+  }, []);
+
+  useEffect(() => {
+    if (detailsText) {
+      setInDetailsView(true);
+    } else {
+      setInDetailsView(false);
+    }
+  }, [detailsText]);
+
+  useEffect(() => {
+    const fetchCategoriesForCategoryBar = async () => {
+      console.log("Fetching categories for category bar from context");
+      try {
+        const baseCategories = await getAllCategories();
+        const categoriesWithIcons = await setCategoryImages(baseCategories);
+        if (inDetailsView && primaryActiveCategory) {
+          setBaseCategories(
+            categoriesWithIcons.filter((cat) => cat.id !== primaryActiveCategory.id)
+          );
+        } else {
+          setBaseCategories(categoriesWithIcons);
+        }
+      } catch (error) {
+        console.error("Error fetching categories for category bar", error);
+      }
+    };
+
+    fetchCategoriesForCategoryBar();
+  }, [inDetailsView, primaryActiveCategory]);
+
+  useEffect(() => {
+    const fetchAndUpdateCategoryBarCount = async () => {
+      console.log("Fetching category counts for category bar from context");
+      try {
+        const updatedSearchText =
+          searchText || detailsText
+            ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
+            : undefined;
+
+        const categoryCounts = await getCategoryCounts(updatedSearchText);
+        const categories = structuredClone(baseCategories);
+
+        categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
+        setViewableCategories(categories);
+      } catch (error) {
+        console.error("Failed to fetch category counts for categorybar", error);
+      }
+    };
+
+    fetchAndUpdateCategoryBarCount();
+  }, [searchText, detailsText, baseCategories]);
+
+  return (
+    <CategoryContext.Provider
+      value={{
+        viewableCategories,
+        setViewableCategories,
+        primaryActiveCategory,
+        setPrimaryActiveCategory,
+        relatedActiveCategory,
+        setRelatedActiveCategory,
+      }}
+    >
+      {children}
+    </CategoryContext.Provider>
+  );
+};
diff --git a/src/contexts/categoryContext.tsx b/src/contexts/categoryContext.tsx
deleted file mode 100644
index 167adcd..0000000
--- a/src/contexts/categoryContext.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-"use client";
-import { createContext, useState, useEffect } from "react";
-
-import { Category } from "@/types/types";
-import { getAllCategories } from "@/utils/apiCall";
-
-// Defining the type of the context
-// TODO: move this to types.ts
-export interface CategoryContextType {
-  viewableCategories: Category[];
-  setViewableCategories: (categories: Category[]) => void;
-  primaryActiveCategory: Category | null;
-  setPrimaryActiveCategory: (category: Category) => void;
-  relatedActiveCategory: Category | null;
-  setRelatedActiveCategory: (category: Category) => void;
-}
-
-const defaultCategoryContext: CategoryContextType = {
-  viewableCategories: [],
-  setViewableCategories: () => {},
-  primaryActiveCategory: null,
-  setPrimaryActiveCategory: () => {},
-  relatedActiveCategory: null,
-  setRelatedActiveCategory: () => {},
-};
-
-// Creating a context for the category
-export const CategoryContext = createContext<CategoryContextType>(defaultCategoryContext);
-
-// Creating a provider for the category
-export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
-  // The state that will be shared
-  const [viewableCategories, setViewableCategories] = useState<Category[]>([]);
-  const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
-  const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
-
-  useEffect(() => {
-    const fetchCategoriesForLandingPage = async () => {
-      console.log("Fetching categories in context");
-      try {
-        const baseCategories = await getAllCategories();
-        for (const category of baseCategories) {
-          let iconImport;
-
-          try {
-            iconImport = await import("@/resources/images/category/" + category.id + ".png");
-          } catch {
-            iconImport = await import("@/resources/images/category/software.png");
-          }
-
-          category.icon = iconImport.default;
-        }
-        setViewableCategories(baseCategories);
-      } catch (error) {
-        console.log("Error fetching categories", error);
-      }
-    };
-    fetchCategoriesForLandingPage();
-  }, []);
-
-  return (
-    <CategoryContext.Provider
-      value={{
-        viewableCategories,
-        setViewableCategories,
-        primaryActiveCategory,
-        setPrimaryActiveCategory,
-        relatedActiveCategory,
-        setRelatedActiveCategory,
-      }}
-    >
-      {children}
-    </CategoryContext.Provider>
-  );
-};
-- 
GitLab


From 7eea61a647af17e6241ef69f0e26523cb124cb0a Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 19 Feb 2025 17:04:16 +0100
Subject: [PATCH 03/48] made category bar visible in the details page view

---
 src/app/[locale]/results/page.tsx             |  1 -
 src/components/app/results/CategoryBar.tsx    | 82 +++++++++----------
 .../app/results/Details/RelatedResults.tsx    |  2 +-
 src/contexts/CategoryContext.tsx              |  8 +-
 4 files changed, 48 insertions(+), 45 deletions(-)

diff --git a/src/app/[locale]/results/page.tsx b/src/app/[locale]/results/page.tsx
index 6a33e04..9de1d05 100644
--- a/src/app/[locale]/results/page.tsx
+++ b/src/app/[locale]/results/page.tsx
@@ -37,7 +37,6 @@ const ResultsComponent = () => {
           setActiveCategory={setActiveCategory}
           setHasBcError={setHasBcError}
           setHasSearchError={setHasSearchError}
-          inDetailsView={false}
         />
         <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
           {!categoryLoading && !resultsLoading ? (
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index 2dd555c..7f735fc 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -2,7 +2,7 @@
 
 import { useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { Dispatch, SetStateAction, Suspense, useEffect, useState, useRef, useContext } from "react";
+import { Dispatch, SetStateAction, Suspense, useEffect, useContext } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n";
@@ -42,14 +42,14 @@ const CategoryBarComponent = ({
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
 
-  const [categories, setCategories] = useState<Category[]>([]);
-  const [baseCategories, setBaseCategories] = useState<Category[]>([]);
+  // const [categories, setCategories] = useState<Category[]>([]);
+  // const [baseCategories, setBaseCategories] = useState<Category[]>([]);
 
   const { viewableCategories, primaryActiveCategory } = useContext(CategoryContext);
 
   console.log("context categories via API:", viewableCategories, primaryActiveCategory);
 
-  const firstRender = useRef(true);
+  // const firstRender = useRef(true);
 
   // useEffect(() => {
   //   const loadCategories = async () => {
@@ -69,17 +69,19 @@ const CategoryBarComponent = ({
   // }, [inDetailsView, currentlyActivecategory, setHasBcError]);
 
   useEffect(() => {
-    if (!activeCategory && categories.length) {
-      setActiveCategory(categories[0]);
+    console.log("being called now");
+    if (!activeCategory && viewableCategories.length) {
+      setActiveCategory(viewableCategories[0]);
     }
-  }, [activeCategory, categories, setActiveCategory]);
+  }, [activeCategory, viewableCategories, setActiveCategory]);
 
   useEffect(() => {
-    categories.forEach((cat: Category) =>
+    console.log("being called now 2");
+    viewableCategories.forEach((cat: Category) =>
       cat.id === currentlyActivecategory ? setActiveCategory(cat) : null
     );
-    inDetailsView && setActiveCategory(categories[0]);
-  }, [currentlyActivecategory, categories, inDetailsView, setActiveCategory]);
+    inDetailsView && setActiveCategory(viewableCategories[0]);
+  }, [currentlyActivecategory, viewableCategories, inDetailsView, setActiveCategory]);
 
   // useEffect(() => {
   //   const updateCategoryCount = async () => {
@@ -120,38 +122,36 @@ const CategoryBarComponent = ({
 
   return (
     <>
-      {categoryLoading || hasBcError || hasSearchError ? null : (
-        <div className="bg-primary-helmholtz-dunkelblau text-primary-helmholtz-weiss grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:flex gap-y-2 gap-x-2 xl:gap-x-6 px-4 pt-2 pb-3 md:px-10 lg:px-16 xl:pt-4 xl:min-h-24">
-          {viewableCategories.map((o, idx) => (
-            <Link
-              href={
-                inDetailsView
-                  ? `/results/details?&category=${currentlyActivecategory}${search}${details}`
-                  : `/results?category=${o.id}${search}`
-              }
-              shallow
-              replace={!!(inDetailsView && activeCategory?.id !== currentlyActivecategory)}
-              onClick={() => setActiveCategory(o)}
-              key={idx}
-              className={`${
-                o.id === activeCategory?.id
-                  ? "font-semibold underline decoration-secondary-helmholtz-mint underline-offset-[20px]"
-                  : "text-blue-100 hover:rounded-full hover:bg-white/[0.12] hover:font-medium hover:py-3 hover:px-2"
-              } flex place-content-center cursor-pointer text-md w-full flex-1 py-2 font-normal text-primary-helmholtz-weiss `}
-            >
-              <div className="xl:basis-1/4 2xl:basis-1/5 mr-2 ">
-                {/* count indicator for category */}
-                <span className="inline-flex h-11 w-11 place-content-center rounded-full bg-primary-helmholtz-hellblau object-contain align-middle">
-                  <span className="self-center font-medium text-primary-helmholtz-weiss">
-                    {formatter.format(o.count)}
-                  </span>
+      <div className="bg-primary-helmholtz-dunkelblau text-primary-helmholtz-weiss grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:flex gap-y-2 gap-x-2 xl:gap-x-6 px-4 pt-2 pb-3 md:px-10 lg:px-16 xl:pt-4 xl:min-h-24">
+        {viewableCategories.map((o, idx) => (
+          <Link
+            href={
+              inDetailsView
+                ? `/results/details?&category=${currentlyActivecategory}${search}${details}`
+                : `/results?category=${o.id}${search}`
+            }
+            shallow
+            replace={!!(inDetailsView && activeCategory?.id !== currentlyActivecategory)}
+            onClick={() => setActiveCategory(o)}
+            key={idx}
+            className={`${
+              o.id === activeCategory?.id
+                ? "font-semibold underline decoration-secondary-helmholtz-mint underline-offset-[20px]"
+                : "text-blue-100 hover:rounded-full hover:bg-white/[0.12] hover:font-medium hover:py-3 hover:px-2"
+            } flex place-content-center cursor-pointer text-md w-full flex-1 py-2 font-normal text-primary-helmholtz-weiss `}
+          >
+            <div className="xl:basis-1/4 2xl:basis-1/5 mr-2 ">
+              {/* count indicator for category */}
+              <span className="inline-flex h-11 w-11 place-content-center rounded-full bg-primary-helmholtz-hellblau object-contain align-middle">
+                <span className="self-center font-medium text-primary-helmholtz-weiss">
+                  {formatter.format(o.count)}
                 </span>
-              </div>
-              <div className="basis-2/3 self-center text-left">{c(o.id, { count: o.count })}</div>
-            </Link>
-          ))}
-        </div>
-      )}
+              </span>
+            </div>
+            <div className="basis-2/3 self-center text-left">{c(o.id, { count: o.count })}</div>
+          </Link>
+        ))}
+      </div>
     </>
   );
 };
diff --git a/src/components/app/results/Details/RelatedResults.tsx b/src/components/app/results/Details/RelatedResults.tsx
index d322c8c..e50b970 100644
--- a/src/components/app/results/Details/RelatedResults.tsx
+++ b/src/components/app/results/Details/RelatedResults.tsx
@@ -11,7 +11,7 @@ import CategoryBar from "../CategoryBar";
 
 const RelatedResults = () => {
   const d = useTranslations("DetailsView");
-  const [categoryLoading, setCategoryLoading] = useState(true);
+  const [categoryLoading, setCategoryLoading] = useState(false);
   const [resultsLoading, setResultsLoading] = useState(true);
   const [hasBcError, setHasBcError] = useState(false);
   const [hasSearchError, setHasSearchError] = useState(false);
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index c8710d5..30a116b 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -16,7 +16,8 @@ export interface CategoryContextType {
   setRelatedActiveCategory: (category: Category) => void;
 }
 
-const defaultCategoryContext: CategoryContextType = {
+// TODO: move this to config.ts
+const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
   viewableCategories: [],
   setViewableCategories: () => {},
   primaryActiveCategory: null,
@@ -26,7 +27,7 @@ const defaultCategoryContext: CategoryContextType = {
 };
 
 // Creating a context for the category
-export const CategoryContext = createContext<CategoryContextType>(defaultCategoryContext);
+export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGORY_CONTEXT);
 
 // Creating a provider for the category
 export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
@@ -74,6 +75,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   }, []);
 
   useEffect(() => {
+    console.log("Setting in details to calculate appropriate derived values");
     if (detailsText) {
       setInDetailsView(true);
     } else {
@@ -81,11 +83,13 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [detailsText]);
 
+  // FIND OUT: WHY DO THE FOLLOWING TO EFFECTS RUN WHEN IN THE LANDING PAGE?
   useEffect(() => {
     const fetchCategoriesForCategoryBar = async () => {
       console.log("Fetching categories for category bar from context");
       try {
         const baseCategories = await getAllCategories();
+        // calling this here for completeness of information though its not necessarily used in the UI right now
         const categoriesWithIcons = await setCategoryImages(baseCategories);
         if (inDetailsView && primaryActiveCategory) {
           setBaseCategories(
-- 
GitLab


From ee8527d74a9d8d3ba5f7321f6b721f0fd481a7a4 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 20 Feb 2025 15:37:04 +0100
Subject: [PATCH 04/48] displayed updated category bar on change of details
 object

---
 src/app/[locale]/results/page.tsx             |  14 +--
 src/components/app/Categories.tsx             |   2 -
 src/components/app/results/CategoryBar.tsx    | 115 +++---------------
 .../app/results/Details/DetailsItemCard.tsx   |   2 +-
 .../app/results/Details/RelatedResults.tsx    |  30 +++--
 src/components/app/results/ListResults.tsx    |   2 +
 .../app/results/ListResults/ResultItem.tsx    |  33 +++--
 src/contexts/CategoryContext.tsx              |  25 ++--
 8 files changed, 83 insertions(+), 140 deletions(-)

diff --git a/src/app/[locale]/results/page.tsx b/src/app/[locale]/results/page.tsx
index 9de1d05..5c3bdb2 100644
--- a/src/app/[locale]/results/page.tsx
+++ b/src/app/[locale]/results/page.tsx
@@ -30,13 +30,13 @@ const ResultsComponent = () => {
       <>
         <CategoryBar
           activeCategory={activeCategory}
-          categoryLoading={categoryLoading}
-          setCategoryLoading={setCategoryLoading}
-          hasBcError={hasBcError}
-          hasSearchError={hasSearchError}
-          setActiveCategory={setActiveCategory}
-          setHasBcError={setHasBcError}
-          setHasSearchError={setHasSearchError}
+          // categoryLoading={categoryLoading}
+          // setCategoryLoading={setCategoryLoading}
+          // hasBcError={hasBcError}
+          // hasSearchError={hasSearchError}
+          // setActiveCategory={setActiveCategory}
+          // setHasBcError={setHasBcError}
+          // setHasSearchError={setHasSearchError}
         />
         <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
           {!categoryLoading && !resultsLoading ? (
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index ab4c1e8..2bd9ab3 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -17,8 +17,6 @@ const Categories = () => {
   const [isLoading, setIsLoading] = useState<boolean>(false);
   const [hasError, setHasError] = useState<boolean>(false);
 
-  console.log("context categories via API in Categories.tsx:", viewableCategories);
-
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
   /* traditional JS module that makes the badge numbers appear as strings representing exponents
       check - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options */
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index 7f735fc..280656e 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -2,7 +2,7 @@
 
 import { useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { Dispatch, SetStateAction, Suspense, useEffect, useContext } from "react";
+import { Suspense, useContext } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n";
@@ -10,31 +10,12 @@ import type { Category } from "@/types/types";
 
 type Props = {
   activeCategory?: Category;
-  categoryLoading: boolean;
-  hasBcError: boolean;
-  hasSearchError: boolean;
-  inDetailsView?: boolean;
-  setActiveCategory: Dispatch<SetStateAction<Category | undefined>>;
-  setHasBcError: Dispatch<SetStateAction<boolean>>;
-  setHasSearchError: Dispatch<SetStateAction<boolean>>;
-  setCategoryLoading: Dispatch<SetStateAction<boolean>>;
 };
 
-const CategoryBarComponent = ({
-  activeCategory,
-  categoryLoading,
-  hasBcError,
-  hasSearchError,
-  inDetailsView,
-  setActiveCategory,
-  setCategoryLoading,
-  setHasBcError,
-  setHasSearchError,
-}: Props) => {
+const CategoryBarComponent = ({ activeCategory }: Props) => {
   const c = useTranslations("Categories");
 
   const resultParams = useSearchParams();
-  const currentlyActivecategory = resultParams.get("category") ?? undefined;
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
   const details = detailsText ? `&detailsText=${detailsText}` : "";
@@ -42,83 +23,13 @@ const CategoryBarComponent = ({
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
 
-  // const [categories, setCategories] = useState<Category[]>([]);
-  // const [baseCategories, setBaseCategories] = useState<Category[]>([]);
-
-  const { viewableCategories, primaryActiveCategory } = useContext(CategoryContext);
-
-  console.log("context categories via API:", viewableCategories, primaryActiveCategory);
-
-  // const firstRender = useRef(true);
-
-  // useEffect(() => {
-  //   const loadCategories = async () => {
-  //     try {
-  //       const allCategories = await getAllCategories();
-  //       if (inDetailsView && currentlyActivecategory) {
-  //         setBaseCategories(allCategories.filter((cat) => cat.id !== currentlyActivecategory));
-  //       } else {
-  //         setBaseCategories(allCategories);
-  //       }
-  //     } catch (error) {
-  //       setHasBcError(true);
-  //     }
-  //   };
-
-  //   loadCategories().catch(console.error);
-  // }, [inDetailsView, currentlyActivecategory, setHasBcError]);
-
-  useEffect(() => {
-    console.log("being called now");
-    if (!activeCategory && viewableCategories.length) {
-      setActiveCategory(viewableCategories[0]);
-    }
-  }, [activeCategory, viewableCategories, setActiveCategory]);
-
-  useEffect(() => {
-    console.log("being called now 2");
-    viewableCategories.forEach((cat: Category) =>
-      cat.id === currentlyActivecategory ? setActiveCategory(cat) : null
-    );
-    inDetailsView && setActiveCategory(viewableCategories[0]);
-  }, [currentlyActivecategory, viewableCategories, inDetailsView, setActiveCategory]);
-
-  // useEffect(() => {
-  //   const updateCategoryCount = async () => {
-  //     try {
-  //       setHasSearchError(false);
-  //       const updatedSearchText =
-  //         searchText || detailsText
-  //           ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
-  //           : undefined;
-
-  //       const categoryCounts = await getCategoryCounts(updatedSearchText);
-  //       const categories = structuredClone(baseCategories);
-
-  //       categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
-  //       setCategories(categories);
-  //     } catch (error) {
-  //       console.error("Failed to fetch category counts", error);
-  //       setHasSearchError(true);
-  //     } finally {
-  //       setCategoryLoading(false);
-  //       // console.log("here", categories);
-  //     }
-  //   };
-
-  //   if (firstRender.current) {
-  //     firstRender.current = false;
-  //   } else {
-  //     updateCategoryCount().catch(console.error);
-  //   }
-  // }, [
-  //   baseCategories,
-  //   searchText,
-  //   detailsText,
-  //   setCategoryLoading,
-  //   setHasSearchError,
-  //   setHasBcError,
-  // ]);
+  const {
+    viewableCategories,
+    primaryActiveCategory,
+    inDetailsView,
+    setPrimaryActiveCategory,
+    setRelatedActiveCategory,
+  } = useContext(CategoryContext);
 
   return (
     <>
@@ -127,12 +38,14 @@ const CategoryBarComponent = ({
           <Link
             href={
               inDetailsView
-                ? `/results/details?&category=${currentlyActivecategory}${search}${details}`
+                ? `/results/details?&category=${primaryActiveCategory?.id}${search}${details}`
                 : `/results?category=${o.id}${search}`
             }
             shallow
-            replace={!!(inDetailsView && activeCategory?.id !== currentlyActivecategory)}
-            onClick={() => setActiveCategory(o)}
+            replace={!!(inDetailsView && activeCategory?.id !== primaryActiveCategory?.id)}
+            onClick={() =>
+              inDetailsView ? setRelatedActiveCategory(o) : setPrimaryActiveCategory(o)
+            }
             key={idx}
             className={`${
               o.id === activeCategory?.id
diff --git a/src/components/app/results/Details/DetailsItemCard.tsx b/src/components/app/results/Details/DetailsItemCard.tsx
index 56ab0cd..2e5266c 100644
--- a/src/components/app/results/Details/DetailsItemCard.tsx
+++ b/src/components/app/results/Details/DetailsItemCard.tsx
@@ -24,7 +24,7 @@ const DetailsItemCard = () => {
     <>
       {isResultItem(detailsItem) ? (
         <div className="p-4">
-          <ResultItem item={detailsItem} idx={detailsItem.id} inDetailsPage />
+          <ResultItem item={detailsItem} idx={detailsItem.id} isDetailsCard />
         </div>
       ) : null}
     </>
diff --git a/src/components/app/results/Details/RelatedResults.tsx b/src/components/app/results/Details/RelatedResults.tsx
index e50b970..9cf516f 100644
--- a/src/components/app/results/Details/RelatedResults.tsx
+++ b/src/components/app/results/Details/RelatedResults.tsx
@@ -1,21 +1,28 @@
 "use client";
 import { useTranslations } from "next-intl";
-import React, { useState } from "react";
+import React, { useState, useEffect, useContext } from "react";
 
+import CategoryBar from "@/components/app/results/CategoryBar";
 import ListResults from "@/components/app/results/ListResults";
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Category } from "@/types/types";
 
-import CategoryBar from "../CategoryBar";
-
 const RelatedResults = () => {
   const d = useTranslations("DetailsView");
   const [categoryLoading, setCategoryLoading] = useState(false);
-  const [resultsLoading, setResultsLoading] = useState(true);
+  const [resultsLoading, setResultsLoading] = useState(false);
   const [hasBcError, setHasBcError] = useState(false);
   const [hasSearchError, setHasSearchError] = useState(false);
   const [activeCategory, setActiveCategory] = useState<Category>();
+  const { relatedActiveCategory } = useContext(CategoryContext);
+
+  useEffect(() => {
+    if (relatedActiveCategory !== null) {
+      setActiveCategory(relatedActiveCategory);
+    }
+  }, [relatedActiveCategory]);
 
   return (
     <div className="m-4 space-y-2 rounded-xl border-2 border-primary-helmholtz-hellblau/20 px-5 py-4 shadow-lg shadow-secondary-helmholtz-highlightblau bg-white">
@@ -27,14 +34,13 @@ const RelatedResults = () => {
         ) : null}
         <CategoryBar
           activeCategory={activeCategory}
-          categoryLoading={categoryLoading}
-          setCategoryLoading={setCategoryLoading}
-          hasBcError={hasBcError}
-          hasSearchError={hasSearchError}
-          setActiveCategory={setActiveCategory}
-          setHasBcError={setHasBcError}
-          setHasSearchError={setHasSearchError}
-          inDetailsView
+          // categoryLoading={categoryLoading}
+          // setCategoryLoading={setCategoryLoading}
+          // hasBcError={hasBcError}
+          // hasSearchError={hasSearchError}
+          // setActiveCategory={setActiveCategory}
+          // setHasBcError={setHasBcError}
+          // setHasSearchError={setHasSearchError}
         />
         {activeCategory && (
           <ListResults
diff --git a/src/components/app/results/ListResults.tsx b/src/components/app/results/ListResults.tsx
index d58ca94..b6fd1d1 100644
--- a/src/components/app/results/ListResults.tsx
+++ b/src/components/app/results/ListResults.tsx
@@ -171,6 +171,8 @@ const ListResultsComponent = ({ activeCategory, resultsLoading, setResultsLoadin
                     activeCategory={activeCategory}
                     searchText={searchText}
                     associatedToFilters={availableFilters.length !== 0}
+                    isDetailsCard={undefined}
+                    // TODO: handle the detailsText prop to ResultItem
                   />
                 ))
               ) : (
diff --git a/src/components/app/results/ListResults/ResultItem.tsx b/src/components/app/results/ListResults/ResultItem.tsx
index dc194dc..fa49ebe 100644
--- a/src/components/app/results/ListResults/ResultItem.tsx
+++ b/src/components/app/results/ListResults/ResultItem.tsx
@@ -4,11 +4,12 @@ import { useSetAtom } from "jotai";
 import Image from "next/image";
 import { useRouter } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useRef, useState, useContext } from "react";
 
 import { INSTITUTE_ROR_LOGOS } from "@/app/api/config/config";
 import ExternalLink from "@/components/ExternalLink";
 import Relationships from "@/components/app/results/ListResults/ResultItem/Relationships";
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n";
 import ExternalLinkIcon from "@/resources/images/svg/ExternalLinkIcon";
 import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
@@ -20,8 +21,8 @@ type Props = {
   idx: string;
   searchText?: string;
   activeCategory?: Category;
-  inDetailsPage?: boolean;
   associatedToFilters?: boolean;
+  isDetailsCard?: boolean; //TODO: Redesign code to better handle styling of result item in details page and list view. Reassess how this is used for styling and replace it with better variable name
 };
 
 const ResultItem = ({
@@ -29,8 +30,8 @@ const ResultItem = ({
   idx,
   searchText,
   activeCategory,
-  inDetailsPage,
   associatedToFilters,
+  isDetailsCard,
 }: Props) => {
   const setDetailsItem = useSetAtom(detailsItemAtom);
   const router = useRouter();
@@ -43,6 +44,9 @@ const ResultItem = ({
   const [titleIconHref, setTitleIconHref] = useState<string>("");
   const [logoHref, setLogoHref] = useState<string>("");
 
+  const { inDetailsView, setPrimaryActiveCategory, setRelatedActiveCategory } =
+    useContext(CategoryContext);
+
   useEffect(() => {
     const element = descRef.current;
     if (element && element.offsetHeight && element.scrollHeight) {
@@ -126,6 +130,15 @@ const ResultItem = ({
 
   const handleDetailSearch = () => {
     setDetailsItem((prev) => [...prev, ...[item]]);
+    //technically in details view activeCategory is not undefined. But this component is reused for the details card
+    // for which the activeCategory is supposed to be undefined. So we check for undefined here to appease the Gods of Typescript
+    if (inDetailsView && activeCategory) {
+      // setting currently chosen relatedActiveCategory, which in this case is the activeCategory, as the primaryActiveCategory
+      setPrimaryActiveCategory(activeCategory);
+      // cleaning the slate
+      setRelatedActiveCategory(null);
+    }
+
     router.push(
       `/results/details?category=${activeCategory?.id}${searchText ? `&searchText=${searchText}` : ``}&detailsText=${item.name}`
     );
@@ -133,9 +146,9 @@ const ResultItem = ({
 
   return (
     <div
-      className={`flex my-2 space-y-2 rounded-xl border-2 border-primary-helmholtz-hellblau/20 p-5 shadow-lg shadow-secondary-helmholtz-highlightblau bg-white size-auto ${inDetailsPage && logoHref !== "" ? "xl:gap-x-10" : "xl:flex-col"}`}
+      className={`flex my-2 space-y-2 rounded-xl border-2 border-primary-helmholtz-hellblau/20 p-5 shadow-lg shadow-secondary-helmholtz-highlightblau bg-white size-auto ${isDetailsCard && logoHref !== "" ? "xl:gap-x-10" : "xl:flex-col"}`}
     >
-      {inDetailsPage && logoHref !== "" ? (
+      {isDetailsCard && logoHref !== "" ? (
         <div className="hidden xl:space-y-2 xl:p-5 xl:mt-2 xl:basis-1/3 xl:flex">
           <Image
             src={logoHref}
@@ -149,12 +162,12 @@ const ResultItem = ({
 
       <div
         key={idx}
-        className={`flex flex-col my-2 space-y-2 p-5 ${inDetailsPage && logoHref !== "" ? "xl:basis-2/3" : ""} `}
+        className={`flex flex-col my-2 space-y-2 p-5 ${isDetailsCard && logoHref !== "" ? "xl:basis-2/3" : ""} `}
       >
         <button
-          className={`${inDetailsPage ? "" : "hover:transition-all hover:translate-x-3"} inline-flex align-middle items-center pb-2 text-primary-helmholtz-dunkelblau ${associatedToFilters ? "" : "inline-flex"} ${inDetailsPage ? "mb-5" : ""}`}
+          className={`${isDetailsCard ? "" : "hover:transition-all hover:translate-x-3"} inline-flex align-middle items-center pb-2 text-primary-helmholtz-dunkelblau ${associatedToFilters ? "" : "inline-flex"} ${isDetailsCard ? "mb-5" : ""}`}
           onClick={handleDetailSearch}
-          disabled={inDetailsPage}
+          disabled={isDetailsCard}
         >
           {titleIconHref ? (
             <span className="mr-1">
@@ -168,12 +181,12 @@ const ResultItem = ({
             </span>
           ) : null}
           <p
-            className={`${inDetailsPage ? "inline-flex text-2xl underline underline-offset-[6px] xl:underline-offset-[8px] decoration-primary-helmholtz-hellblau" : "truncate"} font-semibold text-left w-[98%]`}
+            className={`${isDetailsCard ? "inline-flex text-2xl underline underline-offset-[6px] xl:underline-offset-[8px] decoration-primary-helmholtz-hellblau" : "truncate"} font-semibold text-left w-[98%]`}
             rel="noreferrer"
           >
             {item.name}
           </p>
-          {inDetailsPage ?? <RightArrowIcon />}
+          {isDetailsCard ?? <RightArrowIcon />}
         </button>
 
         <div className="text-sm inline-flex flex-wrap items-center gap-x-1">
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 30a116b..d925fd4 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -13,7 +13,8 @@ export interface CategoryContextType {
   primaryActiveCategory: Category | null;
   setPrimaryActiveCategory: (category: Category) => void;
   relatedActiveCategory: Category | null;
-  setRelatedActiveCategory: (category: Category) => void;
+  setRelatedActiveCategory: (category: Category | null) => void;
+  inDetailsView?: boolean;
 }
 
 // TODO: move this to config.ts
@@ -24,6 +25,7 @@ const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
   setPrimaryActiveCategory: () => {},
   relatedActiveCategory: null,
   setRelatedActiveCategory: () => {},
+  inDetailsView: false,
 };
 
 // Creating a context for the category
@@ -35,14 +37,14 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const [viewableCategories, setViewableCategories] = useState<Category[]>([]);
   const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
   const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
+  const [inDetailsView, setInDetailsView] = useState<boolean>(false);
 
   // State for processing
   const resultParams = useSearchParams();
-  // const currentlyActivecategory = resultParams.get("category") ?? undefined;
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
+
   const [baseCategories, setBaseCategories] = useState<Category[]>([]);
-  const [inDetailsView, setInDetailsView] = useState<boolean>(false);
 
   const setCategoryImages = async (categories: Category[]) => {
     const categoriesClone = structuredClone(categories);
@@ -62,7 +64,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
 
   useEffect(() => {
     const fetchCategoriesForLandingPage = async () => {
-      console.log("Fetching categories in context");
+      // console.log("Fetching categories in context");
       try {
         const baseCategories = await getAllCategories();
         const categories = await setCategoryImages(baseCategories);
@@ -75,7 +77,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   }, []);
 
   useEffect(() => {
-    console.log("Setting in details to calculate appropriate derived values");
+    // console.log("Setting in details to calculate appropriate derived values");
     if (detailsText) {
       setInDetailsView(true);
     } else {
@@ -86,7 +88,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   // FIND OUT: WHY DO THE FOLLOWING TO EFFECTS RUN WHEN IN THE LANDING PAGE?
   useEffect(() => {
     const fetchCategoriesForCategoryBar = async () => {
-      console.log("Fetching categories for category bar from context");
+      // console.log("Fetching categories for category bar from context");
       try {
         const baseCategories = await getAllCategories();
         // calling this here for completeness of information though its not necessarily used in the UI right now
@@ -108,7 +110,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
 
   useEffect(() => {
     const fetchAndUpdateCategoryBarCount = async () => {
-      console.log("Fetching category counts for category bar from context");
+      // console.log("Fetching category counts for category bar from context");
       try {
         const updatedSearchText =
           searchText || detailsText
@@ -128,6 +130,14 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     fetchAndUpdateCategoryBarCount();
   }, [searchText, detailsText, baseCategories]);
 
+  useEffect(() => {
+    if (inDetailsView && relatedActiveCategory === null) {
+      console.log("Setting related active category to first viewable category", viewableCategories);
+
+      setRelatedActiveCategory(viewableCategories[0]);
+    }
+  }, [viewableCategories, inDetailsView, relatedActiveCategory]);
+
   return (
     <CategoryContext.Provider
       value={{
@@ -137,6 +147,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         setPrimaryActiveCategory,
         relatedActiveCategory,
         setRelatedActiveCategory,
+        inDetailsView,
       }}
     >
       {children}
-- 
GitLab


From 58a6a30f054670321f94c1add784e2f241df38ee Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 20 Feb 2025 16:57:16 +0100
Subject: [PATCH 05/48] extracted all category context and made the category
 based components derive state from it

---
 src/app/[locale]/results/page.tsx             | 15 ++---
 src/app/api/config/config.ts                  | 11 ++++
 src/components/app/Categories.tsx             | 12 +++-
 src/components/app/results/CategoryBar.tsx    |  4 +-
 .../app/results/Details/RelatedResults.tsx    | 17 ++----
 src/components/app/results/ListResults.tsx    |  2 -
 .../app/results/ListResults/ResultItem.tsx    |  7 +--
 src/contexts/CategoryContext.tsx              | 58 ++++++-------------
 src/types/types.ts                            | 10 ++++
 9 files changed, 63 insertions(+), 73 deletions(-)

diff --git a/src/app/[locale]/results/page.tsx b/src/app/[locale]/results/page.tsx
index 5c3bdb2..5a3dd66 100644
--- a/src/app/[locale]/results/page.tsx
+++ b/src/app/[locale]/results/page.tsx
@@ -13,11 +13,13 @@ import type { Category } from "@/types/types";
 
 const ResultsComponent = () => {
   const { primaryActiveCategory } = useContext(CategoryContext);
+  const [activeCategory, setActiveCategory] = useState<Category>();
+
+  // Following will be refactored in a new MR that looks in loading and error state handling
   const [hasBcError, setHasBcError] = useState(false);
   const [hasSearchError, setHasSearchError] = useState(false);
   const [categoryLoading, setCategoryLoading] = useState(false);
   const [resultsLoading, setResultsLoading] = useState(true);
-  const [activeCategory, setActiveCategory] = useState<Category>();
 
   useEffect(() => {
     if (primaryActiveCategory !== null) {
@@ -28,16 +30,7 @@ const ResultsComponent = () => {
   return (
     <div className="grow flex flex-col justify-start">
       <>
-        <CategoryBar
-          activeCategory={activeCategory}
-          // categoryLoading={categoryLoading}
-          // setCategoryLoading={setCategoryLoading}
-          // hasBcError={hasBcError}
-          // hasSearchError={hasSearchError}
-          // setActiveCategory={setActiveCategory}
-          // setHasBcError={setHasBcError}
-          // setHasSearchError={setHasSearchError}
-        />
+        <CategoryBar activeCategory={activeCategory} />
         <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
           {!categoryLoading && !resultsLoading ? (
             <>
diff --git a/src/app/api/config/config.ts b/src/app/api/config/config.ts
index 30a16bb..109363e 100644
--- a/src/app/api/config/config.ts
+++ b/src/app/api/config/config.ts
@@ -1,5 +1,6 @@
 import type {
   Category,
+  CategoryContextType,
   FacetIntervalFieldKeys,
   FacetFieldKeys,
   SOLRCategories,
@@ -59,6 +60,16 @@ export const INITIAL_CATECORY_COUNT_MAP: Record<FacetFieldKeys, number> = {
   instruments: 0,
 };
 
+export const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
+  visibleCategories: [],
+  setVisibleCategories: () => {},
+  primaryActiveCategory: null,
+  setPrimaryActiveCategory: () => {},
+  relatedActiveCategory: null,
+  setRelatedActiveCategory: () => {},
+  inDetailsView: false,
+};
+
 export const AVAILABLE_FACETS: string[] = [
   "txt_knowsAbout",
   "txt_knowsLanguage",
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index 2bd9ab3..229b792 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -2,7 +2,7 @@
 
 import Image from "next/image";
 import { useTranslations } from "next-intl";
-import { useState, useContext } from "react";
+import { useState, useContext, useEffect } from "react";
 
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
@@ -11,7 +11,13 @@ import { Link } from "@/i18n";
 
 const Categories = () => {
   const t = useTranslations("Categories");
-  const { viewableCategories, setPrimaryActiveCategory } = useContext(CategoryContext);
+  const { visibleCategories, setPrimaryActiveCategory, setRelatedActiveCategory } =
+    useContext(CategoryContext);
+
+  useEffect(() => {
+    setRelatedActiveCategory(null);
+    setPrimaryActiveCategory(null);
+  }, [setPrimaryActiveCategory, setRelatedActiveCategory]);
 
   // TODO: handle loading and error states
   const [isLoading, setIsLoading] = useState<boolean>(false);
@@ -34,7 +40,7 @@ const Categories = () => {
           </div>
           {/* grid of categories */}
           <div className="px-10 grid grid-cols-1  md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 gap-y-28 md:gap-x-16 lg:gap-x-32 xl:gap-x-46 content-center justify-center">
-            {viewableCategories.map((o) => {
+            {visibleCategories.map((o) => {
               return (
                 <Link
                   key={o.id}
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index 280656e..bd365dc 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -24,7 +24,7 @@ const CategoryBarComponent = ({ activeCategory }: Props) => {
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
 
   const {
-    viewableCategories,
+    visibleCategories,
     primaryActiveCategory,
     inDetailsView,
     setPrimaryActiveCategory,
@@ -34,7 +34,7 @@ const CategoryBarComponent = ({ activeCategory }: Props) => {
   return (
     <>
       <div className="bg-primary-helmholtz-dunkelblau text-primary-helmholtz-weiss grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:flex gap-y-2 gap-x-2 xl:gap-x-6 px-4 pt-2 pb-3 md:px-10 lg:px-16 xl:pt-4 xl:min-h-24">
-        {viewableCategories.map((o, idx) => (
+        {visibleCategories.map((o, idx) => (
           <Link
             href={
               inDetailsView
diff --git a/src/components/app/results/Details/RelatedResults.tsx b/src/components/app/results/Details/RelatedResults.tsx
index 9cf516f..5ad0e95 100644
--- a/src/components/app/results/Details/RelatedResults.tsx
+++ b/src/components/app/results/Details/RelatedResults.tsx
@@ -11,12 +11,14 @@ import { Category } from "@/types/types";
 
 const RelatedResults = () => {
   const d = useTranslations("DetailsView");
+  const { relatedActiveCategory } = useContext(CategoryContext);
+  const [activeCategory, setActiveCategory] = useState<Category>();
+
+  // Following will be refactored in a new MR that looks in loading and error state handling
   const [categoryLoading, setCategoryLoading] = useState(false);
   const [resultsLoading, setResultsLoading] = useState(false);
   const [hasBcError, setHasBcError] = useState(false);
   const [hasSearchError, setHasSearchError] = useState(false);
-  const [activeCategory, setActiveCategory] = useState<Category>();
-  const { relatedActiveCategory } = useContext(CategoryContext);
 
   useEffect(() => {
     if (relatedActiveCategory !== null) {
@@ -32,16 +34,7 @@ const RelatedResults = () => {
             {d("other_items")}
           </p>
         ) : null}
-        <CategoryBar
-          activeCategory={activeCategory}
-          // categoryLoading={categoryLoading}
-          // setCategoryLoading={setCategoryLoading}
-          // hasBcError={hasBcError}
-          // hasSearchError={hasSearchError}
-          // setActiveCategory={setActiveCategory}
-          // setHasBcError={setHasBcError}
-          // setHasSearchError={setHasSearchError}
-        />
+        <CategoryBar activeCategory={activeCategory} />
         {activeCategory && (
           <ListResults
             activeCategory={activeCategory}
diff --git a/src/components/app/results/ListResults.tsx b/src/components/app/results/ListResults.tsx
index b6fd1d1..d58ca94 100644
--- a/src/components/app/results/ListResults.tsx
+++ b/src/components/app/results/ListResults.tsx
@@ -171,8 +171,6 @@ const ListResultsComponent = ({ activeCategory, resultsLoading, setResultsLoadin
                     activeCategory={activeCategory}
                     searchText={searchText}
                     associatedToFilters={availableFilters.length !== 0}
-                    isDetailsCard={undefined}
-                    // TODO: handle the detailsText prop to ResultItem
                   />
                 ))
               ) : (
diff --git a/src/components/app/results/ListResults/ResultItem.tsx b/src/components/app/results/ListResults/ResultItem.tsx
index fa49ebe..e40ba8c 100644
--- a/src/components/app/results/ListResults/ResultItem.tsx
+++ b/src/components/app/results/ListResults/ResultItem.tsx
@@ -22,7 +22,7 @@ type Props = {
   searchText?: string;
   activeCategory?: Category;
   associatedToFilters?: boolean;
-  isDetailsCard?: boolean; //TODO: Redesign code to better handle styling of result item in details page and list view. Reassess how this is used for styling and replace it with better variable name
+  isDetailsCard?: boolean;
 };
 
 const ResultItem = ({
@@ -130,12 +130,11 @@ const ResultItem = ({
 
   const handleDetailSearch = () => {
     setDetailsItem((prev) => [...prev, ...[item]]);
-    //technically in details view activeCategory is not undefined. But this component is reused for the details card
-    // for which the activeCategory is supposed to be undefined. So we check for undefined here to appease the Gods of Typescript
+
     if (inDetailsView && activeCategory) {
       // setting currently chosen relatedActiveCategory, which in this case is the activeCategory, as the primaryActiveCategory
       setPrimaryActiveCategory(activeCategory);
-      // cleaning the slate
+      // cleaning the slate on relatedActiveCategory
       setRelatedActiveCategory(null);
     }
 
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index d925fd4..e0aca39 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -2,39 +2,17 @@
 import { useSearchParams } from "next/navigation";
 import { createContext, useState, useEffect } from "react";
 
-import { Category } from "@/types/types";
+import { DEFAULT_CATEGORY_CONTEXT } from "@/app/api/config/config";
+import { Category, CategoryContextType } from "@/types/types";
 import { getAllCategories, getCategoryCounts } from "@/utils/apiCall";
 
-// Defining the type of the context
-// TODO: move this to types.ts
-export interface CategoryContextType {
-  viewableCategories: Category[];
-  setViewableCategories: (categories: Category[]) => void;
-  primaryActiveCategory: Category | null;
-  setPrimaryActiveCategory: (category: Category) => void;
-  relatedActiveCategory: Category | null;
-  setRelatedActiveCategory: (category: Category | null) => void;
-  inDetailsView?: boolean;
-}
-
-// TODO: move this to config.ts
-const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
-  viewableCategories: [],
-  setViewableCategories: () => {},
-  primaryActiveCategory: null,
-  setPrimaryActiveCategory: () => {},
-  relatedActiveCategory: null,
-  setRelatedActiveCategory: () => {},
-  inDetailsView: false,
-};
-
 // Creating a context for the category
 export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGORY_CONTEXT);
 
 // Creating a provider for the category
 export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
   // The state that will be shared
-  const [viewableCategories, setViewableCategories] = useState<Category[]>([]);
+  const [visibleCategories, setVisibleCategories] = useState<Category[]>([]);
   const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
   const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
   const [inDetailsView, setInDetailsView] = useState<boolean>(false);
@@ -46,6 +24,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
 
   const [baseCategories, setBaseCategories] = useState<Category[]>([]);
 
+  // Function to set the category images
   const setCategoryImages = async (categories: Category[]) => {
     const categoriesClone = structuredClone(categories);
     for (const category of categoriesClone) {
@@ -62,13 +41,14 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     return categoriesClone;
   };
 
+  // Effects affecting the Landing page actions related to categories
+
   useEffect(() => {
     const fetchCategoriesForLandingPage = async () => {
-      // console.log("Fetching categories in context");
       try {
         const baseCategories = await getAllCategories();
         const categories = await setCategoryImages(baseCategories);
-        setViewableCategories(categories);
+        setVisibleCategories(categories);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
       }
@@ -77,7 +57,12 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   }, []);
 
   useEffect(() => {
-    // console.log("Setting in details to calculate appropriate derived values");
+    setPrimaryActiveCategory(visibleCategories[0]);
+  }, [searchText]);
+
+  // Effects affecting the CategoryBar component
+
+  useEffect(() => {
     if (detailsText) {
       setInDetailsView(true);
     } else {
@@ -85,10 +70,8 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [detailsText]);
 
-  // FIND OUT: WHY DO THE FOLLOWING TO EFFECTS RUN WHEN IN THE LANDING PAGE?
   useEffect(() => {
     const fetchCategoriesForCategoryBar = async () => {
-      // console.log("Fetching categories for category bar from context");
       try {
         const baseCategories = await getAllCategories();
         // calling this here for completeness of information though its not necessarily used in the UI right now
@@ -110,7 +93,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
 
   useEffect(() => {
     const fetchAndUpdateCategoryBarCount = async () => {
-      // console.log("Fetching category counts for category bar from context");
       try {
         const updatedSearchText =
           searchText || detailsText
@@ -121,7 +103,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         const categories = structuredClone(baseCategories);
 
         categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
-        setViewableCategories(categories);
+        setVisibleCategories(categories);
       } catch (error) {
         console.error("Failed to fetch category counts for categorybar", error);
       }
@@ -131,18 +113,16 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   }, [searchText, detailsText, baseCategories]);
 
   useEffect(() => {
-    if (inDetailsView && relatedActiveCategory === null) {
-      console.log("Setting related active category to first viewable category", viewableCategories);
-
-      setRelatedActiveCategory(viewableCategories[0]);
+    if (inDetailsView) {
+      setRelatedActiveCategory(visibleCategories[0]);
     }
-  }, [viewableCategories, inDetailsView, relatedActiveCategory]);
+  }, [visibleCategories, inDetailsView]);
 
   return (
     <CategoryContext.Provider
       value={{
-        viewableCategories,
-        setViewableCategories,
+        visibleCategories,
+        setVisibleCategories,
         primaryActiveCategory,
         setPrimaryActiveCategory,
         relatedActiveCategory,
diff --git a/src/types/types.ts b/src/types/types.ts
index fb271d2..0804134 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -183,6 +183,16 @@ export type Category = {
   count: number;
 };
 
+export interface CategoryContextType {
+  visibleCategories: Category[];
+  setVisibleCategories: (categories: Category[]) => void;
+  primaryActiveCategory: Category | null;
+  setPrimaryActiveCategory: (category: Category | null) => void;
+  relatedActiveCategory: Category | null;
+  setRelatedActiveCategory: (category: Category | null) => void;
+  inDetailsView?: boolean;
+}
+
 // -------- SEARCH API ---------
 export const searchSchema = z.object({
   searchText: z.string().optional(),
-- 
GitLab


From 19a97b81f5cebf5c910c990033c31b08b3a9b820 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 20 Feb 2025 17:03:24 +0100
Subject: [PATCH 06/48] note to fix es-lint warning

---
 src/contexts/CategoryContext.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index e0aca39..f48220c 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -56,6 +56,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     fetchCategoriesForLandingPage();
   }, []);
 
+  // TODO: ES-lint says use useReducer to do what I am doing here. Implement it
   useEffect(() => {
     setPrimaryActiveCategory(visibleCategories[0]);
   }, [searchText]);
-- 
GitLab


From 5b976f851bdcb70a90aa88b996deadcad5ceff12 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Fri, 21 Feb 2025 14:25:44 +0100
Subject: [PATCH 07/48] fixed a paragraph within paragraph warning and a
 useEffect to reset primaryCategory on navigation to landing page

---
 .../ListResults/ResultItem/Relationships.tsx  |  4 +-
 src/components/layout/Search.tsx              | 25 ++++++++++--
 src/contexts/CategoryContext.tsx              | 39 +++++++++----------
 3 files changed, 41 insertions(+), 27 deletions(-)

diff --git a/src/components/app/results/ListResults/ResultItem/Relationships.tsx b/src/components/app/results/ListResults/ResultItem/Relationships.tsx
index e813e47..655ff2c 100644
--- a/src/components/app/results/ListResults/ResultItem/Relationships.tsx
+++ b/src/components/app/results/ListResults/ResultItem/Relationships.tsx
@@ -35,7 +35,7 @@ const Relationships = ({ filteredArr, type }: Props) => {
     <div className="mt-2 cursor-default">
       <span className="font-semibold">{s(`${type.toLowerCase()}`)}: </span>
 
-      <p ref={relRef} className={relShowMore ? "" : "line-clamp-2"}>
+      <div ref={relRef} className={relShowMore ? "" : "line-clamp-2"}>
         {filteredArr.map((displayItem, idx: number) => {
           const iconArr = INSTITUTE_ROR_LOGOS.filter((o) => o.id === displayItem.id);
           const iconHref = iconArr.pop()?.favicon || "";
@@ -64,7 +64,7 @@ const Relationships = ({ filteredArr, type }: Props) => {
             </span>
           );
         })}
-      </p>
+      </div>
 
       {relShowReadMore ? (
         <button
diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index 2c6d9a7..939d159 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -1,8 +1,9 @@
 "use client";
 
 import { useSearchParams, useRouter } from "next/navigation";
-import { Suspense, useEffect, useState } from "react";
+import { Suspense, useEffect, useState, useContext } from "react";
 
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n";
 import ClearIcon from "@/resources/images/svg/ClearIcon";
 import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
@@ -18,15 +19,24 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
   const router = useRouter();
 
   const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
-  const category = resultParams.get("category") ?? "documents";
+  const [category, setCategory] = useState("datasets"); // default category is a hack, need a better fix
+  //const category = resultParams.get("category") ?? "documents";
 
   // not really random but randomly enough for us…
   const [exampleSearch, setExamples] = useState<string[]>([]);
 
+  const { primaryActiveCategory, setPrimaryActiveCategory } = useContext(CategoryContext);
+
   useEffect(() => {
     setSearchQuery(resultParams.get("searchText") ?? "");
   }, [resultParams]);
 
+  useEffect(() => {
+    if (primaryActiveCategory) {
+      setCategory(primaryActiveCategory.id);
+    }
+  }, [primaryActiveCategory]);
+
   useEffect(() => {
     setExamples(
       [
@@ -52,7 +62,9 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
   const submitSearch = (event: any) => {
     event.preventDefault();
     router.push(
-      `/results?category=${category}` + (searchQuery ? `&searchText=${searchQuery}` : "")
+      `/results?` +
+        (category ? `category=${category}` : "") +
+        (searchQuery ? `&searchText=${searchQuery}` : "")
     );
   };
 
@@ -79,6 +91,8 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
               className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
               onClick={() => {
                 setSearchQuery("");
+                setCategory("datasets");
+                setPrimaryActiveCategory(null);
               }}
               href={`/results?category=${category}`}
             >
@@ -100,7 +114,10 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
           {exampleSearch.map((query, ix) => (
             <Link
               key={ix}
-              href={`/results?category=${category}&searchText=${query}`}
+              href={
+                `/results?` +
+                (category ? `category=${category}&searchText=${query}` : `searchText=${query}`)
+              }
               className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
             >
               <div className="basis-1">{query}</div>
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index f48220c..71b20b5 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -25,8 +25,9 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const [baseCategories, setBaseCategories] = useState<Category[]>([]);
 
   // Function to set the category images
-  const setCategoryImages = async (categories: Category[]) => {
-    const categoriesClone = structuredClone(categories);
+  const fetchCategoriesAndSetImages = async () => {
+    const allCategories = await getAllCategories();
+    const categoriesClone = structuredClone(allCategories);
     for (const category of categoriesClone) {
       let iconImport;
 
@@ -44,23 +45,17 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   // Effects affecting the Landing page actions related to categories
 
   useEffect(() => {
-    const fetchCategoriesForLandingPage = async () => {
+    const setLandingPageCategories = async () => {
       try {
-        const baseCategories = await getAllCategories();
-        const categories = await setCategoryImages(baseCategories);
-        setVisibleCategories(categories);
+        const allCategories = await fetchCategoriesAndSetImages();
+        setVisibleCategories(allCategories);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
       }
     };
-    fetchCategoriesForLandingPage();
+    setLandingPageCategories();
   }, []);
 
-  // TODO: ES-lint says use useReducer to do what I am doing here. Implement it
-  useEffect(() => {
-    setPrimaryActiveCategory(visibleCategories[0]);
-  }, [searchText]);
-
   // Effects affecting the CategoryBar component
 
   useEffect(() => {
@@ -72,24 +67,20 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   }, [detailsText]);
 
   useEffect(() => {
-    const fetchCategoriesForCategoryBar = async () => {
+    const setCategoryBarCategories = async () => {
       try {
-        const baseCategories = await getAllCategories();
-        // calling this here for completeness of information though its not necessarily used in the UI right now
-        const categoriesWithIcons = await setCategoryImages(baseCategories);
+        const allCategories = await fetchCategoriesAndSetImages();
         if (inDetailsView && primaryActiveCategory) {
-          setBaseCategories(
-            categoriesWithIcons.filter((cat) => cat.id !== primaryActiveCategory.id)
-          );
+          setBaseCategories(allCategories.filter((cat) => cat.id !== primaryActiveCategory.id));
         } else {
-          setBaseCategories(categoriesWithIcons);
+          setBaseCategories(allCategories);
         }
       } catch (error) {
         console.error("Error fetching categories for category bar", error);
       }
     };
 
-    fetchCategoriesForCategoryBar();
+    setCategoryBarCategories();
   }, [inDetailsView, primaryActiveCategory]);
 
   useEffect(() => {
@@ -113,6 +104,12 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     fetchAndUpdateCategoryBarCount();
   }, [searchText, detailsText, baseCategories]);
 
+  useEffect(() => {
+    if (!primaryActiveCategory && !inDetailsView) {
+      setPrimaryActiveCategory(visibleCategories[0]);
+    }
+  }, [inDetailsView, primaryActiveCategory, visibleCategories]);
+
   useEffect(() => {
     if (inDetailsView) {
       setRelatedActiveCategory(visibleCategories[0]);
-- 
GitLab


From cd7f08745c92a44de384377a7ac419f1620d9c0b Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Fri, 21 Feb 2025 18:29:56 +0100
Subject: [PATCH 08/48] debugging race conditions, navigation resets,
 state-context-ref updates in component lifecycles to get category context to
 run the search component

---
 src/components/layout/Search.tsx | 184 ++++++++++++++++++++++++++++---
 src/contexts/CategoryContext.tsx |   5 +
 2 files changed, 171 insertions(+), 18 deletions(-)

diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index 939d159..f039349 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -1,7 +1,7 @@
 "use client";
 
 import { useSearchParams, useRouter } from "next/navigation";
-import { Suspense, useEffect, useState, useContext } from "react";
+import { Suspense, useEffect, useState, useContext, useRef } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n";
@@ -19,11 +19,14 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
   const router = useRouter();
 
   const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
-  const [category, setCategory] = useState("datasets"); // default category is a hack, need a better fix
-  //const category = resultParams.get("category") ?? "documents";
+  // Add this to track the current category ID
+  const [currentCategoryId, setCurrentCategoryId] = useState<string | undefined>(undefined);
 
   // not really random but randomly enough for us…
   const [exampleSearch, setExamples] = useState<string[]>([]);
+  const categoryIdRef = useRef<string | undefined>(undefined);
+
+  console.log("RENDER - currentCategoryId:", currentCategoryId);
 
   const { primaryActiveCategory, setPrimaryActiveCategory } = useContext(CategoryContext);
 
@@ -31,12 +34,6 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
     setSearchQuery(resultParams.get("searchText") ?? "");
   }, [resultParams]);
 
-  useEffect(() => {
-    if (primaryActiveCategory) {
-      setCategory(primaryActiveCategory.id);
-    }
-  }, [primaryActiveCategory]);
-
   useEffect(() => {
     setExamples(
       [
@@ -59,11 +56,48 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
     );
   }, []);
 
+  useEffect(() => {
+    console.log("Effect triggered - primaryActiveCategory:", primaryActiveCategory);
+    if (primaryActiveCategory?.id) {
+      console.log("Setting category ID from", currentCategoryId, "to", primaryActiveCategory.id);
+      setCurrentCategoryId(primaryActiveCategory.id);
+      categoryIdRef.current = primaryActiveCategory.id;
+    }
+  }, [primaryActiveCategory, currentCategoryId]);
+
+  // Log whenever currentCategoryId changes
+  useEffect(() => {
+    console.log("currentCategoryId changed to:", currentCategoryId);
+    categoryIdRef.current = currentCategoryId;
+  }, [currentCategoryId]);
+
+  useEffect(() => {
+    categoryIdRef.current = primaryActiveCategory?.id;
+  }, [primaryActiveCategory]);
+
+  const handleClear = () => {
+    console.log("=== handleClear execution ===");
+    console.log("state value:", currentCategoryId);
+    console.log("ref value:", categoryIdRef.current);
+    console.log("context value:", primaryActiveCategory?.id);
+    setSearchQuery("");
+  };
+
+  // Let's create this outside the event handler
+  const navigateToResults = () => {
+    // Copy the ID to a local variable to ensure it doesn't change
+    const categoryId = categoryIdRef.current || currentCategoryId;
+    console.log("Navigation function - using categoryId:", categoryId);
+    const url = `/results?${categoryId ? `category=${categoryId}` : ""}`;
+    console.log("Navigating to:", url);
+    router.push(url);
+  };
+
   const submitSearch = (event: any) => {
     event.preventDefault();
     router.push(
       `/results?` +
-        (category ? `category=${category}` : "") +
+        (currentCategoryId ? `category=${currentCategoryId}` : "") +
         (searchQuery ? `&searchText=${searchQuery}` : "")
     );
   };
@@ -87,17 +121,22 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
         {/* TODO: refactor this into a single reusable component? */}
         <div className="flex md:basis-1/12 justify-end divide-x-2">
           {searchQuery !== "" ? (
-            <Link
+            <button
               className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
-              onClick={() => {
-                setSearchQuery("");
-                setCategory("datasets");
-                setPrimaryActiveCategory(null);
+              onClick={(e) => {
+                e.preventDefault();
+                console.log("Button clicked - currentCategoryId:", currentCategoryId);
+                console.log("Button clicked - ref value:", categoryIdRef.current);
+                handleClear();
+                // Manual navigation after a small delay
+                setTimeout(() => {
+                  navigateToResults();
+                }, 50);
               }}
-              href={`/results?category=${category}`}
+              // href={`#`}
             >
               <ClearIcon />
-            </Link>
+            </button>
           ) : null}
 
           <div className="pl-2">
@@ -116,7 +155,9 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
               key={ix}
               href={
                 `/results?` +
-                (category ? `category=${category}&searchText=${query}` : `searchText=${query}`)
+                (primaryActiveCategory?.id
+                  ? `category=${primaryActiveCategory?.id}&searchText=${query}`
+                  : `searchText=${query}`)
               }
               className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
             >
@@ -137,3 +178,110 @@ export default function Search(props: Props) {
     </Suspense>
   );
 }
+
+{
+  /*
+
+Effect triggered - primaryActiveCategory: 
+
+Object { id: "experts", text: "Experts", count: 746, icon: {…} }
+
+ Search.tsx:61:12
+
+Setting category ID from datasets to experts Search.tsx:63:14
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+Effect triggered - primaryActiveCategory:
+
+Object { id: "experts", text: "Experts", count: 746, icon: {…} }
+
+ Search.tsx:61:12
+
+Setting category ID from experts to experts Search.tsx:63:14
+
+currentCategoryId changed to: experts Search.tsx:71:12
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+RENDER - currentCategoryId: undefined Search.tsx:30:10
+
+RENDER - currentCategoryId: undefined results:13898:27
+
+RENDER - currentCategoryId: undefined Search.tsx:30:10
+
+RENDER - currentCategoryId: undefined results:13898:27
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+RENDER - currentCategoryId: undefined Search.tsx:30:10
+
+RENDER - currentCategoryId: undefined results:13898:27
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+Button clicked - currentCategoryId: undefined Search.tsx:129:24
+
+Button clicked - ref value: undefined Search.tsx:130:24
+
+=== handleClear execution === Search.tsx:80:12
+
+state value: undefined Search.tsx:81:12
+
+ref value: undefined Search.tsx:82:12
+
+context value: undefined Search.tsx:83:12
+
+RENDER - currentCategoryId: undefined Search.tsx:30:10
+
+RENDER - currentCategoryId: undefined results:13898:27
+
+Navigation function - using categoryId: undefined Search.tsx:91:12
+
+Navigating to: /results? Search.tsx:93:12
+
+RENDER - currentCategoryId: undefined Search.tsx:30:10
+
+RENDER - currentCategoryId: undefined results:13898:27
+
+RENDER - currentCategoryId: undefined Search.tsx:30:10
+
+RENDER - currentCategoryId: undefined results:13898:27
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+RENDER - currentCategoryId: undefined Search.tsx:30:10
+
+RENDER - currentCategoryId: undefined results:13898:27
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+RENDER - currentCategoryId: experts Search.tsx:30:10
+
+RENDER - currentCategoryId: experts results:13898:27
+
+Really cool to watch how multiple effects interact, race conditions, component life cycles, re-renders, state context and ref handling.... Its really complicated to keep track of all this, 
+This is not an easy job!!!
+
+*/
+}
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 71b20b5..62739d9 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -104,8 +104,13 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     fetchAndUpdateCategoryBarCount();
   }, [searchText, detailsText, baseCategories]);
 
+  useEffect(() => {
+    //console.log(searchText, visibleCategories, primaryActiveCategory);
+  }, [searchText, visibleCategories]);
+
   useEffect(() => {
     if (!primaryActiveCategory && !inDetailsView) {
+      //console.log("I am setting primary category again");
       setPrimaryActiveCategory(visibleCategories[0]);
     }
   }, [inDetailsView, primaryActiveCategory, visibleCategories]);
-- 
GitLab


From d617d984407bab07ccfed03219bad24966c93f7c Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Mon, 24 Feb 2025 11:10:57 +0100
Subject: [PATCH 09/48] back to using searchParams for up to date category
 information during search and clear in landing and results view

---
 src/components/layout/Search.tsx | 179 ++++---------------------------
 1 file changed, 18 insertions(+), 161 deletions(-)

diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index f039349..1326245 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -1,7 +1,7 @@
 "use client";
 
 import { useSearchParams, useRouter } from "next/navigation";
-import { Suspense, useEffect, useState, useContext, useRef } from "react";
+import { Suspense, useEffect, useState, useContext } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n";
@@ -19,17 +19,27 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
   const router = useRouter();
 
   const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
+  const paramsCategory = resultParams.get("category") ?? "datasets";
   // Add this to track the current category ID
   const [currentCategoryId, setCurrentCategoryId] = useState<string | undefined>(undefined);
 
   // not really random but randomly enough for us…
   const [exampleSearch, setExamples] = useState<string[]>([]);
-  const categoryIdRef = useRef<string | undefined>(undefined);
+  // const categoryIdRef = useRef<string | undefined>(undefined);
 
   console.log("RENDER - currentCategoryId:", currentCategoryId);
 
   const { primaryActiveCategory, setPrimaryActiveCategory } = useContext(CategoryContext);
 
+  useEffect(() => {
+    console.log("Effect triggered - primaryActiveCategory:", primaryActiveCategory);
+    if (primaryActiveCategory?.id) {
+      // console.log("Setting category ID from", currentCategoryId, "to", primaryActiveCategory.id);
+      setCurrentCategoryId(primaryActiveCategory.id);
+      // categoryIdRef.current = primaryActiveCategory.id;
+    }
+  }, [primaryActiveCategory]);
+
   useEffect(() => {
     setSearchQuery(resultParams.get("searchText") ?? "");
   }, [resultParams]);
@@ -56,43 +66,6 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
     );
   }, []);
 
-  useEffect(() => {
-    console.log("Effect triggered - primaryActiveCategory:", primaryActiveCategory);
-    if (primaryActiveCategory?.id) {
-      console.log("Setting category ID from", currentCategoryId, "to", primaryActiveCategory.id);
-      setCurrentCategoryId(primaryActiveCategory.id);
-      categoryIdRef.current = primaryActiveCategory.id;
-    }
-  }, [primaryActiveCategory, currentCategoryId]);
-
-  // Log whenever currentCategoryId changes
-  useEffect(() => {
-    console.log("currentCategoryId changed to:", currentCategoryId);
-    categoryIdRef.current = currentCategoryId;
-  }, [currentCategoryId]);
-
-  useEffect(() => {
-    categoryIdRef.current = primaryActiveCategory?.id;
-  }, [primaryActiveCategory]);
-
-  const handleClear = () => {
-    console.log("=== handleClear execution ===");
-    console.log("state value:", currentCategoryId);
-    console.log("ref value:", categoryIdRef.current);
-    console.log("context value:", primaryActiveCategory?.id);
-    setSearchQuery("");
-  };
-
-  // Let's create this outside the event handler
-  const navigateToResults = () => {
-    // Copy the ID to a local variable to ensure it doesn't change
-    const categoryId = categoryIdRef.current || currentCategoryId;
-    console.log("Navigation function - using categoryId:", categoryId);
-    const url = `/results?${categoryId ? `category=${categoryId}` : ""}`;
-    console.log("Navigating to:", url);
-    router.push(url);
-  };
-
   const submitSearch = (event: any) => {
     event.preventDefault();
     router.push(
@@ -121,22 +94,13 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
         {/* TODO: refactor this into a single reusable component? */}
         <div className="flex md:basis-1/12 justify-end divide-x-2">
           {searchQuery !== "" ? (
-            <button
+            <Link
               className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
-              onClick={(e) => {
-                e.preventDefault();
-                console.log("Button clicked - currentCategoryId:", currentCategoryId);
-                console.log("Button clicked - ref value:", categoryIdRef.current);
-                handleClear();
-                // Manual navigation after a small delay
-                setTimeout(() => {
-                  navigateToResults();
-                }, 50);
-              }}
-              // href={`#`}
+              onClick={() => setSearchQuery("")}
+              href={`/results?` + (paramsCategory ? `category=${paramsCategory}` : "")}
             >
               <ClearIcon />
-            </button>
+            </Link>
           ) : null}
 
           <div className="pl-2">
@@ -155,8 +119,8 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
               key={ix}
               href={
                 `/results?` +
-                (primaryActiveCategory?.id
-                  ? `category=${primaryActiveCategory?.id}&searchText=${query}`
+                (paramsCategory
+                  ? `category=${paramsCategory}&searchText=${query}`
                   : `searchText=${query}`)
               }
               className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
@@ -178,110 +142,3 @@ export default function Search(props: Props) {
     </Suspense>
   );
 }
-
-{
-  /*
-
-Effect triggered - primaryActiveCategory: 
-
-Object { id: "experts", text: "Experts", count: 746, icon: {…} }
-
- Search.tsx:61:12
-
-Setting category ID from datasets to experts Search.tsx:63:14
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-Effect triggered - primaryActiveCategory:
-
-Object { id: "experts", text: "Experts", count: 746, icon: {…} }
-
- Search.tsx:61:12
-
-Setting category ID from experts to experts Search.tsx:63:14
-
-currentCategoryId changed to: experts Search.tsx:71:12
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-RENDER - currentCategoryId: undefined Search.tsx:30:10
-
-RENDER - currentCategoryId: undefined results:13898:27
-
-RENDER - currentCategoryId: undefined Search.tsx:30:10
-
-RENDER - currentCategoryId: undefined results:13898:27
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-RENDER - currentCategoryId: undefined Search.tsx:30:10
-
-RENDER - currentCategoryId: undefined results:13898:27
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-Button clicked - currentCategoryId: undefined Search.tsx:129:24
-
-Button clicked - ref value: undefined Search.tsx:130:24
-
-=== handleClear execution === Search.tsx:80:12
-
-state value: undefined Search.tsx:81:12
-
-ref value: undefined Search.tsx:82:12
-
-context value: undefined Search.tsx:83:12
-
-RENDER - currentCategoryId: undefined Search.tsx:30:10
-
-RENDER - currentCategoryId: undefined results:13898:27
-
-Navigation function - using categoryId: undefined Search.tsx:91:12
-
-Navigating to: /results? Search.tsx:93:12
-
-RENDER - currentCategoryId: undefined Search.tsx:30:10
-
-RENDER - currentCategoryId: undefined results:13898:27
-
-RENDER - currentCategoryId: undefined Search.tsx:30:10
-
-RENDER - currentCategoryId: undefined results:13898:27
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-RENDER - currentCategoryId: undefined Search.tsx:30:10
-
-RENDER - currentCategoryId: undefined results:13898:27
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-RENDER - currentCategoryId: experts Search.tsx:30:10
-
-RENDER - currentCategoryId: experts results:13898:27
-
-Really cool to watch how multiple effects interact, race conditions, component life cycles, re-renders, state context and ref handling.... Its really complicated to keep track of all this, 
-This is not an easy job!!!
-
-*/
-}
-- 
GitLab


From 528435265f0ff4898a37599ad8050eaf95ea5e59 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Mon, 24 Feb 2025 13:03:38 +0100
Subject: [PATCH 10/48] syncing context state on navigation from landing page
 to results view via both search and categories component

---
 src/components/layout/Search.tsx | 29 +++++++++++++++++------------
 src/contexts/CategoryContext.tsx |  7 ++++++-
 2 files changed, 23 insertions(+), 13 deletions(-)

diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index 1326245..02c2b07 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -29,7 +29,8 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
 
   console.log("RENDER - currentCategoryId:", currentCategoryId);
 
-  const { primaryActiveCategory, setPrimaryActiveCategory } = useContext(CategoryContext);
+  const { visibleCategories, primaryActiveCategory, inDetailsView, setPrimaryActiveCategory } =
+    useContext(CategoryContext);
 
   useEffect(() => {
     console.log("Effect triggered - primaryActiveCategory:", primaryActiveCategory);
@@ -66,12 +67,16 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
     );
   }, []);
 
+  const handleClear = () => {
+    setSearchQuery("");
+    setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
+  };
+
   const submitSearch = (event: any) => {
     event.preventDefault();
+    setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
     router.push(
-      `/results?` +
-        (currentCategoryId ? `category=${currentCategoryId}` : "") +
-        (searchQuery ? `&searchText=${searchQuery}` : "")
+      `/results?category=${paramsCategory}` + (searchQuery ? `&searchText=${searchQuery}` : "")
     );
   };
 
@@ -96,8 +101,8 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
           {searchQuery !== "" ? (
             <Link
               className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
-              onClick={() => setSearchQuery("")}
-              href={`/results?` + (paramsCategory ? `category=${paramsCategory}` : "")}
+              onClick={handleClear}
+              href={`/results?` + `category=${paramsCategory}`}
             >
               <ClearIcon />
             </Link>
@@ -117,13 +122,13 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
           {exampleSearch.map((query, ix) => (
             <Link
               key={ix}
-              href={
-                `/results?` +
-                (paramsCategory
-                  ? `category=${paramsCategory}&searchText=${query}`
-                  : `searchText=${query}`)
-              }
+              href={`/results?` + `category=${paramsCategory}&searchText=${query}`}
               className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
+              onClick={() =>
+                setPrimaryActiveCategory(
+                  visibleCategories.filter((cat) => cat.id === paramsCategory)[0]
+                )
+              }
             >
               <div className="basis-1">{query}</div>
               <RightArrowIcon />
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 62739d9..3a36790 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -104,9 +104,14 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     fetchAndUpdateCategoryBarCount();
   }, [searchText, detailsText, baseCategories]);
 
+  // Effects affecting the categories in the details view
+
   useEffect(() => {
     //console.log(searchText, visibleCategories, primaryActiveCategory);
-  }, [searchText, visibleCategories]);
+    if (!searchText && inDetailsView) {
+      setPrimaryActiveCategory(visibleCategories[0]);
+    }
+  }, [searchText, visibleCategories, inDetailsView]);
 
   useEffect(() => {
     if (!primaryActiveCategory && !inDetailsView) {
-- 
GitLab


From 686568ba38815e72c2652b9c149186ce906c2954 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Mon, 24 Feb 2025 14:28:05 +0100
Subject: [PATCH 11/48] syncing category context with params when category
 changed in details view, with default behavior of navigating to the datasets
 category

---
 src/components/app/results/ListResults.tsx |  2 +-
 src/components/layout/Search.tsx           | 29 ++++++++--------------
 src/contexts/CategoryContext.tsx           |  8 ------
 3 files changed, 11 insertions(+), 28 deletions(-)

diff --git a/src/components/app/results/ListResults.tsx b/src/components/app/results/ListResults.tsx
index d58ca94..59100f3 100644
--- a/src/components/app/results/ListResults.tsx
+++ b/src/components/app/results/ListResults.tsx
@@ -147,7 +147,7 @@ const ListResultsComponent = ({ activeCategory, resultsLoading, setResultsLoadin
           >
             <div className="py-5 text-primary-helmholtz-dunkelblau underline">
               {t("result_count", {
-                count: activeCategory.count,
+                count: searchCount,
                 category: s(activeCategory.id, { count: activeCategory.count }),
               })}
             </div>
diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index 02c2b07..9ebb5e1 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -20,26 +20,12 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
 
   const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
   const paramsCategory = resultParams.get("category") ?? "datasets";
-  // Add this to track the current category ID
-  const [currentCategoryId, setCurrentCategoryId] = useState<string | undefined>(undefined);
+  const detailsText = resultParams.get("detailsText") ?? undefined;
 
   // not really random but randomly enough for us…
   const [exampleSearch, setExamples] = useState<string[]>([]);
-  // const categoryIdRef = useRef<string | undefined>(undefined);
 
-  console.log("RENDER - currentCategoryId:", currentCategoryId);
-
-  const { visibleCategories, primaryActiveCategory, inDetailsView, setPrimaryActiveCategory } =
-    useContext(CategoryContext);
-
-  useEffect(() => {
-    console.log("Effect triggered - primaryActiveCategory:", primaryActiveCategory);
-    if (primaryActiveCategory?.id) {
-      // console.log("Setting category ID from", currentCategoryId, "to", primaryActiveCategory.id);
-      setCurrentCategoryId(primaryActiveCategory.id);
-      // categoryIdRef.current = primaryActiveCategory.id;
-    }
-  }, [primaryActiveCategory]);
+  const { visibleCategories, setPrimaryActiveCategory } = useContext(CategoryContext);
 
   useEffect(() => {
     setSearchQuery(resultParams.get("searchText") ?? "");
@@ -102,7 +88,7 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
             <Link
               className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
               onClick={handleClear}
-              href={`/results?` + `category=${paramsCategory}`}
+              href={`/results?category=${paramsCategory}`}
             >
               <ClearIcon />
             </Link>
@@ -122,11 +108,16 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
           {exampleSearch.map((query, ix) => (
             <Link
               key={ix}
-              href={`/results?` + `category=${paramsCategory}&searchText=${query}`}
+              href={
+                `/results?` +
+                `category=${detailsText ? "datasets" : paramsCategory}&searchText=${query}`
+              }
               className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
               onClick={() =>
                 setPrimaryActiveCategory(
-                  visibleCategories.filter((cat) => cat.id === paramsCategory)[0]
+                  visibleCategories.filter(
+                    (cat) => cat.id === (detailsText ? "datasets" : paramsCategory)
+                  )[0]
                 )
               }
             >
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 3a36790..bb96698 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -106,16 +106,8 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
 
   // Effects affecting the categories in the details view
 
-  useEffect(() => {
-    //console.log(searchText, visibleCategories, primaryActiveCategory);
-    if (!searchText && inDetailsView) {
-      setPrimaryActiveCategory(visibleCategories[0]);
-    }
-  }, [searchText, visibleCategories, inDetailsView]);
-
   useEffect(() => {
     if (!primaryActiveCategory && !inDetailsView) {
-      //console.log("I am setting primary category again");
       setPrimaryActiveCategory(visibleCategories[0]);
     }
   }, [inDetailsView, primaryActiveCategory, visibleCategories]);
-- 
GitLab


From 446f125e334861ddb9ef37a43d9cbe53e7c76b56 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Mon, 24 Feb 2025 17:30:15 +0100
Subject: [PATCH 12/48] merging dev into category-context

---
 .env.example                                  |    6 +-
 .gitlab-ci.yml                                |    4 +-
 next.config.mjs => next.config.ts             |    4 +-
 package-lock.json                             | 9184 -----------------
 package.json                                  |   25 +-
 src/app/[locale]/globals.css                  |    3 +
 src/app/[locale]/layout.tsx                   |   21 +-
 src/app/[locale]/results/details/page.tsx     |    5 +
 src/components/ExternalLink.tsx               |    6 +-
 src/components/app/Categories.tsx             |    3 +-
 src/components/app/Intro.tsx                  |    8 +-
 src/components/app/results/CategoryBar.tsx    |    3 +-
 .../app/results/ListResults/ResultItem.tsx    |    3 +-
 .../ListResults/ResultItem/Relationships.tsx  |    3 +-
 src/components/layout/Footer.tsx              |   67 +-
 src/components/layout/Header.tsx              |    3 +-
 src/components/layout/Search.tsx              |   66 +-
 src/components/layout/SearchForm.tsx          |   84 +
 src/i18n.ts                                   |   21 -
 src/locales/de.json                           |    6 +-
 src/locales/en.json                           |    6 +-
 src/middleware.ts                             |    4 +-
 src/resources/images/svg/BlueskyLogo.tsx      |   21 +
 src/resources/images/svg/TwitterLogo.tsx      |   16 -
 src/resources/images/svg/YouTubeLogo.tsx      |   17 -
 tsconfig.json                                 |    3 +-
 yarn.lock                                     | 1454 ++-
 27 files changed, 1200 insertions(+), 9846 deletions(-)
 rename next.config.mjs => next.config.ts (98%)
 delete mode 100644 package-lock.json
 create mode 100644 src/components/layout/SearchForm.tsx
 delete mode 100644 src/i18n.ts
 create mode 100644 src/resources/images/svg/BlueskyLogo.tsx
 delete mode 100644 src/resources/images/svg/TwitterLogo.tsx
 delete mode 100644 src/resources/images/svg/YouTubeLogo.tsx

diff --git a/.env.example b/.env.example
index 4c4168e..d2a7389 100644
--- a/.env.example
+++ b/.env.example
@@ -5,9 +5,9 @@ NEXT_PRIVATE_SOLR_SERVICE_URL=
 NEXT_PUBLIC_SHOW_BANNER=
 
 # urls for the sparql endpoint and the docs
-NEXT_PRIVATE_SPARQL_URL=
-NEXT_PRIVATE_DOC_URL=
+NEXT_PUBLIC_SPARQL_URL=
+NEXT_PUBLIC_DOC_URL=
 
-# for the time being, you will also need 
+# for the time being, you will also need
 NEXT_PUBLIC_API_URL=
 
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e9e9f4a..ae616f3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,11 +7,11 @@ NodeJS Tests (new API and frontend):
   script:
     - node --version
     - yarn --version
-    - export NEXT_PRIVATE_SOLR_SERVICE_URL=http://example.com
+    - export NEXT_PRIVATE_SOLR_SERVICE_URL=https://example.com
     - export NEXT_PUBLIC_SHOW_BANNER=false
     - yarn install
     - yarn run lint
     - yarn test
   parallel:
     matrix:
-      - NODE_VERSION: ["18", "20", "21"]
+      - NODE_VERSION: ["18", "20", "22", "23"]
diff --git a/next.config.mjs b/next.config.ts
similarity index 98%
rename from next.config.mjs
rename to next.config.ts
index 1740598..5256858 100644
--- a/next.config.mjs
+++ b/next.config.ts
@@ -1,9 +1,9 @@
 import createNextIntlPlugin from "next-intl/plugin";
+import type { NextConfig } from "next";
 
 const withNextIntl = createNextIntlPlugin();
 
-/** @type {import('next').NextConfig} */
-const nextConfig = {
+const nextConfig: NextConfig = {
   // reactStrictMode: false,
   eslint: {
     dirs: ["src"],
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 2ea095f..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,9184 +0,0 @@
-{
-  "name": "unhide-ui",
-  "version": "2.0.0",
-  "lockfileVersion": 3,
-  "requires": true,
-  "packages": {
-    "": {
-      "name": "unhide-ui",
-      "version": "2.0.0",
-      "license": "MIT",
-      "dependencies": {
-        "@headlessui/react": "^1.7.18",
-        "@headlessui/tailwindcss": "^0.1.3",
-        "jotai": "^2.10.3",
-        "next": "14.1.0",
-        "next-intl": "^3.19.1",
-        "react": "^18",
-        "react-dom": "^18",
-        "react-paginate": "^8.2.0",
-        "sharp": "^0.33.4",
-        "zod": "^3.22.4"
-      },
-      "devDependencies": {
-        "@testing-library/jest-dom": "^6.4.2",
-        "@testing-library/react": "^14.2.1",
-        "@types/isomorphic-fetch": "^0.0.39",
-        "@types/jest": "^29.5.12",
-        "@types/node": "^20",
-        "@types/react": "^18",
-        "@types/react-dom": "^18",
-        "@types/test-listen": "^1.1.2",
-        "autoprefixer": "^10.0.1",
-        "eslint": "^8",
-        "eslint-config-next": "14.1.0",
-        "eslint-config-universe": "^12.0.0",
-        "eslint-plugin-i18next": "^6.0.3",
-        "eslint-plugin-unused-imports": "^3.1.0",
-        "husky": "^9.0.11",
-        "isomorphic-fetch": "^3.0.0",
-        "jest": "^29.7.0",
-        "jest-environment-jsdom": "^29.7.0",
-        "node-mocks-http": "^1.14.1",
-        "postcss": "^8",
-        "prettier": "^3.2.5",
-        "tailwindcss": "^3.3.0",
-        "test-listen": "^1.1.0",
-        "ts-node": "^10.9.2",
-        "typescript": "^5"
-      }
-    },
-    "node_modules/@adobe/css-tools": {
-      "version": "4.3.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@alloc/quick-lru": {
-      "version": "5.2.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/@ampproject/remapping": {
-      "version": "2.3.0",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@babel/code-frame": {
-      "version": "7.24.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/highlight": "^7.24.2",
-        "picocolors": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/compat-data": {
-      "version": "7.24.4",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/core": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.24.2",
-        "@babel/generator": "^7.24.5",
-        "@babel/helper-compilation-targets": "^7.23.6",
-        "@babel/helper-module-transforms": "^7.24.5",
-        "@babel/helpers": "^7.24.5",
-        "@babel/parser": "^7.24.5",
-        "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.5",
-        "@babel/types": "^7.24.5",
-        "convert-source-map": "^2.0.0",
-        "debug": "^4.1.0",
-        "gensync": "^1.0.0-beta.2",
-        "json5": "^2.2.3",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/babel"
-      }
-    },
-    "node_modules/@babel/core/node_modules/json5": {
-      "version": "2.2.3",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "json5": "lib/cli.js"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/@babel/core/node_modules/semver": {
-      "version": "6.3.1",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/@babel/generator": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.24.5",
-        "@jridgewell/gen-mapping": "^0.3.5",
-        "@jridgewell/trace-mapping": "^0.3.25",
-        "jsesc": "^2.5.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-compilation-targets": {
-      "version": "7.23.6",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/compat-data": "^7.23.5",
-        "@babel/helper-validator-option": "^7.23.5",
-        "browserslist": "^4.22.2",
-        "lru-cache": "^5.1.1",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
-      "version": "5.1.1",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^3.0.2"
-      }
-    },
-    "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
-      "version": "6.3.1",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/@babel/helper-environment-visitor": {
-      "version": "7.22.20",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-function-name": {
-      "version": "7.23.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/template": "^7.22.15",
-        "@babel/types": "^7.23.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-hoist-variables": {
-      "version": "7.22.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.22.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-module-imports": {
-      "version": "7.24.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.24.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-module-transforms": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-environment-visitor": "^7.22.20",
-        "@babel/helper-module-imports": "^7.24.3",
-        "@babel/helper-simple-access": "^7.24.5",
-        "@babel/helper-split-export-declaration": "^7.24.5",
-        "@babel/helper-validator-identifier": "^7.24.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-simple-access": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.24.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-split-export-declaration": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.24.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-string-parser": {
-      "version": "7.24.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-validator-option": {
-      "version": "7.23.5",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helpers": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/template": "^7.24.0",
-        "@babel/traverse": "^7.24.5",
-        "@babel/types": "^7.24.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/highlight": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-validator-identifier": "^7.24.5",
-        "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0",
-        "picocolors": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/ansi-styles": {
-      "version": "3.2.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/chalk": {
-      "version": "2.4.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/color-convert": {
-      "version": "1.9.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/color-name": {
-      "version": "1.1.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@babel/highlight/node_modules/has-flag": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/highlight/node_modules/supports-color": {
-      "version": "5.5.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/parser": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "parser": "bin/babel-parser.js"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-async-generators": {
-      "version": "7.8.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-bigint": {
-      "version": "7.8.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-class-properties": {
-      "version": "7.12.13",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.12.13"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-import-meta": {
-      "version": "7.10.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.10.4"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-json-strings": {
-      "version": "7.8.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-jsx": {
-      "version": "7.24.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
-      "version": "7.10.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.10.4"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
-      "version": "7.8.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-numeric-separator": {
-      "version": "7.10.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.10.4"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-object-rest-spread": {
-      "version": "7.8.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-optional-catch-binding": {
-      "version": "7.8.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-optional-chaining": {
-      "version": "7.8.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.8.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-top-level-await": {
-      "version": "7.14.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.14.5"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/plugin-syntax-typescript": {
-      "version": "7.24.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.24.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0-0"
-      }
-    },
-    "node_modules/@babel/runtime": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "regenerator-runtime": "^0.14.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/template": {
-      "version": "7.24.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.23.5",
-        "@babel/parser": "^7.24.0",
-        "@babel/types": "^7.24.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/traverse": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.24.2",
-        "@babel/generator": "^7.24.5",
-        "@babel/helper-environment-visitor": "^7.22.20",
-        "@babel/helper-function-name": "^7.23.0",
-        "@babel/helper-hoist-variables": "^7.22.5",
-        "@babel/helper-split-export-declaration": "^7.24.5",
-        "@babel/parser": "^7.24.5",
-        "@babel/types": "^7.24.5",
-        "debug": "^4.3.1",
-        "globals": "^11.1.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/traverse/node_modules/globals": {
-      "version": "11.12.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/types": {
-      "version": "7.24.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/helper-string-parser": "^7.24.1",
-        "@babel/helper-validator-identifier": "^7.24.5",
-        "to-fast-properties": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@bcoe/v8-coverage": {
-      "version": "0.2.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@cspotcode/source-map-support": {
-      "version": "0.8.1",
-      "devOptional": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/trace-mapping": "0.3.9"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.9",
-      "devOptional": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/resolve-uri": "^3.0.3",
-        "@jridgewell/sourcemap-codec": "^1.4.10"
-      }
-    },
-    "node_modules/@emnapi/runtime": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
-      "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
-      "optional": true,
-      "dependencies": {
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@eslint-community/eslint-utils": {
-      "version": "4.4.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-visitor-keys": "^3.3.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "peerDependencies": {
-        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
-      }
-    },
-    "node_modules/@eslint-community/regexpp": {
-      "version": "4.10.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
-      }
-    },
-    "node_modules/@eslint/eslintrc": {
-      "version": "2.1.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ajv": "^6.12.4",
-        "debug": "^4.3.2",
-        "espree": "^9.6.0",
-        "globals": "^13.19.0",
-        "ignore": "^5.2.0",
-        "import-fresh": "^3.2.1",
-        "js-yaml": "^4.1.0",
-        "minimatch": "^3.1.2",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint/js": {
-      "version": "8.57.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      }
-    },
-    "node_modules/@formatjs/ecma402-abstract": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
-      "integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
-      "dependencies": {
-        "@formatjs/intl-localematcher": "0.5.4",
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@formatjs/fast-memoize": {
-      "version": "2.2.0",
-      "license": "MIT",
-      "dependencies": {
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@formatjs/icu-messageformat-parser": {
-      "version": "2.7.8",
-      "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz",
-      "integrity": "sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==",
-      "dependencies": {
-        "@formatjs/ecma402-abstract": "2.0.0",
-        "@formatjs/icu-skeleton-parser": "1.8.2",
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@formatjs/icu-skeleton-parser": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz",
-      "integrity": "sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==",
-      "dependencies": {
-        "@formatjs/ecma402-abstract": "2.0.0",
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@formatjs/intl-localematcher": {
-      "version": "0.5.4",
-      "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
-      "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
-      "dependencies": {
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@headlessui/react": {
-      "version": "1.7.19",
-      "license": "MIT",
-      "dependencies": {
-        "@tanstack/react-virtual": "^3.0.0-beta.60",
-        "client-only": "^0.0.1"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "peerDependencies": {
-        "react": "^16 || ^17 || ^18",
-        "react-dom": "^16 || ^17 || ^18"
-      }
-    },
-    "node_modules/@headlessui/tailwindcss": {
-      "version": "0.1.3",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "peerDependencies": {
-        "tailwindcss": "^3.0"
-      }
-    },
-    "node_modules/@humanwhocodes/config-array": {
-      "version": "0.11.14",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@humanwhocodes/object-schema": "^2.0.2",
-        "debug": "^4.3.1",
-        "minimatch": "^3.0.5"
-      },
-      "engines": {
-        "node": ">=10.10.0"
-      }
-    },
-    "node_modules/@humanwhocodes/module-importer": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=12.22"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/nzakas"
-      }
-    },
-    "node_modules/@humanwhocodes/object-schema": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/@img/sharp-darwin-arm64": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz",
-      "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "glibc": ">=2.26",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-darwin-arm64": "1.0.2"
-      }
-    },
-    "node_modules/@img/sharp-darwin-x64": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz",
-      "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "glibc": ">=2.26",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-darwin-x64": "1.0.2"
-      }
-    },
-    "node_modules/@img/sharp-libvips-darwin-arm64": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz",
-      "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "macos": ">=11",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-darwin-x64": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz",
-      "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "macos": ">=10.13",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linux-arm": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz",
-      "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==",
-      "cpu": [
-        "arm"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "glibc": ">=2.28",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linux-arm64": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz",
-      "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "glibc": ">=2.26",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linux-s390x": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz",
-      "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==",
-      "cpu": [
-        "s390x"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "glibc": ">=2.28",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linux-x64": {
-      "version": "1.0.2",
-      "cpu": [
-        "x64"
-      ],
-      "license": "LGPL-3.0-or-later",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "glibc": ">=2.26",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz",
-      "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "musl": ">=1.2.2",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-libvips-linuxmusl-x64": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz",
-      "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "musl": ">=1.2.2",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-linux-arm": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz",
-      "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==",
-      "cpu": [
-        "arm"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "glibc": ">=2.28",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linux-arm": "1.0.2"
-      }
-    },
-    "node_modules/@img/sharp-linux-arm64": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz",
-      "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "glibc": ">=2.26",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linux-arm64": "1.0.2"
-      }
-    },
-    "node_modules/@img/sharp-linux-s390x": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz",
-      "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==",
-      "cpu": [
-        "s390x"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "glibc": ">=2.31",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linux-s390x": "1.0.2"
-      }
-    },
-    "node_modules/@img/sharp-linux-x64": {
-      "version": "0.33.4",
-      "cpu": [
-        "x64"
-      ],
-      "license": "Apache-2.0",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "glibc": ">=2.26",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linux-x64": "1.0.2"
-      }
-    },
-    "node_modules/@img/sharp-linuxmusl-arm64": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz",
-      "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "musl": ">=1.2.2",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linuxmusl-arm64": "1.0.2"
-      }
-    },
-    "node_modules/@img/sharp-linuxmusl-x64": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz",
-      "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "musl": ">=1.2.2",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-libvips-linuxmusl-x64": "1.0.2"
-      }
-    },
-    "node_modules/@img/sharp-wasm32": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz",
-      "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==",
-      "cpu": [
-        "wasm32"
-      ],
-      "optional": true,
-      "dependencies": {
-        "@emnapi/runtime": "^1.1.1"
-      },
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-win32-ia32": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz",
-      "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==",
-      "cpu": [
-        "ia32"
-      ],
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@img/sharp-win32-x64": {
-      "version": "0.33.4",
-      "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz",
-      "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
-        "npm": ">=9.6.5",
-        "pnpm": ">=7.1.0",
-        "yarn": ">=3.2.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      }
-    },
-    "node_modules/@isaacs/cliui": {
-      "version": "8.0.2",
-      "license": "ISC",
-      "dependencies": {
-        "string-width": "^5.1.2",
-        "string-width-cjs": "npm:string-width@^4.2.0",
-        "strip-ansi": "^7.0.1",
-        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
-        "wrap-ansi": "^8.1.0",
-        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
-      "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
-      }
-    },
-    "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
-      "version": "6.2.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/@isaacs/cliui/node_modules/string-width": {
-      "version": "5.1.2",
-      "license": "MIT",
-      "dependencies": {
-        "eastasianwidth": "^0.2.0",
-        "emoji-regex": "^9.2.2",
-        "strip-ansi": "^7.0.1"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
-      "version": "7.1.0",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
-      }
-    },
-    "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
-      "version": "8.1.0",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^6.1.0",
-        "string-width": "^5.0.1",
-        "strip-ansi": "^7.0.1"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/@istanbuljs/load-nyc-config": {
-      "version": "1.1.0",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "camelcase": "^5.3.1",
-        "find-up": "^4.1.0",
-        "get-package-type": "^0.1.0",
-        "js-yaml": "^3.13.1",
-        "resolve-from": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
-      "version": "1.0.10",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "sprintf-js": "~1.0.2"
-      }
-    },
-    "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
-      "version": "3.14.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
-    "node_modules/@istanbuljs/schema": {
-      "version": "0.1.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@jest/console": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/core": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/console": "^29.7.0",
-        "@jest/reporters": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "ansi-escapes": "^4.2.1",
-        "chalk": "^4.0.0",
-        "ci-info": "^3.2.0",
-        "exit": "^0.1.2",
-        "graceful-fs": "^4.2.9",
-        "jest-changed-files": "^29.7.0",
-        "jest-config": "^29.7.0",
-        "jest-haste-map": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-regex-util": "^29.6.3",
-        "jest-resolve": "^29.7.0",
-        "jest-resolve-dependencies": "^29.7.0",
-        "jest-runner": "^29.7.0",
-        "jest-runtime": "^29.7.0",
-        "jest-snapshot": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-validate": "^29.7.0",
-        "jest-watcher": "^29.7.0",
-        "micromatch": "^4.0.4",
-        "pretty-format": "^29.7.0",
-        "slash": "^3.0.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@jest/environment": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/fake-timers": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "jest-mock": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/expect": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "expect": "^29.7.0",
-        "jest-snapshot": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/expect-utils": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "jest-get-type": "^29.6.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/fake-timers": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@sinonjs/fake-timers": "^10.0.2",
-        "@types/node": "*",
-        "jest-message-util": "^29.7.0",
-        "jest-mock": "^29.7.0",
-        "jest-util": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/globals": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/expect": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "jest-mock": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/reporters": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@bcoe/v8-coverage": "^0.2.3",
-        "@jest/console": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@jridgewell/trace-mapping": "^0.3.18",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "collect-v8-coverage": "^1.0.0",
-        "exit": "^0.1.2",
-        "glob": "^7.1.3",
-        "graceful-fs": "^4.2.9",
-        "istanbul-lib-coverage": "^3.0.0",
-        "istanbul-lib-instrument": "^6.0.0",
-        "istanbul-lib-report": "^3.0.0",
-        "istanbul-lib-source-maps": "^4.0.0",
-        "istanbul-reports": "^3.1.3",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-worker": "^29.7.0",
-        "slash": "^3.0.0",
-        "string-length": "^4.0.1",
-        "strip-ansi": "^6.0.0",
-        "v8-to-istanbul": "^9.0.1"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
-      "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
-      "dev": true,
-      "dependencies": {
-        "@babel/core": "^7.23.9",
-        "@babel/parser": "^7.23.9",
-        "@istanbuljs/schema": "^0.1.3",
-        "istanbul-lib-coverage": "^3.2.0",
-        "semver": "^7.5.4"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@jest/schemas": {
-      "version": "29.6.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@sinclair/typebox": "^0.27.8"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/source-map": {
-      "version": "29.6.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/trace-mapping": "^0.3.18",
-        "callsites": "^3.0.0",
-        "graceful-fs": "^4.2.9"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/test-result": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/console": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/istanbul-lib-coverage": "^2.0.0",
-        "collect-v8-coverage": "^1.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/test-sequencer": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/test-result": "^29.7.0",
-        "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.7.0",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/transform": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/core": "^7.11.6",
-        "@jest/types": "^29.6.3",
-        "@jridgewell/trace-mapping": "^0.3.18",
-        "babel-plugin-istanbul": "^6.1.1",
-        "chalk": "^4.0.0",
-        "convert-source-map": "^2.0.0",
-        "fast-json-stable-stringify": "^2.1.0",
-        "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.7.0",
-        "jest-regex-util": "^29.6.3",
-        "jest-util": "^29.7.0",
-        "micromatch": "^4.0.4",
-        "pirates": "^4.0.4",
-        "slash": "^3.0.0",
-        "write-file-atomic": "^4.0.2"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jest/types": {
-      "version": "29.6.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/schemas": "^29.6.3",
-        "@types/istanbul-lib-coverage": "^2.0.0",
-        "@types/istanbul-reports": "^3.0.0",
-        "@types/node": "*",
-        "@types/yargs": "^17.0.8",
-        "chalk": "^4.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/@jridgewell/gen-mapping": {
-      "version": "0.3.5",
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/set-array": "^1.2.1",
-        "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.24"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/resolve-uri": {
-      "version": "3.1.2",
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/set-array": {
-      "version": "1.2.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@jridgewell/sourcemap-codec": {
-      "version": "1.4.15",
-      "license": "MIT"
-    },
-    "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.25",
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/resolve-uri": "^3.1.0",
-        "@jridgewell/sourcemap-codec": "^1.4.14"
-      }
-    },
-    "node_modules/@next/env": {
-      "version": "14.1.0",
-      "license": "MIT"
-    },
-    "node_modules/@next/eslint-plugin-next": {
-      "version": "14.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "glob": "10.3.10"
-      }
-    },
-    "node_modules/@next/eslint-plugin-next/node_modules/glob": {
-      "version": "10.3.10",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.5",
-        "minimatch": "^9.0.1",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
-        "path-scurry": "^1.10.1"
-      },
-      "bin": {
-        "glob": "dist/esm/bin.mjs"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/@next/eslint-plugin-next/node_modules/minimatch": {
-      "version": "9.0.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
-      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/@next/swc-darwin-arm64": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz",
-      "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-darwin-x64": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz",
-      "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-linux-arm64-gnu": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz",
-      "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-linux-arm64-musl": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz",
-      "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-linux-x64-gnu": {
-      "version": "14.1.0",
-      "cpu": [
-        "x64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-linux-x64-musl": {
-      "version": "14.1.0",
-      "cpu": [
-        "x64"
-      ],
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-win32-arm64-msvc": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz",
-      "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==",
-      "cpu": [
-        "arm64"
-      ],
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-win32-ia32-msvc": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz",
-      "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==",
-      "cpu": [
-        "ia32"
-      ],
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@next/swc-win32-x64-msvc": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz",
-      "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==",
-      "cpu": [
-        "x64"
-      ],
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@nodelib/fs.scandir": {
-      "version": "2.1.5",
-      "license": "MIT",
-      "dependencies": {
-        "@nodelib/fs.stat": "2.0.5",
-        "run-parallel": "^1.1.9"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.stat": {
-      "version": "2.0.5",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.walk": {
-      "version": "1.2.8",
-      "license": "MIT",
-      "dependencies": {
-        "@nodelib/fs.scandir": "2.1.5",
-        "fastq": "^1.6.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@pkgjs/parseargs": {
-      "version": "0.11.0",
-      "license": "MIT",
-      "optional": true,
-      "engines": {
-        "node": ">=14"
-      }
-    },
-    "node_modules/@pkgr/core": {
-      "version": "0.1.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/unts"
-      }
-    },
-    "node_modules/@rushstack/eslint-patch": {
-      "version": "1.10.2",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@sinclair/typebox": {
-      "version": "0.27.8",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@sinonjs/commons": {
-      "version": "3.0.1",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "type-detect": "4.0.8"
-      }
-    },
-    "node_modules/@sinonjs/fake-timers": {
-      "version": "10.3.0",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "@sinonjs/commons": "^3.0.0"
-      }
-    },
-    "node_modules/@swc/helpers": {
-      "version": "0.5.2",
-      "license": "Apache-2.0",
-      "dependencies": {
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/@tanstack/react-virtual": {
-      "version": "3.5.0",
-      "license": "MIT",
-      "dependencies": {
-        "@tanstack/virtual-core": "3.5.0"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/tannerlinsley"
-      },
-      "peerDependencies": {
-        "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
-        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
-      }
-    },
-    "node_modules/@tanstack/virtual-core": {
-      "version": "3.5.0",
-      "license": "MIT",
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/tannerlinsley"
-      }
-    },
-    "node_modules/@testing-library/dom": {
-      "version": "9.3.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.10.4",
-        "@babel/runtime": "^7.12.5",
-        "@types/aria-query": "^5.0.1",
-        "aria-query": "5.1.3",
-        "chalk": "^4.1.0",
-        "dom-accessibility-api": "^0.5.9",
-        "lz-string": "^1.5.0",
-        "pretty-format": "^27.0.2"
-      },
-      "engines": {
-        "node": ">=14"
-      }
-    },
-    "node_modules/@testing-library/dom/node_modules/ansi-styles": {
-      "version": "5.2.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/@testing-library/dom/node_modules/aria-query": {
-      "version": "5.1.3",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "deep-equal": "^2.0.5"
-      }
-    },
-    "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": {
-      "version": "0.5.16",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@testing-library/dom/node_modules/pretty-format": {
-      "version": "27.5.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^5.0.1",
-        "ansi-styles": "^5.0.0",
-        "react-is": "^17.0.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
-      }
-    },
-    "node_modules/@testing-library/dom/node_modules/react-is": {
-      "version": "17.0.2",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@testing-library/jest-dom": {
-      "version": "6.4.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@adobe/css-tools": "^4.3.2",
-        "@babel/runtime": "^7.9.2",
-        "aria-query": "^5.0.0",
-        "chalk": "^3.0.0",
-        "css.escape": "^1.5.1",
-        "dom-accessibility-api": "^0.6.3",
-        "lodash": "^4.17.21",
-        "redent": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=14",
-        "npm": ">=6",
-        "yarn": ">=1"
-      },
-      "peerDependencies": {
-        "@jest/globals": ">= 28",
-        "@types/bun": "latest",
-        "@types/jest": ">= 28",
-        "jest": ">= 28",
-        "vitest": ">= 0.32"
-      },
-      "peerDependenciesMeta": {
-        "@jest/globals": {
-          "optional": true
-        },
-        "@types/bun": {
-          "optional": true
-        },
-        "@types/jest": {
-          "optional": true
-        },
-        "jest": {
-          "optional": true
-        },
-        "vitest": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@testing-library/jest-dom/node_modules/chalk": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@testing-library/react": {
-      "version": "14.3.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/runtime": "^7.12.5",
-        "@testing-library/dom": "^9.0.0",
-        "@types/react-dom": "^18.0.0"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "peerDependencies": {
-        "react": "^18.0.0",
-        "react-dom": "^18.0.0"
-      }
-    },
-    "node_modules/@tootallnate/once": {
-      "version": "2.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/@tsconfig/node10": {
-      "version": "1.0.11",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/@tsconfig/node12": {
-      "version": "1.0.11",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/@tsconfig/node14": {
-      "version": "1.0.3",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/@tsconfig/node16": {
-      "version": "1.0.4",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/aria-query": {
-      "version": "5.0.4",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/babel__core": {
-      "version": "7.20.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/parser": "^7.20.7",
-        "@babel/types": "^7.20.7",
-        "@types/babel__generator": "*",
-        "@types/babel__template": "*",
-        "@types/babel__traverse": "*"
-      }
-    },
-    "node_modules/@types/babel__generator": {
-      "version": "7.6.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.0.0"
-      }
-    },
-    "node_modules/@types/babel__template": {
-      "version": "7.4.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/parser": "^7.1.0",
-        "@babel/types": "^7.0.0"
-      }
-    },
-    "node_modules/@types/babel__traverse": {
-      "version": "7.20.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/types": "^7.20.7"
-      }
-    },
-    "node_modules/@types/body-parser": {
-      "version": "1.19.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/connect": "*",
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/connect": {
-      "version": "3.4.38",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/express": {
-      "version": "4.17.21",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/body-parser": "*",
-        "@types/express-serve-static-core": "^4.17.33",
-        "@types/qs": "*",
-        "@types/serve-static": "*"
-      }
-    },
-    "node_modules/@types/express-serve-static-core": {
-      "version": "4.19.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "@types/qs": "*",
-        "@types/range-parser": "*",
-        "@types/send": "*"
-      }
-    },
-    "node_modules/@types/graceful-fs": {
-      "version": "4.1.9",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/http-errors": {
-      "version": "2.0.4",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/isomorphic-fetch": {
-      "version": "0.0.39",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/istanbul-lib-coverage": {
-      "version": "2.0.6",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/istanbul-lib-report": {
-      "version": "3.0.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/istanbul-lib-coverage": "*"
-      }
-    },
-    "node_modules/@types/istanbul-reports": {
-      "version": "3.0.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/istanbul-lib-report": "*"
-      }
-    },
-    "node_modules/@types/jest": {
-      "version": "29.5.12",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "expect": "^29.0.0",
-        "pretty-format": "^29.0.0"
-      }
-    },
-    "node_modules/@types/jsdom": {
-      "version": "20.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "@types/tough-cookie": "*",
-        "parse5": "^7.0.0"
-      }
-    },
-    "node_modules/@types/json-schema": {
-      "version": "7.0.15",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/json5": {
-      "version": "0.0.29",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/mime": {
-      "version": "1.3.5",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/node": {
-      "version": "20.17.5",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.5.tgz",
-      "integrity": "sha512-n8FYY/pRxu496441gIcAQFZPKXbhsd6VZygcq+PTSZ75eMh/Ke0hCAROdUa21qiFqKNsPPYic46yXDO1JGiPBQ==",
-      "devOptional": true,
-      "dependencies": {
-        "undici-types": "~6.19.2"
-      }
-    },
-    "node_modules/@types/prop-types": {
-      "version": "15.7.12",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/qs": {
-      "version": "6.9.15",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/range-parser": {
-      "version": "1.2.7",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/react": {
-      "version": "18.3.2",
-      "devOptional": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/prop-types": "*",
-        "csstype": "^3.0.2"
-      }
-    },
-    "node_modules/@types/react-dom": {
-      "version": "18.3.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/react": "*"
-      }
-    },
-    "node_modules/@types/semver": {
-      "version": "7.5.8",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/send": {
-      "version": "0.17.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/mime": "^1",
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/serve-static": {
-      "version": "1.15.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/http-errors": "*",
-        "@types/node": "*",
-        "@types/send": "*"
-      }
-    },
-    "node_modules/@types/stack-utils": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/test-listen": {
-      "version": "1.1.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
-    "node_modules/@types/tough-cookie": {
-      "version": "4.0.5",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@types/yargs": {
-      "version": "17.0.32",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/yargs-parser": "*"
-      }
-    },
-    "node_modules/@types/yargs-parser": {
-      "version": "21.0.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "6.21.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "6.21.0",
-        "@typescript-eslint/type-utils": "6.21.0",
-        "@typescript-eslint/utils": "6.21.0",
-        "@typescript-eslint/visitor-keys": "6.21.0",
-        "debug": "^4.3.4",
-        "graphemer": "^1.4.0",
-        "ignore": "^5.2.4",
-        "natural-compare": "^1.4.0",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
-      },
-      "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
-        "eslint": "^7.0.0 || ^8.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/parser": {
-      "version": "6.21.0",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "@typescript-eslint/scope-manager": "6.21.0",
-        "@typescript-eslint/types": "6.21.0",
-        "@typescript-eslint/typescript-estree": "6.21.0",
-        "@typescript-eslint/visitor-keys": "6.21.0",
-        "debug": "^4.3.4"
-      },
-      "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.21.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "6.21.0",
-        "@typescript-eslint/visitor-keys": "6.21.0"
-      },
-      "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@typescript-eslint/type-utils": {
-      "version": "6.21.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/typescript-estree": "6.21.0",
-        "@typescript-eslint/utils": "6.21.0",
-        "debug": "^4.3.4",
-        "ts-api-utils": "^1.0.1"
-      },
-      "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/types": {
-      "version": "6.21.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "6.21.0",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "@typescript-eslint/types": "6.21.0",
-        "@typescript-eslint/visitor-keys": "6.21.0",
-        "debug": "^4.3.4",
-        "globby": "^11.1.0",
-        "is-glob": "^4.0.3",
-        "minimatch": "9.0.3",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
-      },
-      "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "9.0.3",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/@typescript-eslint/utils": {
-      "version": "6.21.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.12",
-        "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.21.0",
-        "@typescript-eslint/types": "6.21.0",
-        "@typescript-eslint/typescript-estree": "6.21.0",
-        "semver": "^7.5.4"
-      },
-      "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
-      }
-    },
-    "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.21.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "6.21.0",
-        "eslint-visitor-keys": "^3.4.1"
-      },
-      "engines": {
-        "node": "^16.0.0 || >=18.0.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@ungap/structured-clone": {
-      "version": "1.2.0",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/abab": {
-      "version": "2.0.6",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/accepts": {
-      "version": "1.3.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "mime-types": "~2.1.34",
-        "negotiator": "0.6.3"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/acorn": {
-      "version": "8.11.3",
-      "devOptional": true,
-      "license": "MIT",
-      "bin": {
-        "acorn": "bin/acorn"
-      },
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/acorn-globals": {
-      "version": "7.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "acorn": "^8.1.0",
-        "acorn-walk": "^8.0.2"
-      }
-    },
-    "node_modules/acorn-jsx": {
-      "version": "5.3.2",
-      "dev": true,
-      "license": "MIT",
-      "peerDependencies": {
-        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
-      }
-    },
-    "node_modules/acorn-walk": {
-      "version": "8.3.2",
-      "devOptional": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/agent-base": {
-      "version": "6.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "4"
-      },
-      "engines": {
-        "node": ">= 6.0.0"
-      }
-    },
-    "node_modules/ajv": {
-      "version": "6.12.6",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/ansi-escapes": {
-      "version": "4.3.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "type-fest": "^0.21.3"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/ansi-escapes/node_modules/type-fest": {
-      "version": "0.21.3",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/any-promise": {
-      "version": "1.3.0",
-      "license": "MIT"
-    },
-    "node_modules/anymatch": {
-      "version": "3.1.3",
-      "license": "ISC",
-      "dependencies": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/arg": {
-      "version": "5.0.2",
-      "license": "MIT"
-    },
-    "node_modules/argparse": {
-      "version": "2.0.1",
-      "dev": true,
-      "license": "Python-2.0"
-    },
-    "node_modules/aria-query": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
-      "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/array-buffer-byte-length": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.5",
-        "is-array-buffer": "^3.0.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array-includes": {
-      "version": "3.1.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-object-atoms": "^1.0.0",
-        "get-intrinsic": "^1.2.4",
-        "is-string": "^1.0.7"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array-union": {
-      "version": "2.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/array.prototype.findlast": {
-      "version": "1.2.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
-        "es-shim-unscopables": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.findlastindex": {
-      "version": "1.2.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
-        "es-shim-unscopables": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.flat": {
-      "version": "1.3.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "es-shim-unscopables": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.flatmap": {
-      "version": "1.3.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "es-shim-unscopables": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.toreversed": {
-      "version": "1.1.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "es-shim-unscopables": "^1.0.0"
-      }
-    },
-    "node_modules/array.prototype.tosorted": {
-      "version": "1.1.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.5",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.3",
-        "es-errors": "^1.1.0",
-        "es-shim-unscopables": "^1.0.2"
-      }
-    },
-    "node_modules/arraybuffer.prototype.slice": {
-      "version": "1.0.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-buffer-byte-length": "^1.0.1",
-        "call-bind": "^1.0.5",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.3",
-        "es-errors": "^1.2.1",
-        "get-intrinsic": "^1.2.3",
-        "is-array-buffer": "^3.0.4",
-        "is-shared-array-buffer": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/ast-types-flow": {
-      "version": "0.0.8",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/asynckit": {
-      "version": "0.4.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/autoprefixer": {
-      "version": "10.4.19",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "browserslist": "^4.23.0",
-        "caniuse-lite": "^1.0.30001599",
-        "fraction.js": "^4.3.7",
-        "normalize-range": "^0.1.2",
-        "picocolors": "^1.0.0",
-        "postcss-value-parser": "^4.2.0"
-      },
-      "bin": {
-        "autoprefixer": "bin/autoprefixer"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.0"
-      }
-    },
-    "node_modules/available-typed-arrays": {
-      "version": "1.0.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "possible-typed-array-names": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/axe-core": {
-      "version": "4.7.0",
-      "dev": true,
-      "license": "MPL-2.0",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/axobject-query": {
-      "version": "3.2.1",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "dequal": "^2.0.3"
-      }
-    },
-    "node_modules/babel-jest": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/transform": "^29.7.0",
-        "@types/babel__core": "^7.1.14",
-        "babel-plugin-istanbul": "^6.1.1",
-        "babel-preset-jest": "^29.6.3",
-        "chalk": "^4.0.0",
-        "graceful-fs": "^4.2.9",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.8.0"
-      }
-    },
-    "node_modules/babel-plugin-istanbul": {
-      "version": "6.1.1",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "@babel/helper-plugin-utils": "^7.0.0",
-        "@istanbuljs/load-nyc-config": "^1.0.0",
-        "@istanbuljs/schema": "^0.1.2",
-        "istanbul-lib-instrument": "^5.0.4",
-        "test-exclude": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/babel-plugin-jest-hoist": {
-      "version": "29.6.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/template": "^7.3.3",
-        "@babel/types": "^7.3.3",
-        "@types/babel__core": "^7.1.14",
-        "@types/babel__traverse": "^7.0.6"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/babel-preset-current-node-syntax": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/plugin-syntax-async-generators": "^7.8.4",
-        "@babel/plugin-syntax-bigint": "^7.8.3",
-        "@babel/plugin-syntax-class-properties": "^7.8.3",
-        "@babel/plugin-syntax-import-meta": "^7.8.3",
-        "@babel/plugin-syntax-json-strings": "^7.8.3",
-        "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
-        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
-        "@babel/plugin-syntax-numeric-separator": "^7.8.3",
-        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
-        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
-        "@babel/plugin-syntax-top-level-await": "^7.8.3"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/babel-preset-jest": {
-      "version": "29.6.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "babel-plugin-jest-hoist": "^29.6.3",
-        "babel-preset-current-node-syntax": "^1.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "license": "MIT"
-    },
-    "node_modules/binary-extensions": {
-      "version": "2.3.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/brace-expansion": {
-      "version": "2.0.1",
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
-    "node_modules/braces": {
-      "version": "3.0.2",
-      "license": "MIT",
-      "dependencies": {
-        "fill-range": "^7.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/browserslist": {
-      "version": "4.23.0",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/browserslist"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "caniuse-lite": "^1.0.30001587",
-        "electron-to-chromium": "^1.4.668",
-        "node-releases": "^2.0.14",
-        "update-browserslist-db": "^1.0.13"
-      },
-      "bin": {
-        "browserslist": "cli.js"
-      },
-      "engines": {
-        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
-      }
-    },
-    "node_modules/bser": {
-      "version": "2.1.1",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "node-int64": "^0.4.0"
-      }
-    },
-    "node_modules/buffer-from": {
-      "version": "1.1.2",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/busboy": {
-      "version": "1.6.0",
-      "dependencies": {
-        "streamsearch": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=10.16.0"
-      }
-    },
-    "node_modules/call-bind": {
-      "version": "1.0.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "set-function-length": "^1.2.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/callsites": {
-      "version": "3.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/camelcase": {
-      "version": "5.3.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/camelcase-css": {
-      "version": "2.0.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/caniuse-lite": {
-      "version": "1.0.30001660",
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "CC-BY-4.0"
-    },
-    "node_modules/chalk": {
-      "version": "4.1.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/char-regex": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/chokidar": {
-      "version": "3.6.0",
-      "license": "MIT",
-      "dependencies": {
-        "anymatch": "~3.1.2",
-        "braces": "~3.0.2",
-        "glob-parent": "~5.1.2",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.6.0"
-      },
-      "engines": {
-        "node": ">= 8.10.0"
-      },
-      "funding": {
-        "url": "https://paulmillr.com/funding/"
-      },
-      "optionalDependencies": {
-        "fsevents": "~2.3.2"
-      }
-    },
-    "node_modules/chokidar/node_modules/glob-parent": {
-      "version": "5.1.2",
-      "license": "ISC",
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/ci-info": {
-      "version": "3.9.0",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/sibiraj-s"
-        }
-      ],
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cjs-module-lexer": {
-      "version": "1.3.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/client-only": {
-      "version": "0.0.1",
-      "license": "MIT"
-    },
-    "node_modules/cliui": {
-      "version": "8.0.1",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "string-width": "^4.2.0",
-        "strip-ansi": "^6.0.1",
-        "wrap-ansi": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/co": {
-      "version": "4.6.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "iojs": ">= 1.0.0",
-        "node": ">= 0.12.0"
-      }
-    },
-    "node_modules/collect-v8-coverage": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/color": {
-      "version": "4.2.3",
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^2.0.1",
-        "color-string": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=12.5.0"
-      }
-    },
-    "node_modules/color-convert": {
-      "version": "2.0.1",
-      "license": "MIT",
-      "dependencies": {
-        "color-name": "~1.1.4"
-      },
-      "engines": {
-        "node": ">=7.0.0"
-      }
-    },
-    "node_modules/color-name": {
-      "version": "1.1.4",
-      "license": "MIT"
-    },
-    "node_modules/color-string": {
-      "version": "1.9.1",
-      "license": "MIT",
-      "dependencies": {
-        "color-name": "^1.0.0",
-        "simple-swizzle": "^0.2.2"
-      }
-    },
-    "node_modules/combined-stream": {
-      "version": "1.0.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "delayed-stream": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/commander": {
-      "version": "4.1.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/concat-map": {
-      "version": "0.0.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/content-disposition": {
-      "version": "0.5.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "safe-buffer": "5.2.1"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/convert-source-map": {
-      "version": "2.0.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/create-jest": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "chalk": "^4.0.0",
-        "exit": "^0.1.2",
-        "graceful-fs": "^4.2.9",
-        "jest-config": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "prompts": "^2.0.1"
-      },
-      "bin": {
-        "create-jest": "bin/create-jest.js"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/create-require": {
-      "version": "1.1.1",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/cross-spawn": {
-      "version": "7.0.3",
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.1.0",
-        "shebang-command": "^2.0.0",
-        "which": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/css.escape": {
-      "version": "1.5.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/cssesc": {
-      "version": "3.0.0",
-      "license": "MIT",
-      "bin": {
-        "cssesc": "bin/cssesc"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/cssom": {
-      "version": "0.5.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/cssstyle": {
-      "version": "2.3.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cssom": "~0.3.6"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/cssstyle/node_modules/cssom": {
-      "version": "0.3.8",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/csstype": {
-      "version": "3.1.3",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/damerau-levenshtein": {
-      "version": "1.0.8",
-      "dev": true,
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/data-urls": {
-      "version": "3.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "abab": "^2.0.6",
-        "whatwg-mimetype": "^3.0.0",
-        "whatwg-url": "^11.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/data-view-buffer": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.6",
-        "es-errors": "^1.3.0",
-        "is-data-view": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/data-view-byte-length": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "es-errors": "^1.3.0",
-        "is-data-view": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/data-view-byte-offset": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.6",
-        "es-errors": "^1.3.0",
-        "is-data-view": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/debug": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
-      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
-      "dev": true,
-      "dependencies": {
-        "ms": "^2.1.3"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "peerDependenciesMeta": {
-        "supports-color": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/decimal.js": {
-      "version": "10.4.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/dedent": {
-      "version": "1.5.3",
-      "dev": true,
-      "license": "MIT",
-      "peerDependencies": {
-        "babel-plugin-macros": "^3.1.0"
-      },
-      "peerDependenciesMeta": {
-        "babel-plugin-macros": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/deep-equal": {
-      "version": "2.2.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-buffer-byte-length": "^1.0.0",
-        "call-bind": "^1.0.5",
-        "es-get-iterator": "^1.1.3",
-        "get-intrinsic": "^1.2.2",
-        "is-arguments": "^1.1.1",
-        "is-array-buffer": "^3.0.2",
-        "is-date-object": "^1.0.5",
-        "is-regex": "^1.1.4",
-        "is-shared-array-buffer": "^1.0.2",
-        "isarray": "^2.0.5",
-        "object-is": "^1.1.5",
-        "object-keys": "^1.1.1",
-        "object.assign": "^4.1.4",
-        "regexp.prototype.flags": "^1.5.1",
-        "side-channel": "^1.0.4",
-        "which-boxed-primitive": "^1.0.2",
-        "which-collection": "^1.0.1",
-        "which-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/deep-is": {
-      "version": "0.1.4",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/deepmerge": {
-      "version": "4.3.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/define-data-property": {
-      "version": "1.1.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/define-properties": {
-      "version": "1.2.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.0.1",
-        "has-property-descriptors": "^1.0.0",
-        "object-keys": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/delayed-stream": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/depd": {
-      "version": "1.1.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/dequal": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/detect-libc": {
-      "version": "2.0.3",
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/detect-newline": {
-      "version": "3.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/didyoumean": {
-      "version": "1.2.2",
-      "license": "Apache-2.0"
-    },
-    "node_modules/diff": {
-      "version": "4.0.2",
-      "devOptional": true,
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": ">=0.3.1"
-      }
-    },
-    "node_modules/diff-sequences": {
-      "version": "29.6.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/dir-glob": {
-      "version": "3.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-type": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/dlv": {
-      "version": "1.1.3",
-      "license": "MIT"
-    },
-    "node_modules/doctrine": {
-      "version": "2.1.0",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/dom-accessibility-api": {
-      "version": "0.6.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/domexception": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "webidl-conversions": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/eastasianwidth": {
-      "version": "0.2.0",
-      "license": "MIT"
-    },
-    "node_modules/electron-to-chromium": {
-      "version": "1.4.772",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/emittery": {
-      "version": "0.13.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/emittery?sponsor=1"
-      }
-    },
-    "node_modules/emoji-regex": {
-      "version": "9.2.2",
-      "license": "MIT"
-    },
-    "node_modules/enhanced-resolve": {
-      "version": "5.16.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "graceful-fs": "^4.2.4",
-        "tapable": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/entities": {
-      "version": "4.5.0",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=0.12"
-      },
-      "funding": {
-        "url": "https://github.com/fb55/entities?sponsor=1"
-      }
-    },
-    "node_modules/error-ex": {
-      "version": "1.3.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-arrayish": "^0.2.1"
-      }
-    },
-    "node_modules/error-ex/node_modules/is-arrayish": {
-      "version": "0.2.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/es-abstract": {
-      "version": "1.23.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-buffer-byte-length": "^1.0.1",
-        "arraybuffer.prototype.slice": "^1.0.3",
-        "available-typed-arrays": "^1.0.7",
-        "call-bind": "^1.0.7",
-        "data-view-buffer": "^1.0.1",
-        "data-view-byte-length": "^1.0.1",
-        "data-view-byte-offset": "^1.0.0",
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
-        "es-set-tostringtag": "^2.0.3",
-        "es-to-primitive": "^1.2.1",
-        "function.prototype.name": "^1.1.6",
-        "get-intrinsic": "^1.2.4",
-        "get-symbol-description": "^1.0.2",
-        "globalthis": "^1.0.3",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2",
-        "has-proto": "^1.0.3",
-        "has-symbols": "^1.0.3",
-        "hasown": "^2.0.2",
-        "internal-slot": "^1.0.7",
-        "is-array-buffer": "^3.0.4",
-        "is-callable": "^1.2.7",
-        "is-data-view": "^1.0.1",
-        "is-negative-zero": "^2.0.3",
-        "is-regex": "^1.1.4",
-        "is-shared-array-buffer": "^1.0.3",
-        "is-string": "^1.0.7",
-        "is-typed-array": "^1.1.13",
-        "is-weakref": "^1.0.2",
-        "object-inspect": "^1.13.1",
-        "object-keys": "^1.1.1",
-        "object.assign": "^4.1.5",
-        "regexp.prototype.flags": "^1.5.2",
-        "safe-array-concat": "^1.1.2",
-        "safe-regex-test": "^1.0.3",
-        "string.prototype.trim": "^1.2.9",
-        "string.prototype.trimend": "^1.0.8",
-        "string.prototype.trimstart": "^1.0.8",
-        "typed-array-buffer": "^1.0.2",
-        "typed-array-byte-length": "^1.0.1",
-        "typed-array-byte-offset": "^1.0.2",
-        "typed-array-length": "^1.0.6",
-        "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.15"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/es-define-property": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.2.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-errors": {
-      "version": "1.3.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-get-iterator": {
-      "version": "1.1.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.1.3",
-        "has-symbols": "^1.0.3",
-        "is-arguments": "^1.1.1",
-        "is-map": "^2.0.2",
-        "is-set": "^2.0.2",
-        "is-string": "^1.0.7",
-        "isarray": "^2.0.5",
-        "stop-iteration-iterator": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/es-iterator-helpers": {
-      "version": "1.0.19",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.3",
-        "es-errors": "^1.3.0",
-        "es-set-tostringtag": "^2.0.3",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "globalthis": "^1.0.3",
-        "has-property-descriptors": "^1.0.2",
-        "has-proto": "^1.0.3",
-        "has-symbols": "^1.0.3",
-        "internal-slot": "^1.0.7",
-        "iterator.prototype": "^1.1.2",
-        "safe-array-concat": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-object-atoms": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-set-tostringtag": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.2.4",
-        "has-tostringtag": "^1.0.2",
-        "hasown": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/es-shim-unscopables": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "hasown": "^2.0.0"
-      }
-    },
-    "node_modules/es-to-primitive": {
-      "version": "1.2.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-callable": "^1.1.4",
-        "is-date-object": "^1.0.1",
-        "is-symbol": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/escalade": {
-      "version": "3.1.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/escape-string-regexp": {
-      "version": "1.0.5",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/escodegen": {
-      "version": "2.1.0",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "esprima": "^4.0.1",
-        "estraverse": "^5.2.0",
-        "esutils": "^2.0.2"
-      },
-      "bin": {
-        "escodegen": "bin/escodegen.js",
-        "esgenerate": "bin/esgenerate.js"
-      },
-      "engines": {
-        "node": ">=6.0"
-      },
-      "optionalDependencies": {
-        "source-map": "~0.6.1"
-      }
-    },
-    "node_modules/eslint": {
-      "version": "8.57.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/eslint-utils": "^4.2.0",
-        "@eslint-community/regexpp": "^4.6.1",
-        "@eslint/eslintrc": "^2.1.4",
-        "@eslint/js": "8.57.0",
-        "@humanwhocodes/config-array": "^0.11.14",
-        "@humanwhocodes/module-importer": "^1.0.1",
-        "@nodelib/fs.walk": "^1.2.8",
-        "@ungap/structured-clone": "^1.2.0",
-        "ajv": "^6.12.4",
-        "chalk": "^4.0.0",
-        "cross-spawn": "^7.0.2",
-        "debug": "^4.3.2",
-        "doctrine": "^3.0.0",
-        "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^7.2.2",
-        "eslint-visitor-keys": "^3.4.3",
-        "espree": "^9.6.1",
-        "esquery": "^1.4.2",
-        "esutils": "^2.0.2",
-        "fast-deep-equal": "^3.1.3",
-        "file-entry-cache": "^6.0.1",
-        "find-up": "^5.0.0",
-        "glob-parent": "^6.0.2",
-        "globals": "^13.19.0",
-        "graphemer": "^1.4.0",
-        "ignore": "^5.2.0",
-        "imurmurhash": "^0.1.4",
-        "is-glob": "^4.0.0",
-        "is-path-inside": "^3.0.3",
-        "js-yaml": "^4.1.0",
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.4.1",
-        "lodash.merge": "^4.6.2",
-        "minimatch": "^3.1.2",
-        "natural-compare": "^1.4.0",
-        "optionator": "^0.9.3",
-        "strip-ansi": "^6.0.1",
-        "text-table": "^0.2.0"
-      },
-      "bin": {
-        "eslint": "bin/eslint.js"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/eslint-config-next": {
-      "version": "14.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@next/eslint-plugin-next": "14.1.0",
-        "@rushstack/eslint-patch": "^1.3.3",
-        "@typescript-eslint/parser": "^5.4.2 || ^6.0.0",
-        "eslint-import-resolver-node": "^0.3.6",
-        "eslint-import-resolver-typescript": "^3.5.2",
-        "eslint-plugin-import": "^2.28.1",
-        "eslint-plugin-jsx-a11y": "^6.7.1",
-        "eslint-plugin-react": "^7.33.2",
-        "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705"
-      },
-      "peerDependencies": {
-        "eslint": "^7.23.0 || ^8.0.0",
-        "typescript": ">=3.3.1"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-config-prettier": {
-      "version": "8.10.0",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "eslint-config-prettier": "bin/cli.js"
-      },
-      "peerDependencies": {
-        "eslint": ">=7.0.0"
-      }
-    },
-    "node_modules/eslint-config-universe": {
-      "version": "12.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/eslint-plugin": "^6.0.0",
-        "@typescript-eslint/parser": "^6.0.0",
-        "eslint-config-prettier": "^8.8.0",
-        "eslint-plugin-import": "^2.27.5",
-        "eslint-plugin-node": "^11.1.0",
-        "eslint-plugin-prettier": "^5.0.0",
-        "eslint-plugin-react": "^7.32.2",
-        "eslint-plugin-react-hooks": "^4.6.0"
-      },
-      "peerDependencies": {
-        "eslint": ">=8.10",
-        "prettier": ">=3"
-      },
-      "peerDependenciesMeta": {
-        "prettier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-import-resolver-node": {
-      "version": "0.3.9",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^3.2.7",
-        "is-core-module": "^2.13.0",
-        "resolve": "^1.22.4"
-      }
-    },
-    "node_modules/eslint-import-resolver-node/node_modules/debug": {
-      "version": "3.2.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-import-resolver-typescript": {
-      "version": "3.6.1",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "debug": "^4.3.4",
-        "enhanced-resolve": "^5.12.0",
-        "eslint-module-utils": "^2.7.4",
-        "fast-glob": "^3.3.1",
-        "get-tsconfig": "^4.5.0",
-        "is-core-module": "^2.11.0",
-        "is-glob": "^4.0.3"
-      },
-      "engines": {
-        "node": "^14.18.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
-      },
-      "peerDependencies": {
-        "eslint": "*",
-        "eslint-plugin-import": "*"
-      }
-    },
-    "node_modules/eslint-module-utils": {
-      "version": "2.8.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^3.2.7"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependenciesMeta": {
-        "eslint": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-module-utils/node_modules/debug": {
-      "version": "3.2.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-plugin-es": {
-      "version": "3.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-utils": "^2.0.0",
-        "regexpp": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      },
-      "peerDependencies": {
-        "eslint": ">=4.19.1"
-      }
-    },
-    "node_modules/eslint-plugin-i18next": {
-      "version": "6.0.3",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "lodash": "^4.17.21",
-        "requireindex": "~1.1.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/eslint-plugin-import": {
-      "version": "2.29.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-includes": "^3.1.7",
-        "array.prototype.findlastindex": "^1.2.3",
-        "array.prototype.flat": "^1.3.2",
-        "array.prototype.flatmap": "^1.3.2",
-        "debug": "^3.2.7",
-        "doctrine": "^2.1.0",
-        "eslint-import-resolver-node": "^0.3.9",
-        "eslint-module-utils": "^2.8.0",
-        "hasown": "^2.0.0",
-        "is-core-module": "^2.13.1",
-        "is-glob": "^4.0.3",
-        "minimatch": "^3.1.2",
-        "object.fromentries": "^2.0.7",
-        "object.groupby": "^1.0.1",
-        "object.values": "^1.1.7",
-        "semver": "^6.3.1",
-        "tsconfig-paths": "^3.15.0"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependencies": {
-        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/debug": {
-      "version": "3.2.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/semver": {
-      "version": "6.3.1",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/eslint-plugin-jsx-a11y": {
-      "version": "6.8.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/runtime": "^7.23.2",
-        "aria-query": "^5.3.0",
-        "array-includes": "^3.1.7",
-        "array.prototype.flatmap": "^1.3.2",
-        "ast-types-flow": "^0.0.8",
-        "axe-core": "=4.7.0",
-        "axobject-query": "^3.2.1",
-        "damerau-levenshtein": "^1.0.8",
-        "emoji-regex": "^9.2.2",
-        "es-iterator-helpers": "^1.0.15",
-        "hasown": "^2.0.0",
-        "jsx-ast-utils": "^3.3.5",
-        "language-tags": "^1.0.9",
-        "minimatch": "^3.1.2",
-        "object.entries": "^1.1.7",
-        "object.fromentries": "^2.0.7"
-      },
-      "engines": {
-        "node": ">=4.0"
-      },
-      "peerDependencies": {
-        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
-      }
-    },
-    "node_modules/eslint-plugin-node": {
-      "version": "11.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-plugin-es": "^3.0.0",
-        "eslint-utils": "^2.0.0",
-        "ignore": "^5.1.1",
-        "minimatch": "^3.0.4",
-        "resolve": "^1.10.1",
-        "semver": "^6.1.0"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      },
-      "peerDependencies": {
-        "eslint": ">=5.16.0"
-      }
-    },
-    "node_modules/eslint-plugin-node/node_modules/semver": {
-      "version": "6.3.1",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/eslint-plugin-prettier": {
-      "version": "5.1.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "prettier-linter-helpers": "^1.0.0",
-        "synckit": "^0.8.6"
-      },
-      "engines": {
-        "node": "^14.18.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint-plugin-prettier"
-      },
-      "peerDependencies": {
-        "@types/eslint": ">=8.0.0",
-        "eslint": ">=8.0.0",
-        "eslint-config-prettier": "*",
-        "prettier": ">=3.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/eslint": {
-          "optional": true
-        },
-        "eslint-config-prettier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-plugin-react": {
-      "version": "7.34.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-includes": "^3.1.7",
-        "array.prototype.findlast": "^1.2.4",
-        "array.prototype.flatmap": "^1.3.2",
-        "array.prototype.toreversed": "^1.1.2",
-        "array.prototype.tosorted": "^1.1.3",
-        "doctrine": "^2.1.0",
-        "es-iterator-helpers": "^1.0.17",
-        "estraverse": "^5.3.0",
-        "jsx-ast-utils": "^2.4.1 || ^3.0.0",
-        "minimatch": "^3.1.2",
-        "object.entries": "^1.1.7",
-        "object.fromentries": "^2.0.7",
-        "object.hasown": "^1.1.3",
-        "object.values": "^1.1.7",
-        "prop-types": "^15.8.1",
-        "resolve": "^2.0.0-next.5",
-        "semver": "^6.3.1",
-        "string.prototype.matchall": "^4.0.10"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependencies": {
-        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
-      }
-    },
-    "node_modules/eslint-plugin-react-hooks": {
-      "version": "4.6.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "peerDependencies": {
-        "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
-      }
-    },
-    "node_modules/eslint-plugin-react/node_modules/resolve": {
-      "version": "2.0.0-next.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-core-module": "^2.13.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/eslint-plugin-react/node_modules/semver": {
-      "version": "6.3.1",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/eslint-plugin-unused-imports": {
-      "version": "3.2.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-rule-composer": "^0.3.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "peerDependencies": {
-        "@typescript-eslint/eslint-plugin": "6 - 7",
-        "eslint": "8"
-      },
-      "peerDependenciesMeta": {
-        "@typescript-eslint/eslint-plugin": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-rule-composer": {
-      "version": "0.3.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4.0.0"
-      }
-    },
-    "node_modules/eslint-scope": {
-      "version": "7.2.2",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "esrecurse": "^4.3.0",
-        "estraverse": "^5.2.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/eslint-utils": {
-      "version": "2.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-visitor-keys": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      }
-    },
-    "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
-      "version": "1.3.0",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/eslint/node_modules/doctrine": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/eslint/node_modules/escape-string-regexp": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint/node_modules/find-up": {
-      "version": "5.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "locate-path": "^6.0.0",
-        "path-exists": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/espree": {
-      "version": "9.6.1",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "acorn": "^8.9.0",
-        "acorn-jsx": "^5.3.2",
-        "eslint-visitor-keys": "^3.4.1"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/esprima": {
-      "version": "4.0.1",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "bin": {
-        "esparse": "bin/esparse.js",
-        "esvalidate": "bin/esvalidate.js"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/esquery": {
-      "version": "1.5.0",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "estraverse": "^5.1.0"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/esrecurse": {
-      "version": "4.3.0",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "estraverse": "^5.2.0"
-      },
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/estraverse": {
-      "version": "5.3.0",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/esutils": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/execa": {
-      "version": "5.1.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cross-spawn": "^7.0.3",
-        "get-stream": "^6.0.0",
-        "human-signals": "^2.1.0",
-        "is-stream": "^2.0.0",
-        "merge-stream": "^2.0.0",
-        "npm-run-path": "^4.0.1",
-        "onetime": "^5.1.2",
-        "signal-exit": "^3.0.3",
-        "strip-final-newline": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/execa?sponsor=1"
-      }
-    },
-    "node_modules/exit": {
-      "version": "0.1.2",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/expect": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/expect-utils": "^29.7.0",
-        "jest-get-type": "^29.6.3",
-        "jest-matcher-utils": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/fast-deep-equal": {
-      "version": "3.1.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/fast-diff": {
-      "version": "1.3.0",
-      "dev": true,
-      "license": "Apache-2.0"
-    },
-    "node_modules/fast-glob": {
-      "version": "3.3.2",
-      "license": "MIT",
-      "dependencies": {
-        "@nodelib/fs.stat": "^2.0.2",
-        "@nodelib/fs.walk": "^1.2.3",
-        "glob-parent": "^5.1.2",
-        "merge2": "^1.3.0",
-        "micromatch": "^4.0.4"
-      },
-      "engines": {
-        "node": ">=8.6.0"
-      }
-    },
-    "node_modules/fast-glob/node_modules/glob-parent": {
-      "version": "5.1.2",
-      "license": "ISC",
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/fast-json-stable-stringify": {
-      "version": "2.1.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/fast-levenshtein": {
-      "version": "2.0.6",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/fastq": {
-      "version": "1.17.1",
-      "license": "ISC",
-      "dependencies": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "node_modules/fb-watchman": {
-      "version": "2.0.2",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "bser": "2.1.1"
-      }
-    },
-    "node_modules/file-entry-cache": {
-      "version": "6.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "flat-cache": "^3.0.4"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      }
-    },
-    "node_modules/fill-range": {
-      "version": "7.0.1",
-      "license": "MIT",
-      "dependencies": {
-        "to-regex-range": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/find-up": {
-      "version": "4.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "locate-path": "^5.0.0",
-        "path-exists": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/find-up/node_modules/locate-path": {
-      "version": "5.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-locate": "^4.1.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/find-up/node_modules/p-limit": {
-      "version": "2.3.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-try": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/find-up/node_modules/p-locate": {
-      "version": "4.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-limit": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/flat-cache": {
-      "version": "3.2.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "flatted": "^3.2.9",
-        "keyv": "^4.5.3",
-        "rimraf": "^3.0.2"
-      },
-      "engines": {
-        "node": "^10.12.0 || >=12.0.0"
-      }
-    },
-    "node_modules/flatted": {
-      "version": "3.3.1",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/for-each": {
-      "version": "0.3.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-callable": "^1.1.3"
-      }
-    },
-    "node_modules/foreground-child": {
-      "version": "3.1.1",
-      "license": "ISC",
-      "dependencies": {
-        "cross-spawn": "^7.0.0",
-        "signal-exit": "^4.0.1"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/foreground-child/node_modules/signal-exit": {
-      "version": "4.1.0",
-      "license": "ISC",
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/form-data": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.8",
-        "mime-types": "^2.1.12"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/fraction.js": {
-      "version": "4.3.7",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "type": "patreon",
-        "url": "https://github.com/sponsors/rawify"
-      }
-    },
-    "node_modules/fresh": {
-      "version": "0.5.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/fs.realpath": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/fsevents": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
-      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
-      "hasInstallScript": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
-      }
-    },
-    "node_modules/function-bind": {
-      "version": "1.1.2",
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/function.prototype.name": {
-      "version": "1.1.6",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "functions-have-names": "^1.2.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/functions-have-names": {
-      "version": "1.2.3",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/gensync": {
-      "version": "1.0.0-beta.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/get-caller-file": {
-      "version": "2.0.5",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": "6.* || 8.* || >= 10.*"
-      }
-    },
-    "node_modules/get-intrinsic": {
-      "version": "1.2.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "has-proto": "^1.0.1",
-        "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/get-package-type": {
-      "version": "0.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8.0.0"
-      }
-    },
-    "node_modules/get-stream": {
-      "version": "6.0.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/get-symbol-description": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.5",
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/get-tsconfig": {
-      "version": "4.7.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "resolve-pkg-maps": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
-      }
-    },
-    "node_modules/glob": {
-      "version": "7.2.3",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/glob-parent": {
-      "version": "6.0.2",
-      "license": "ISC",
-      "dependencies": {
-        "is-glob": "^4.0.3"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/globals": {
-      "version": "13.24.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "type-fest": "^0.20.2"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/globalthis": {
-      "version": "1.0.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-properties": "^1.2.1",
-        "gopd": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/globby": {
-      "version": "11.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-union": "^2.1.0",
-        "dir-glob": "^3.0.1",
-        "fast-glob": "^3.2.9",
-        "ignore": "^5.2.0",
-        "merge2": "^1.4.1",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/gopd": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.1.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/graceful-fs": {
-      "version": "4.2.11",
-      "license": "ISC"
-    },
-    "node_modules/graphemer": {
-      "version": "1.4.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/has-bigints": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-flag": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/has-property-descriptors": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-proto": {
-      "version": "1.0.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-symbols": {
-      "version": "1.0.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-tostringtag": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-symbols": "^1.0.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/hasown": {
-      "version": "2.0.2",
-      "license": "MIT",
-      "dependencies": {
-        "function-bind": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/html-encoding-sniffer": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "whatwg-encoding": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/html-escaper": {
-      "version": "2.0.2",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/http-proxy-agent": {
-      "version": "5.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@tootallnate/once": "2",
-        "agent-base": "6",
-        "debug": "4"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/https-proxy-agent": {
-      "version": "5.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "agent-base": "6",
-        "debug": "4"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/human-signals": {
-      "version": "2.1.0",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10.17.0"
-      }
-    },
-    "node_modules/husky": {
-      "version": "9.0.11",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "husky": "bin.mjs"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/typicode"
-      }
-    },
-    "node_modules/iconv-lite": {
-      "version": "0.6.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "safer-buffer": ">= 2.1.2 < 3.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/ignore": {
-      "version": "5.3.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/import-fresh": {
-      "version": "3.3.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "parent-module": "^1.0.0",
-        "resolve-from": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/import-fresh/node_modules/resolve-from": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/import-local": {
-      "version": "3.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "pkg-dir": "^4.2.0",
-        "resolve-cwd": "^3.0.0"
-      },
-      "bin": {
-        "import-local-fixture": "fixtures/cli.js"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/imurmurhash": {
-      "version": "0.1.4",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.19"
-      }
-    },
-    "node_modules/indent-string": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/inflight": {
-      "version": "1.0.6",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "node_modules/inherits": {
-      "version": "2.0.4",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/internal-slot": {
-      "version": "1.0.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "es-errors": "^1.3.0",
-        "hasown": "^2.0.0",
-        "side-channel": "^1.0.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/intl-messageformat": {
-      "version": "10.5.14",
-      "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz",
-      "integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==",
-      "dependencies": {
-        "@formatjs/ecma402-abstract": "2.0.0",
-        "@formatjs/fast-memoize": "2.2.0",
-        "@formatjs/icu-messageformat-parser": "2.7.8",
-        "tslib": "^2.4.0"
-      }
-    },
-    "node_modules/is-arguments": {
-      "version": "1.1.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-array-buffer": {
-      "version": "3.0.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-arrayish": {
-      "version": "0.3.2",
-      "license": "MIT"
-    },
-    "node_modules/is-async-function": {
-      "version": "2.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-bigint": {
-      "version": "1.0.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-bigints": "^1.0.1"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-binary-path": {
-      "version": "2.1.0",
-      "license": "MIT",
-      "dependencies": {
-        "binary-extensions": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-boolean-object": {
-      "version": "1.1.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-callable": {
-      "version": "1.2.7",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-core-module": {
-      "version": "2.13.1",
-      "license": "MIT",
-      "dependencies": {
-        "hasown": "^2.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-data-view": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-date-object": {
-      "version": "1.0.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-extglob": {
-      "version": "2.1.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-finalizationregistry": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-fullwidth-code-point": {
-      "version": "3.0.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-generator-fn": {
-      "version": "2.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/is-generator-function": {
-      "version": "1.0.10",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-glob": {
-      "version": "4.0.3",
-      "license": "MIT",
-      "dependencies": {
-        "is-extglob": "^2.1.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-map": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-negative-zero": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-number": {
-      "version": "7.0.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.12.0"
-      }
-    },
-    "node_modules/is-number-object": {
-      "version": "1.0.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-path-inside": {
-      "version": "3.0.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-potential-custom-element-name": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/is-regex": {
-      "version": "1.1.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-set": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-shared-array-buffer": {
-      "version": "1.0.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-stream": {
-      "version": "2.0.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/is-string": {
-      "version": "1.0.7",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-symbol": {
-      "version": "1.0.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-symbols": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-typed-array": {
-      "version": "1.1.13",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "which-typed-array": "^1.1.14"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-weakmap": {
-      "version": "2.0.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-weakref": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-weakset": {
-      "version": "2.0.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "get-intrinsic": "^1.2.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/isarray": {
-      "version": "2.0.5",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/isexe": {
-      "version": "2.0.0",
-      "license": "ISC"
-    },
-    "node_modules/isomorphic-fetch": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "node-fetch": "^2.6.1",
-        "whatwg-fetch": "^3.4.1"
-      }
-    },
-    "node_modules/istanbul-lib-coverage": {
-      "version": "3.2.2",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/istanbul-lib-instrument": {
-      "version": "5.2.1",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "@babel/core": "^7.12.3",
-        "@babel/parser": "^7.14.7",
-        "@istanbuljs/schema": "^0.1.2",
-        "istanbul-lib-coverage": "^3.2.0",
-        "semver": "^6.3.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/istanbul-lib-instrument/node_modules/semver": {
-      "version": "6.3.1",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/istanbul-lib-report": {
-      "version": "3.0.1",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "istanbul-lib-coverage": "^3.0.0",
-        "make-dir": "^4.0.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/istanbul-lib-source-maps": {
-      "version": "4.0.1",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "debug": "^4.1.1",
-        "istanbul-lib-coverage": "^3.0.0",
-        "source-map": "^0.6.1"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/istanbul-reports": {
-      "version": "3.1.7",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "html-escaper": "^2.0.0",
-        "istanbul-lib-report": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/iterator.prototype": {
-      "version": "1.1.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-properties": "^1.2.1",
-        "get-intrinsic": "^1.2.1",
-        "has-symbols": "^1.0.3",
-        "reflect.getprototypeof": "^1.0.4",
-        "set-function-name": "^2.0.1"
-      }
-    },
-    "node_modules/jackspeak": {
-      "version": "2.3.6",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "@isaacs/cliui": "^8.0.2"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      },
-      "optionalDependencies": {
-        "@pkgjs/parseargs": "^0.11.0"
-      }
-    },
-    "node_modules/jest": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/core": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "import-local": "^3.0.2",
-        "jest-cli": "^29.7.0"
-      },
-      "bin": {
-        "jest": "bin/jest.js"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-changed-files": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "execa": "^5.0.0",
-        "jest-util": "^29.7.0",
-        "p-limit": "^3.1.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-circus": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/expect": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "co": "^4.6.0",
-        "dedent": "^1.0.0",
-        "is-generator-fn": "^2.0.0",
-        "jest-each": "^29.7.0",
-        "jest-matcher-utils": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-runtime": "^29.7.0",
-        "jest-snapshot": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "p-limit": "^3.1.0",
-        "pretty-format": "^29.7.0",
-        "pure-rand": "^6.0.0",
-        "slash": "^3.0.0",
-        "stack-utils": "^2.0.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-cli": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/core": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "chalk": "^4.0.0",
-        "create-jest": "^29.7.0",
-        "exit": "^0.1.2",
-        "import-local": "^3.0.2",
-        "jest-config": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-validate": "^29.7.0",
-        "yargs": "^17.3.1"
-      },
-      "bin": {
-        "jest": "bin/jest.js"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "node-notifier": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-config": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/core": "^7.11.6",
-        "@jest/test-sequencer": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "babel-jest": "^29.7.0",
-        "chalk": "^4.0.0",
-        "ci-info": "^3.2.0",
-        "deepmerge": "^4.2.2",
-        "glob": "^7.1.3",
-        "graceful-fs": "^4.2.9",
-        "jest-circus": "^29.7.0",
-        "jest-environment-node": "^29.7.0",
-        "jest-get-type": "^29.6.3",
-        "jest-regex-util": "^29.6.3",
-        "jest-resolve": "^29.7.0",
-        "jest-runner": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-validate": "^29.7.0",
-        "micromatch": "^4.0.4",
-        "parse-json": "^5.2.0",
-        "pretty-format": "^29.7.0",
-        "slash": "^3.0.0",
-        "strip-json-comments": "^3.1.1"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "@types/node": "*",
-        "ts-node": ">=9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/node": {
-          "optional": true
-        },
-        "ts-node": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-diff": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^4.0.0",
-        "diff-sequences": "^29.6.3",
-        "jest-get-type": "^29.6.3",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-docblock": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "detect-newline": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-each": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "chalk": "^4.0.0",
-        "jest-get-type": "^29.6.3",
-        "jest-util": "^29.7.0",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-environment-jsdom": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/fake-timers": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/jsdom": "^20.0.0",
-        "@types/node": "*",
-        "jest-mock": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jsdom": "^20.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "peerDependencies": {
-        "canvas": "^2.5.0"
-      },
-      "peerDependenciesMeta": {
-        "canvas": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-environment-node": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/fake-timers": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "jest-mock": "^29.7.0",
-        "jest-util": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-get-type": {
-      "version": "29.6.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-haste-map": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@types/graceful-fs": "^4.1.3",
-        "@types/node": "*",
-        "anymatch": "^3.0.3",
-        "fb-watchman": "^2.0.0",
-        "graceful-fs": "^4.2.9",
-        "jest-regex-util": "^29.6.3",
-        "jest-util": "^29.7.0",
-        "jest-worker": "^29.7.0",
-        "micromatch": "^4.0.4",
-        "walker": "^1.0.8"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      },
-      "optionalDependencies": {
-        "fsevents": "^2.3.2"
-      }
-    },
-    "node_modules/jest-leak-detector": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "jest-get-type": "^29.6.3",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-matcher-utils": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^4.0.0",
-        "jest-diff": "^29.7.0",
-        "jest-get-type": "^29.6.3",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-message-util": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.12.13",
-        "@jest/types": "^29.6.3",
-        "@types/stack-utils": "^2.0.0",
-        "chalk": "^4.0.0",
-        "graceful-fs": "^4.2.9",
-        "micromatch": "^4.0.4",
-        "pretty-format": "^29.7.0",
-        "slash": "^3.0.0",
-        "stack-utils": "^2.0.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-mock": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "jest-util": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-pnp-resolver": {
-      "version": "1.2.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      },
-      "peerDependencies": {
-        "jest-resolve": "*"
-      },
-      "peerDependenciesMeta": {
-        "jest-resolve": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jest-regex-util": {
-      "version": "29.6.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-resolve": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "chalk": "^4.0.0",
-        "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.7.0",
-        "jest-pnp-resolver": "^1.2.2",
-        "jest-util": "^29.7.0",
-        "jest-validate": "^29.7.0",
-        "resolve": "^1.20.0",
-        "resolve.exports": "^2.0.0",
-        "slash": "^3.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-resolve-dependencies": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "jest-regex-util": "^29.6.3",
-        "jest-snapshot": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-runner": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/console": "^29.7.0",
-        "@jest/environment": "^29.7.0",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "emittery": "^0.13.1",
-        "graceful-fs": "^4.2.9",
-        "jest-docblock": "^29.7.0",
-        "jest-environment-node": "^29.7.0",
-        "jest-haste-map": "^29.7.0",
-        "jest-leak-detector": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-resolve": "^29.7.0",
-        "jest-runtime": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "jest-watcher": "^29.7.0",
-        "jest-worker": "^29.7.0",
-        "p-limit": "^3.1.0",
-        "source-map-support": "0.5.13"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-runtime": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/environment": "^29.7.0",
-        "@jest/fake-timers": "^29.7.0",
-        "@jest/globals": "^29.7.0",
-        "@jest/source-map": "^29.6.3",
-        "@jest/test-result": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "cjs-module-lexer": "^1.0.0",
-        "collect-v8-coverage": "^1.0.0",
-        "glob": "^7.1.3",
-        "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-mock": "^29.7.0",
-        "jest-regex-util": "^29.6.3",
-        "jest-resolve": "^29.7.0",
-        "jest-snapshot": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "slash": "^3.0.0",
-        "strip-bom": "^4.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-runtime/node_modules/strip-bom": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/jest-snapshot": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/core": "^7.11.6",
-        "@babel/generator": "^7.7.2",
-        "@babel/plugin-syntax-jsx": "^7.7.2",
-        "@babel/plugin-syntax-typescript": "^7.7.2",
-        "@babel/types": "^7.3.3",
-        "@jest/expect-utils": "^29.7.0",
-        "@jest/transform": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "babel-preset-current-node-syntax": "^1.0.0",
-        "chalk": "^4.0.0",
-        "expect": "^29.7.0",
-        "graceful-fs": "^4.2.9",
-        "jest-diff": "^29.7.0",
-        "jest-get-type": "^29.6.3",
-        "jest-matcher-utils": "^29.7.0",
-        "jest-message-util": "^29.7.0",
-        "jest-util": "^29.7.0",
-        "natural-compare": "^1.4.0",
-        "pretty-format": "^29.7.0",
-        "semver": "^7.5.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-util": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "chalk": "^4.0.0",
-        "ci-info": "^3.2.0",
-        "graceful-fs": "^4.2.9",
-        "picomatch": "^2.2.3"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-validate": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/types": "^29.6.3",
-        "camelcase": "^6.2.0",
-        "chalk": "^4.0.0",
-        "jest-get-type": "^29.6.3",
-        "leven": "^3.1.0",
-        "pretty-format": "^29.7.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-validate/node_modules/camelcase": {
-      "version": "6.3.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/jest-watcher": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/test-result": "^29.7.0",
-        "@jest/types": "^29.6.3",
-        "@types/node": "*",
-        "ansi-escapes": "^4.2.1",
-        "chalk": "^4.0.0",
-        "emittery": "^0.13.1",
-        "jest-util": "^29.7.0",
-        "string-length": "^4.0.1"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-worker": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "*",
-        "jest-util": "^29.7.0",
-        "merge-stream": "^2.0.0",
-        "supports-color": "^8.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/jest-worker/node_modules/supports-color": {
-      "version": "8.1.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/supports-color?sponsor=1"
-      }
-    },
-    "node_modules/jiti": {
-      "version": "1.21.0",
-      "license": "MIT",
-      "bin": {
-        "jiti": "bin/jiti.js"
-      }
-    },
-    "node_modules/jotai": {
-      "version": "2.10.3",
-      "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.10.3.tgz",
-      "integrity": "sha512-Nnf4IwrLhNfuz2JOQLI0V/AgwcpxvVy8Ec8PidIIDeRi4KCFpwTFIpHAAcU+yCgnw/oASYElq9UY0YdUUegsSA==",
-      "engines": {
-        "node": ">=12.20.0"
-      },
-      "peerDependencies": {
-        "@types/react": ">=17.0.0",
-        "react": ">=17.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@types/react": {
-          "optional": true
-        },
-        "react": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/js-tokens": {
-      "version": "4.0.0",
-      "license": "MIT"
-    },
-    "node_modules/js-yaml": {
-      "version": "4.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
-    "node_modules/jsdom": {
-      "version": "20.0.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "abab": "^2.0.6",
-        "acorn": "^8.8.1",
-        "acorn-globals": "^7.0.0",
-        "cssom": "^0.5.0",
-        "cssstyle": "^2.3.0",
-        "data-urls": "^3.0.2",
-        "decimal.js": "^10.4.2",
-        "domexception": "^4.0.0",
-        "escodegen": "^2.0.0",
-        "form-data": "^4.0.0",
-        "html-encoding-sniffer": "^3.0.0",
-        "http-proxy-agent": "^5.0.0",
-        "https-proxy-agent": "^5.0.1",
-        "is-potential-custom-element-name": "^1.0.1",
-        "nwsapi": "^2.2.2",
-        "parse5": "^7.1.1",
-        "saxes": "^6.0.0",
-        "symbol-tree": "^3.2.4",
-        "tough-cookie": "^4.1.2",
-        "w3c-xmlserializer": "^4.0.0",
-        "webidl-conversions": "^7.0.0",
-        "whatwg-encoding": "^2.0.0",
-        "whatwg-mimetype": "^3.0.0",
-        "whatwg-url": "^11.0.0",
-        "ws": "^8.11.0",
-        "xml-name-validator": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "peerDependencies": {
-        "canvas": "^2.5.0"
-      },
-      "peerDependenciesMeta": {
-        "canvas": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jsesc": {
-      "version": "2.5.2",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "jsesc": "bin/jsesc"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/json-buffer": {
-      "version": "3.0.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json-parse-even-better-errors": {
-      "version": "2.3.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json-schema-traverse": {
-      "version": "0.4.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/json5": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "minimist": "^1.2.0"
-      },
-      "bin": {
-        "json5": "lib/cli.js"
-      }
-    },
-    "node_modules/jsx-ast-utils": {
-      "version": "3.3.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "array-includes": "^3.1.6",
-        "array.prototype.flat": "^1.3.1",
-        "object.assign": "^4.1.4",
-        "object.values": "^1.1.6"
-      },
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/keyv": {
-      "version": "4.5.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "json-buffer": "3.0.1"
-      }
-    },
-    "node_modules/kleur": {
-      "version": "3.0.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/language-subtag-registry": {
-      "version": "0.3.22",
-      "dev": true,
-      "license": "CC0-1.0"
-    },
-    "node_modules/language-tags": {
-      "version": "1.0.9",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "language-subtag-registry": "^0.3.20"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/leven": {
-      "version": "3.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/levn": {
-      "version": "0.4.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "prelude-ls": "^1.2.1",
-        "type-check": "~0.4.0"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/lilconfig": {
-      "version": "2.1.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/lines-and-columns": {
-      "version": "1.2.4",
-      "license": "MIT"
-    },
-    "node_modules/locate-path": {
-      "version": "6.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-locate": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/lodash": {
-      "version": "4.17.21",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/loose-envify": {
-      "version": "1.4.0",
-      "license": "MIT",
-      "dependencies": {
-        "js-tokens": "^3.0.0 || ^4.0.0"
-      },
-      "bin": {
-        "loose-envify": "cli.js"
-      }
-    },
-    "node_modules/lru-cache": {
-      "version": "10.4.3",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
-      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
-    },
-    "node_modules/lz-string": {
-      "version": "1.5.0",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "lz-string": "bin/bin.js"
-      }
-    },
-    "node_modules/make-dir": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "semver": "^7.5.3"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/make-error": {
-      "version": "1.3.6",
-      "devOptional": true,
-      "license": "ISC"
-    },
-    "node_modules/makeerror": {
-      "version": "1.0.12",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "tmpl": "1.0.5"
-      }
-    },
-    "node_modules/media-typer": {
-      "version": "0.3.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/merge-descriptors": {
-      "version": "1.0.3",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/merge-stream": {
-      "version": "2.0.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/merge2": {
-      "version": "1.4.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/methods": {
-      "version": "1.1.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/micromatch": {
-      "version": "4.0.5",
-      "license": "MIT",
-      "dependencies": {
-        "braces": "^3.0.2",
-        "picomatch": "^2.3.1"
-      },
-      "engines": {
-        "node": ">=8.6"
-      }
-    },
-    "node_modules/mime": {
-      "version": "1.6.0",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "mime": "cli.js"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/mime-db": {
-      "version": "1.52.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/mime-types": {
-      "version": "2.1.35",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "mime-db": "1.52.0"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/mimic-fn": {
-      "version": "2.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/min-indent": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/minimatch": {
-      "version": "3.1.2",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/minimatch/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/minimist": {
-      "version": "1.2.8",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/minipass": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
-      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      }
-    },
-    "node_modules/ms": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
-      "dev": true
-    },
-    "node_modules/mz": {
-      "version": "2.7.0",
-      "license": "MIT",
-      "dependencies": {
-        "any-promise": "^1.0.0",
-        "object-assign": "^4.0.1",
-        "thenify-all": "^1.0.0"
-      }
-    },
-    "node_modules/nanoid": {
-      "version": "3.3.7",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "bin": {
-        "nanoid": "bin/nanoid.cjs"
-      },
-      "engines": {
-        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
-      }
-    },
-    "node_modules/natural-compare": {
-      "version": "1.4.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/negotiator": {
-      "version": "0.6.3",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/next": {
-      "version": "14.1.0",
-      "license": "MIT",
-      "dependencies": {
-        "@next/env": "14.1.0",
-        "@swc/helpers": "0.5.2",
-        "busboy": "1.6.0",
-        "caniuse-lite": "^1.0.30001579",
-        "graceful-fs": "^4.2.11",
-        "postcss": "8.4.31",
-        "styled-jsx": "5.1.1"
-      },
-      "bin": {
-        "next": "dist/bin/next"
-      },
-      "engines": {
-        "node": ">=18.17.0"
-      },
-      "optionalDependencies": {
-        "@next/swc-darwin-arm64": "14.1.0",
-        "@next/swc-darwin-x64": "14.1.0",
-        "@next/swc-linux-arm64-gnu": "14.1.0",
-        "@next/swc-linux-arm64-musl": "14.1.0",
-        "@next/swc-linux-x64-gnu": "14.1.0",
-        "@next/swc-linux-x64-musl": "14.1.0",
-        "@next/swc-win32-arm64-msvc": "14.1.0",
-        "@next/swc-win32-ia32-msvc": "14.1.0",
-        "@next/swc-win32-x64-msvc": "14.1.0"
-      },
-      "peerDependencies": {
-        "@opentelemetry/api": "^1.1.0",
-        "react": "^18.2.0",
-        "react-dom": "^18.2.0",
-        "sass": "^1.3.0"
-      },
-      "peerDependenciesMeta": {
-        "@opentelemetry/api": {
-          "optional": true
-        },
-        "sass": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/next-intl": {
-      "version": "3.19.4",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://github.com/sponsors/amannn"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "@formatjs/intl-localematcher": "^0.5.4",
-        "negotiator": "^0.6.3",
-        "use-intl": "^3.19.4"
-      },
-      "peerDependencies": {
-        "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0",
-        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
-      }
-    },
-    "node_modules/node-fetch": {
-      "version": "2.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "whatwg-url": "^5.0.0"
-      },
-      "engines": {
-        "node": "4.x || >=6.0.0"
-      },
-      "peerDependencies": {
-        "encoding": "^0.1.0"
-      },
-      "peerDependenciesMeta": {
-        "encoding": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/node-fetch/node_modules/webidl-conversions": {
-      "version": "3.0.1",
-      "dev": true,
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/node-fetch/node_modules/whatwg-url": {
-      "version": "5.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
-      }
-    },
-    "node_modules/node-int64": {
-      "version": "0.4.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/node-mocks-http": {
-      "version": "1.14.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/express": "^4.17.21",
-        "@types/node": "^20.10.6",
-        "accepts": "^1.3.7",
-        "content-disposition": "^0.5.3",
-        "depd": "^1.1.0",
-        "fresh": "^0.5.2",
-        "merge-descriptors": "^1.0.1",
-        "methods": "^1.1.2",
-        "mime": "^1.3.4",
-        "parseurl": "^1.3.3",
-        "range-parser": "^1.2.0",
-        "type-is": "^1.6.18"
-      },
-      "engines": {
-        "node": ">=14"
-      }
-    },
-    "node_modules/node-releases": {
-      "version": "2.0.14",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/normalize-path": {
-      "version": "3.0.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/normalize-range": {
-      "version": "0.1.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/npm-run-path": {
-      "version": "4.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "path-key": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/nwsapi": {
-      "version": "2.2.10",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/object-assign": {
-      "version": "4.1.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/object-hash": {
-      "version": "3.0.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/object-inspect": {
-      "version": "1.13.1",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object-is": {
-      "version": "1.1.6",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object-keys": {
-      "version": "1.1.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.assign": {
-      "version": "4.1.5",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.5",
-        "define-properties": "^1.2.1",
-        "has-symbols": "^1.0.3",
-        "object-keys": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object.entries": {
-      "version": "1.1.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.fromentries": {
-      "version": "2.0.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object.groupby": {
-      "version": "1.0.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.hasown": {
-      "version": "1.1.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object.values": {
-      "version": "1.2.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/once": {
-      "version": "1.4.0",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "wrappy": "1"
-      }
-    },
-    "node_modules/onetime": {
-      "version": "5.1.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "mimic-fn": "^2.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/optionator": {
-      "version": "0.9.4",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "deep-is": "^0.1.3",
-        "fast-levenshtein": "^2.0.6",
-        "levn": "^0.4.1",
-        "prelude-ls": "^1.2.1",
-        "type-check": "^0.4.0",
-        "word-wrap": "^1.2.5"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/p-limit": {
-      "version": "3.1.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "yocto-queue": "^0.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/p-locate": {
-      "version": "5.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "p-limit": "^3.0.2"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/p-try": {
-      "version": "2.2.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/package-json-from-dist": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
-      "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
-    },
-    "node_modules/parent-module": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "callsites": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/parse-json": {
-      "version": "5.2.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@babel/code-frame": "^7.0.0",
-        "error-ex": "^1.3.1",
-        "json-parse-even-better-errors": "^2.3.0",
-        "lines-and-columns": "^1.1.6"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/parse5": {
-      "version": "7.1.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "entities": "^4.4.0"
-      },
-      "funding": {
-        "url": "https://github.com/inikulin/parse5?sponsor=1"
-      }
-    },
-    "node_modules/parseurl": {
-      "version": "1.3.3",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/path-exists": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/path-is-absolute": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/path-key": {
-      "version": "3.1.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/path-parse": {
-      "version": "1.0.7",
-      "license": "MIT"
-    },
-    "node_modules/path-scurry": {
-      "version": "1.11.1",
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "lru-cache": "^10.2.0",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/path-type": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/picocolors": {
-      "version": "1.0.1",
-      "license": "ISC"
-    },
-    "node_modules/picomatch": {
-      "version": "2.3.1",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/jonschlinkert"
-      }
-    },
-    "node_modules/pify": {
-      "version": "2.3.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/pirates": {
-      "version": "4.0.6",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/pkg-dir": {
-      "version": "4.2.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "find-up": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/possible-typed-array-names": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/postcss": {
-      "version": "8.4.31",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
-      "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/postcss"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "dependencies": {
-        "nanoid": "^3.3.6",
-        "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.2"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      }
-    },
-    "node_modules/postcss-import": {
-      "version": "15.1.0",
-      "license": "MIT",
-      "dependencies": {
-        "postcss-value-parser": "^4.0.0",
-        "read-cache": "^1.0.0",
-        "resolve": "^1.1.7"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      },
-      "peerDependencies": {
-        "postcss": "^8.0.0"
-      }
-    },
-    "node_modules/postcss-js": {
-      "version": "4.0.1",
-      "license": "MIT",
-      "dependencies": {
-        "camelcase-css": "^2.0.1"
-      },
-      "engines": {
-        "node": "^12 || ^14 || >= 16"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      },
-      "peerDependencies": {
-        "postcss": "^8.4.21"
-      }
-    },
-    "node_modules/postcss-load-config": {
-      "version": "4.0.2",
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/postcss/"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "lilconfig": "^3.0.0",
-        "yaml": "^2.3.4"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "postcss": ">=8.0.9",
-        "ts-node": ">=9.0.0"
-      },
-      "peerDependenciesMeta": {
-        "postcss": {
-          "optional": true
-        },
-        "ts-node": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/postcss-load-config/node_modules/lilconfig": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
-      "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antonk52"
-      }
-    },
-    "node_modules/postcss-nested": {
-      "version": "6.0.1",
-      "license": "MIT",
-      "dependencies": {
-        "postcss-selector-parser": "^6.0.11"
-      },
-      "engines": {
-        "node": ">=12.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      },
-      "peerDependencies": {
-        "postcss": "^8.2.14"
-      }
-    },
-    "node_modules/postcss-selector-parser": {
-      "version": "6.0.16",
-      "license": "MIT",
-      "dependencies": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-value-parser": {
-      "version": "4.2.0",
-      "license": "MIT"
-    },
-    "node_modules/prelude-ls": {
-      "version": "1.2.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/prettier": {
-      "version": "3.2.5",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "prettier": "bin/prettier.cjs"
-      },
-      "engines": {
-        "node": ">=14"
-      },
-      "funding": {
-        "url": "https://github.com/prettier/prettier?sponsor=1"
-      }
-    },
-    "node_modules/prettier-linter-helpers": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-diff": "^1.1.2"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/pretty-format": {
-      "version": "29.7.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@jest/schemas": "^29.6.3",
-        "ansi-styles": "^5.0.0",
-        "react-is": "^18.0.0"
-      },
-      "engines": {
-        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
-      }
-    },
-    "node_modules/pretty-format/node_modules/ansi-styles": {
-      "version": "5.2.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/prompts": {
-      "version": "2.4.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "kleur": "^3.0.3",
-        "sisteransi": "^1.0.5"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/prop-types": {
-      "version": "15.8.1",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.4.0",
-        "object-assign": "^4.1.1",
-        "react-is": "^16.13.1"
-      }
-    },
-    "node_modules/prop-types/node_modules/react-is": {
-      "version": "16.13.1",
-      "license": "MIT"
-    },
-    "node_modules/psl": {
-      "version": "1.9.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/punycode": {
-      "version": "2.3.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/pure-rand": {
-      "version": "6.1.0",
-      "dev": true,
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://github.com/sponsors/dubzzz"
-        },
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/fast-check"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/querystringify": {
-      "version": "2.2.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/queue-microtask": {
-      "version": "1.2.3",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/range-parser": {
-      "version": "1.2.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/react": {
-      "version": "18.3.1",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/react-dom": {
-      "version": "18.3.1",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.1.0",
-        "scheduler": "^0.23.2"
-      },
-      "peerDependencies": {
-        "react": "^18.3.1"
-      }
-    },
-    "node_modules/react-is": {
-      "version": "18.3.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/react-paginate": {
-      "version": "8.2.0",
-      "license": "MIT",
-      "dependencies": {
-        "prop-types": "^15"
-      },
-      "peerDependencies": {
-        "react": "^16 || ^17 || ^18"
-      }
-    },
-    "node_modules/read-cache": {
-      "version": "1.0.0",
-      "license": "MIT",
-      "dependencies": {
-        "pify": "^2.3.0"
-      }
-    },
-    "node_modules/readdirp": {
-      "version": "3.6.0",
-      "license": "MIT",
-      "dependencies": {
-        "picomatch": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      }
-    },
-    "node_modules/redent": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "indent-string": "^4.0.0",
-        "strip-indent": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/reflect.getprototypeof": {
-      "version": "1.0.6",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.1",
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4",
-        "globalthis": "^1.0.3",
-        "which-builtin-type": "^1.1.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/regenerator-runtime": {
-      "version": "0.14.1",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/regexp.prototype.flags": {
-      "version": "1.5.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.6",
-        "define-properties": "^1.2.1",
-        "es-errors": "^1.3.0",
-        "set-function-name": "^2.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/regexpp": {
-      "version": "3.2.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      }
-    },
-    "node_modules/require-directory": {
-      "version": "2.1.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/requireindex": {
-      "version": "1.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.5"
-      }
-    },
-    "node_modules/requires-port": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/resolve": {
-      "version": "1.22.8",
-      "license": "MIT",
-      "dependencies": {
-        "is-core-module": "^2.13.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/resolve-cwd": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "resolve-from": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/resolve-from": {
-      "version": "5.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/resolve-pkg-maps": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
-      }
-    },
-    "node_modules/resolve.exports": {
-      "version": "2.0.2",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/reusify": {
-      "version": "1.0.4",
-      "license": "MIT",
-      "engines": {
-        "iojs": ">=1.0.0",
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/rimraf": {
-      "version": "3.0.2",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "glob": "^7.1.3"
-      },
-      "bin": {
-        "rimraf": "bin.js"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/run-parallel": {
-      "version": "1.2.0",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "node_modules/safe-array-concat": {
-      "version": "1.1.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "get-intrinsic": "^1.2.4",
-        "has-symbols": "^1.0.3",
-        "isarray": "^2.0.5"
-      },
-      "engines": {
-        "node": ">=0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/safe-buffer": {
-      "version": "5.2.1",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT"
-    },
-    "node_modules/safe-regex-test": {
-      "version": "1.0.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.6",
-        "es-errors": "^1.3.0",
-        "is-regex": "^1.1.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/safer-buffer": {
-      "version": "2.1.2",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/saxes": {
-      "version": "6.0.0",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "xmlchars": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=v12.22.7"
-      }
-    },
-    "node_modules/scheduler": {
-      "version": "0.23.2",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.1.0"
-      }
-    },
-    "node_modules/semver": {
-      "version": "7.6.3",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
-      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/set-function-length": {
-      "version": "1.2.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/set-function-name": {
-      "version": "2.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "functions-have-names": "^1.2.3",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/sharp": {
-      "version": "0.33.4",
-      "hasInstallScript": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "color": "^4.2.3",
-        "detect-libc": "^2.0.3",
-        "semver": "^7.6.0"
-      },
-      "engines": {
-        "libvips": ">=8.15.2",
-        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/libvips"
-      },
-      "optionalDependencies": {
-        "@img/sharp-darwin-arm64": "0.33.4",
-        "@img/sharp-darwin-x64": "0.33.4",
-        "@img/sharp-libvips-darwin-arm64": "1.0.2",
-        "@img/sharp-libvips-darwin-x64": "1.0.2",
-        "@img/sharp-libvips-linux-arm": "1.0.2",
-        "@img/sharp-libvips-linux-arm64": "1.0.2",
-        "@img/sharp-libvips-linux-s390x": "1.0.2",
-        "@img/sharp-libvips-linux-x64": "1.0.2",
-        "@img/sharp-libvips-linuxmusl-arm64": "1.0.2",
-        "@img/sharp-libvips-linuxmusl-x64": "1.0.2",
-        "@img/sharp-linux-arm": "0.33.4",
-        "@img/sharp-linux-arm64": "0.33.4",
-        "@img/sharp-linux-s390x": "0.33.4",
-        "@img/sharp-linux-x64": "0.33.4",
-        "@img/sharp-linuxmusl-arm64": "0.33.4",
-        "@img/sharp-linuxmusl-x64": "0.33.4",
-        "@img/sharp-wasm32": "0.33.4",
-        "@img/sharp-win32-ia32": "0.33.4",
-        "@img/sharp-win32-x64": "0.33.4"
-      }
-    },
-    "node_modules/shebang-command": {
-      "version": "2.0.0",
-      "license": "MIT",
-      "dependencies": {
-        "shebang-regex": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/shebang-regex": {
-      "version": "3.0.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/side-channel": {
-      "version": "1.0.6",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4",
-        "object-inspect": "^1.13.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/signal-exit": {
-      "version": "3.0.7",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/simple-swizzle": {
-      "version": "0.2.2",
-      "license": "MIT",
-      "dependencies": {
-        "is-arrayish": "^0.3.1"
-      }
-    },
-    "node_modules/sisteransi": {
-      "version": "1.0.5",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/slash": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/source-map": {
-      "version": "0.6.1",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/source-map-js": {
-      "version": "1.2.0",
-      "license": "BSD-3-Clause",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/source-map-support": {
-      "version": "0.5.13",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
-      }
-    },
-    "node_modules/sprintf-js": {
-      "version": "1.0.3",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/stack-utils": {
-      "version": "2.0.6",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "escape-string-regexp": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/stack-utils/node_modules/escape-string-regexp": {
-      "version": "2.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/stop-iteration-iterator": {
-      "version": "1.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "internal-slot": "^1.0.4"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/streamsearch": {
-      "version": "1.1.0",
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/string-length": {
-      "version": "4.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "char-regex": "^1.0.2",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/string-width": {
-      "version": "4.2.3",
-      "license": "MIT",
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/string-width-cjs": {
-      "name": "string-width",
-      "version": "4.2.3",
-      "license": "MIT",
-      "dependencies": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/string-width-cjs/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "license": "MIT"
-    },
-    "node_modules/string-width/node_modules/emoji-regex": {
-      "version": "8.0.0",
-      "license": "MIT"
-    },
-    "node_modules/string.prototype.matchall": {
-      "version": "4.0.11",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.0.0",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-symbols": "^1.0.3",
-        "internal-slot": "^1.0.7",
-        "regexp.prototype.flags": "^1.5.2",
-        "set-function-name": "^2.0.2",
-        "side-channel": "^1.0.6"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/string.prototype.trim": {
-      "version": "1.2.9",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.0",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/string.prototype.trimend": {
-      "version": "1.0.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/string.prototype.trimstart": {
-      "version": "1.0.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-ansi-cjs": {
-      "name": "strip-ansi",
-      "version": "6.0.1",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-bom": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/strip-final-newline": {
-      "version": "2.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/strip-indent": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "min-indent": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/styled-jsx": {
-      "version": "5.1.1",
-      "license": "MIT",
-      "dependencies": {
-        "client-only": "0.0.1"
-      },
-      "engines": {
-        "node": ">= 12.0.0"
-      },
-      "peerDependencies": {
-        "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
-      },
-      "peerDependenciesMeta": {
-        "@babel/core": {
-          "optional": true
-        },
-        "babel-plugin-macros": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/sucrase": {
-      "version": "3.35.0",
-      "license": "MIT",
-      "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "commander": "^4.0.0",
-        "glob": "^10.3.10",
-        "lines-and-columns": "^1.1.6",
-        "mz": "^2.7.0",
-        "pirates": "^4.0.1",
-        "ts-interface-checker": "^0.1.9"
-      },
-      "bin": {
-        "sucrase": "bin/sucrase",
-        "sucrase-node": "bin/sucrase-node"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      }
-    },
-    "node_modules/sucrase/node_modules/glob": {
-      "version": "10.4.5",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
-      "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
-      "dependencies": {
-        "foreground-child": "^3.1.0",
-        "jackspeak": "^3.1.2",
-        "minimatch": "^9.0.4",
-        "minipass": "^7.1.2",
-        "package-json-from-dist": "^1.0.0",
-        "path-scurry": "^1.11.1"
-      },
-      "bin": {
-        "glob": "dist/esm/bin.mjs"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/sucrase/node_modules/jackspeak": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
-      "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
-      "dependencies": {
-        "@isaacs/cliui": "^8.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      },
-      "optionalDependencies": {
-        "@pkgjs/parseargs": "^0.11.0"
-      }
-    },
-    "node_modules/sucrase/node_modules/minimatch": {
-      "version": "9.0.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
-      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/supports-color": {
-      "version": "7.2.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/symbol-tree": {
-      "version": "3.2.4",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/synckit": {
-      "version": "0.8.8",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@pkgr/core": "^0.1.0",
-        "tslib": "^2.6.2"
-      },
-      "engines": {
-        "node": "^14.18.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/unts"
-      }
-    },
-    "node_modules/tailwindcss": {
-      "version": "3.4.3",
-      "license": "MIT",
-      "dependencies": {
-        "@alloc/quick-lru": "^5.2.0",
-        "arg": "^5.0.2",
-        "chokidar": "^3.5.3",
-        "didyoumean": "^1.2.2",
-        "dlv": "^1.1.3",
-        "fast-glob": "^3.3.0",
-        "glob-parent": "^6.0.2",
-        "is-glob": "^4.0.3",
-        "jiti": "^1.21.0",
-        "lilconfig": "^2.1.0",
-        "micromatch": "^4.0.5",
-        "normalize-path": "^3.0.0",
-        "object-hash": "^3.0.0",
-        "picocolors": "^1.0.0",
-        "postcss": "^8.4.23",
-        "postcss-import": "^15.1.0",
-        "postcss-js": "^4.0.1",
-        "postcss-load-config": "^4.0.1",
-        "postcss-nested": "^6.0.1",
-        "postcss-selector-parser": "^6.0.11",
-        "resolve": "^1.22.2",
-        "sucrase": "^3.32.0"
-      },
-      "bin": {
-        "tailwind": "lib/cli.js",
-        "tailwindcss": "lib/cli.js"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      }
-    },
-    "node_modules/tapable": {
-      "version": "2.2.1",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/test-exclude": {
-      "version": "6.0.0",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "@istanbuljs/schema": "^0.1.2",
-        "glob": "^7.1.4",
-        "minimatch": "^3.0.4"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/test-listen": {
-      "version": "1.1.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/text-table": {
-      "version": "0.2.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/thenify": {
-      "version": "3.3.1",
-      "license": "MIT",
-      "dependencies": {
-        "any-promise": "^1.0.0"
-      }
-    },
-    "node_modules/thenify-all": {
-      "version": "1.6.0",
-      "license": "MIT",
-      "dependencies": {
-        "thenify": ">= 3.1.0 < 4"
-      },
-      "engines": {
-        "node": ">=0.8"
-      }
-    },
-    "node_modules/tmpl": {
-      "version": "1.0.5",
-      "dev": true,
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/to-fast-properties": {
-      "version": "2.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/to-regex-range": {
-      "version": "5.0.1",
-      "license": "MIT",
-      "dependencies": {
-        "is-number": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=8.0"
-      }
-    },
-    "node_modules/tough-cookie": {
-      "version": "4.1.4",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "psl": "^1.1.33",
-        "punycode": "^2.1.1",
-        "universalify": "^0.2.0",
-        "url-parse": "^1.5.3"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/tr46": {
-      "version": "0.0.3",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/ts-api-utils": {
-      "version": "1.3.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=16"
-      },
-      "peerDependencies": {
-        "typescript": ">=4.2.0"
-      }
-    },
-    "node_modules/ts-interface-checker": {
-      "version": "0.1.13",
-      "license": "Apache-2.0"
-    },
-    "node_modules/ts-node": {
-      "version": "10.9.2",
-      "devOptional": true,
-      "license": "MIT",
-      "dependencies": {
-        "@cspotcode/source-map-support": "^0.8.0",
-        "@tsconfig/node10": "^1.0.7",
-        "@tsconfig/node12": "^1.0.7",
-        "@tsconfig/node14": "^1.0.0",
-        "@tsconfig/node16": "^1.0.2",
-        "acorn": "^8.4.1",
-        "acorn-walk": "^8.1.1",
-        "arg": "^4.1.0",
-        "create-require": "^1.1.0",
-        "diff": "^4.0.1",
-        "make-error": "^1.1.1",
-        "v8-compile-cache-lib": "^3.0.1",
-        "yn": "3.1.1"
-      },
-      "bin": {
-        "ts-node": "dist/bin.js",
-        "ts-node-cwd": "dist/bin-cwd.js",
-        "ts-node-esm": "dist/bin-esm.js",
-        "ts-node-script": "dist/bin-script.js",
-        "ts-node-transpile-only": "dist/bin-transpile.js",
-        "ts-script": "dist/bin-script-deprecated.js"
-      },
-      "peerDependencies": {
-        "@swc/core": ">=1.2.50",
-        "@swc/wasm": ">=1.2.50",
-        "@types/node": "*",
-        "typescript": ">=2.7"
-      },
-      "peerDependenciesMeta": {
-        "@swc/core": {
-          "optional": true
-        },
-        "@swc/wasm": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/ts-node/node_modules/arg": {
-      "version": "4.1.3",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/tsconfig-paths": {
-      "version": "3.15.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json5": "^0.0.29",
-        "json5": "^1.0.2",
-        "minimist": "^1.2.6",
-        "strip-bom": "^3.0.0"
-      }
-    },
-    "node_modules/tslib": {
-      "version": "2.6.2",
-      "license": "0BSD"
-    },
-    "node_modules/type-check": {
-      "version": "0.4.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "prelude-ls": "^1.2.1"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/type-detect": {
-      "version": "4.0.8",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/type-fest": {
-      "version": "0.20.2",
-      "dev": true,
-      "license": "(MIT OR CC0-1.0)",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/type-is": {
-      "version": "1.6.18",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "media-typer": "0.3.0",
-        "mime-types": "~2.1.24"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/typed-array-buffer": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "es-errors": "^1.3.0",
-        "is-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/typed-array-byte-length": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "has-proto": "^1.0.3",
-        "is-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/typed-array-byte-offset": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "available-typed-arrays": "^1.0.7",
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "has-proto": "^1.0.3",
-        "is-typed-array": "^1.1.13"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/typed-array-length": {
-      "version": "1.0.6",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "has-proto": "^1.0.3",
-        "is-typed-array": "^1.1.13",
-        "possible-typed-array-names": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/typescript": {
-      "version": "5.4.5",
-      "devOptional": true,
-      "license": "Apache-2.0",
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
-      "engines": {
-        "node": ">=14.17"
-      }
-    },
-    "node_modules/unbox-primitive": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-bigints": "^1.0.2",
-        "has-symbols": "^1.0.3",
-        "which-boxed-primitive": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/undici-types": {
-      "version": "6.19.8",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
-      "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
-      "devOptional": true
-    },
-    "node_modules/universalify": {
-      "version": "0.2.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4.0.0"
-      }
-    },
-    "node_modules/update-browserslist-db": {
-      "version": "1.0.16",
-      "dev": true,
-      "funding": [
-        {
-          "type": "opencollective",
-          "url": "https://opencollective.com/browserslist"
-        },
-        {
-          "type": "tidelift",
-          "url": "https://tidelift.com/funding/github/npm/browserslist"
-        },
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/ai"
-        }
-      ],
-      "license": "MIT",
-      "dependencies": {
-        "escalade": "^3.1.2",
-        "picocolors": "^1.0.1"
-      },
-      "bin": {
-        "update-browserslist-db": "cli.js"
-      },
-      "peerDependencies": {
-        "browserslist": ">= 4.21.0"
-      }
-    },
-    "node_modules/uri-js": {
-      "version": "4.4.1",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "punycode": "^2.1.0"
-      }
-    },
-    "node_modules/url-parse": {
-      "version": "1.5.10",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "querystringify": "^2.1.1",
-        "requires-port": "^1.0.0"
-      }
-    },
-    "node_modules/use-intl": {
-      "version": "3.19.4",
-      "license": "MIT",
-      "dependencies": {
-        "@formatjs/fast-memoize": "^2.2.0",
-        "intl-messageformat": "^10.5.14"
-      },
-      "peerDependencies": {
-        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
-      }
-    },
-    "node_modules/util-deprecate": {
-      "version": "1.0.2",
-      "license": "MIT"
-    },
-    "node_modules/v8-compile-cache-lib": {
-      "version": "3.0.1",
-      "devOptional": true,
-      "license": "MIT"
-    },
-    "node_modules/v8-to-istanbul": {
-      "version": "9.2.0",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "@jridgewell/trace-mapping": "^0.3.12",
-        "@types/istanbul-lib-coverage": "^2.0.1",
-        "convert-source-map": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10.12.0"
-      }
-    },
-    "node_modules/w3c-xmlserializer": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "xml-name-validator": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=14"
-      }
-    },
-    "node_modules/walker": {
-      "version": "1.0.8",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "makeerror": "1.0.12"
-      }
-    },
-    "node_modules/webidl-conversions": {
-      "version": "7.0.0",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/whatwg-encoding": {
-      "version": "2.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "iconv-lite": "0.6.3"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/whatwg-fetch": {
-      "version": "3.6.20",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/whatwg-mimetype": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/whatwg-url": {
-      "version": "11.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "tr46": "^3.0.0",
-        "webidl-conversions": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/whatwg-url/node_modules/tr46": {
-      "version": "3.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "punycode": "^2.1.1"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/which": {
-      "version": "2.0.2",
-      "license": "ISC",
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "node-which": "bin/node-which"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/which-boxed-primitive": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-bigint": "^1.0.1",
-        "is-boolean-object": "^1.1.0",
-        "is-number-object": "^1.0.4",
-        "is-string": "^1.0.5",
-        "is-symbol": "^1.0.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/which-builtin-type": {
-      "version": "1.1.3",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "function.prototype.name": "^1.1.5",
-        "has-tostringtag": "^1.0.0",
-        "is-async-function": "^2.0.0",
-        "is-date-object": "^1.0.5",
-        "is-finalizationregistry": "^1.0.2",
-        "is-generator-function": "^1.0.10",
-        "is-regex": "^1.1.4",
-        "is-weakref": "^1.0.2",
-        "isarray": "^2.0.5",
-        "which-boxed-primitive": "^1.0.2",
-        "which-collection": "^1.0.1",
-        "which-typed-array": "^1.1.9"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/which-collection": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "is-map": "^2.0.3",
-        "is-set": "^2.0.3",
-        "is-weakmap": "^2.0.2",
-        "is-weakset": "^2.0.3"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/which-typed-array": {
-      "version": "1.1.15",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "available-typed-arrays": "^1.0.7",
-        "call-bind": "^1.0.7",
-        "for-each": "^0.3.3",
-        "gopd": "^1.0.1",
-        "has-tostringtag": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/word-wrap": {
-      "version": "1.2.5",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/wrap-ansi": {
-      "version": "7.0.0",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/wrap-ansi-cjs": {
-      "name": "wrap-ansi",
-      "version": "7.0.0",
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/wrappy": {
-      "version": "1.0.2",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/write-file-atomic": {
-      "version": "4.0.2",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "imurmurhash": "^0.1.4",
-        "signal-exit": "^3.0.7"
-      },
-      "engines": {
-        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
-      }
-    },
-    "node_modules/ws": {
-      "version": "8.17.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10.0.0"
-      },
-      "peerDependencies": {
-        "bufferutil": "^4.0.1",
-        "utf-8-validate": ">=5.0.2"
-      },
-      "peerDependenciesMeta": {
-        "bufferutil": {
-          "optional": true
-        },
-        "utf-8-validate": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/xml-name-validator": {
-      "version": "4.0.0",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/xmlchars": {
-      "version": "2.2.0",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/y18n": {
-      "version": "5.0.8",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/yallist": {
-      "version": "3.1.1",
-      "dev": true,
-      "license": "ISC"
-    },
-    "node_modules/yaml": {
-      "version": "2.4.2",
-      "license": "ISC",
-      "bin": {
-        "yaml": "bin.mjs"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/yargs": {
-      "version": "17.7.2",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cliui": "^8.0.1",
-        "escalade": "^3.1.1",
-        "get-caller-file": "^2.0.5",
-        "require-directory": "^2.1.1",
-        "string-width": "^4.2.3",
-        "y18n": "^5.0.5",
-        "yargs-parser": "^21.1.1"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/yargs-parser": {
-      "version": "21.1.1",
-      "dev": true,
-      "license": "ISC",
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/yn": {
-      "version": "3.1.1",
-      "devOptional": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/yocto-queue": {
-      "version": "0.1.0",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/zod": {
-      "version": "3.23.8",
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/colinhacks"
-      }
-    }
-  }
-}
diff --git a/package.json b/package.json
index 37f7a7a..8e202ee 100644
--- a/package.json
+++ b/package.json
@@ -26,16 +26,15 @@
     "deploy": "yarn install && yarn add sharp && yarn build && yarn start"
   },
   "dependencies": {
-    "@headlessui/react": "^1.7.18",
-    "@headlessui/tailwindcss": "^0.1.3",
-    "jotai": "^2.10.3",
-    "next": "14.1.0",
-    "next-intl": "^3.19.1",
+    "@headlessui/react": "^2",
+    "@headlessui/tailwindcss": "^0.2.2",
+    "next": "^15",
+    "next-intl": "^3",
     "react": "^18",
     "react-dom": "^18",
-    "react-paginate": "^8.2.0",
-    "sharp": "^0.33.4",
-    "zod": "^3.22.4"
+    "react-paginate": "^8",
+    "sharp": "^0.33",
+    "zod": "^3"
   },
   "devDependencies": {
     "@testing-library/jest-dom": "^6.4.2",
@@ -48,11 +47,11 @@
     "@types/test-listen": "^1.1.2",
     "autoprefixer": "^10.0.1",
     "eslint": "^8",
-    "eslint-config-next": "14.1.0",
-    "eslint-config-universe": "^12.0.0",
-    "eslint-plugin-i18next": "^6.0.3",
-    "eslint-plugin-unused-imports": "^3.1.0",
-    "husky": "^9.0.11",
+    "eslint-config-next": "^15",
+    "eslint-config-universe": "^14",
+    "eslint-plugin-i18next": "^6",
+    "eslint-plugin-unused-imports": "^4",
+    "husky": "^9",
     "isomorphic-fetch": "^3.0.0",
     "jest": "^29.7.0",
     "jest-environment-jsdom": "^29.7.0",
diff --git a/src/app/[locale]/globals.css b/src/app/[locale]/globals.css
index a22dd0d..0d132dc 100644
--- a/src/app/[locale]/globals.css
+++ b/src/app/[locale]/globals.css
@@ -9,4 +9,7 @@
   #smallScreenSearch {
     @apply m-10 p-10 rounded-lg drop-shadow bg-left-bottom border-2 lg:hidden;
   }
+  #smallScreenDetailsSearch {
+    @apply mb-10 p-10 lg:hidden;
+  }
 }
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index 3a73ca3..b4080db 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -37,11 +37,12 @@ export const viewport: Viewport = {
 
 export default function RootLayout({
   children,
-  params: { locale },
+  params,
 }: Readonly<{
   children: React.ReactNode;
-  params: { locale: string };
+  params: Promise<{ locale: string }>;
 }>) {
+  const { locale } = React.use(params);
   const t = useTranslations("Layout");
   const messages = useMessages();
 
@@ -50,20 +51,18 @@ export default function RootLayout({
       <body>
         <noscript>{t("js_needed")}</noscript>
         <main className="flex flex-col">
-          <header className="sticky top-0 w-full border-b-2 border-b-secondary-helmholtz-mint shadow-lg bg-primary-helmholtz-weiss py-5 ">
-            {showBanner && <Banner />}
-            <Header />
-          </header>
-
           <NextIntlClientProvider locale={locale} messages={messages}>
+            <header className="sticky top-0 w-full border-b-2 border-b-secondary-helmholtz-mint shadow-lg bg-primary-helmholtz-weiss py-5 ">
+              {showBanner && <Banner />}
+              <Header />
+            </header>
             <CategoryProvider>
               <div className="grow flex flex-col">{children}</div>
             </CategoryProvider>
+            <div className="sticky bottom-0">
+              <Footer />
+            </div>
           </NextIntlClientProvider>
-
-          <div className="sticky bottom-0">
-            <Footer />
-          </div>
         </main>
       </body>
     </html>
diff --git a/src/app/[locale]/results/details/page.tsx b/src/app/[locale]/results/details/page.tsx
index 7411f9e..c481f9b 100644
--- a/src/app/[locale]/results/details/page.tsx
+++ b/src/app/[locale]/results/details/page.tsx
@@ -4,10 +4,15 @@ import BackBar from "@/components/app/results/BackBar";
 import DetailsItemCard from "@/components/app/results/Details/DetailsItemCard";
 import RelatedResults from "@/components/app/results/Details/RelatedResults";
 
+import SearchForm from "@/components/layout/SearchForm";
+
 const DetailsComponent = () => {
   return (
     <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
       <BackBar />
+      <div id="smallScreenDetailsSearch">
+        <SearchForm />
+      </div>
       <DetailsItemCard />
       <RelatedResults />
     </div>
diff --git a/src/components/ExternalLink.tsx b/src/components/ExternalLink.tsx
index cfe6636..19f5881 100644
--- a/src/components/ExternalLink.tsx
+++ b/src/components/ExternalLink.tsx
@@ -1,5 +1,7 @@
 import { PropsWithChildren } from "react";
 
+import { Link } from "@/i18n/routing";
+
 type Props = {
   href: string;
   underlined?: boolean;
@@ -8,14 +10,14 @@ type Props = {
 
 const ExternalLink = ({ href, underlined, className, children }: PropsWithChildren<Props>) => {
   return (
-    <a
+    <Link
       href={href}
       target="_blank"
       rel="noreferrer"
       className={`hover:text-primary-helmholtz-hellblau ${className || ""} ${underlined ? "underline" : ""}`}
     >
       {children}
-    </a>
+    </Link>
   );
 };
 
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index 229b792..566b179 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -7,7 +7,8 @@ import { useState, useContext, useEffect } from "react";
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
 import { CategoryContext } from "@/contexts/CategoryContext";
-import { Link } from "@/i18n";
+
+import { Link } from "@/i18n/routing";
 
 const Categories = () => {
   const t = useTranslations("Categories");
diff --git a/src/components/app/Intro.tsx b/src/components/app/Intro.tsx
index abd0345..847b72d 100644
--- a/src/components/app/Intro.tsx
+++ b/src/components/app/Intro.tsx
@@ -59,8 +59,8 @@ const Intro = () => {
                     <ExternalLink
                       underlined
                       href={
-                        process.env.NEXT_PRIVATE_DOC_URL ||
-                        "https://docs.unhide.helmholtz-metadaten.de/intro.html"
+                        process.env.NEXT_PUBLIC_DOC_URL ||
+                        "https://docs.unhide.helmholtz-metadaten.de"
                       }
                     >
                       {content}
@@ -77,8 +77,8 @@ const Intro = () => {
                     <ExternalLink
                       underlined
                       href={
-                        process.env.NEXT_PRIVATE_SPARQL_URL ||
-                        "https://sparql.unhide.helmholtz-metadaten.de/intro.html"
+                        process.env.NEXT_PUBLIC_SPARQL_URL ||
+                        "https://sparql.unhide.helmholtz-metadaten.de"
                       }
                     >
                       {content}
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index bd365dc..d464cb1 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -5,9 +5,10 @@ import { useTranslations } from "next-intl";
 import { Suspense, useContext } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
-import { Link } from "@/i18n";
 import type { Category } from "@/types/types";
 
+import { Link } from "@/i18n/routing";
+
 type Props = {
   activeCategory?: Category;
 };
diff --git a/src/components/app/results/ListResults/ResultItem.tsx b/src/components/app/results/ListResults/ResultItem.tsx
index e40ba8c..57be19d 100644
--- a/src/components/app/results/ListResults/ResultItem.tsx
+++ b/src/components/app/results/ListResults/ResultItem.tsx
@@ -10,12 +10,13 @@ import { INSTITUTE_ROR_LOGOS } from "@/app/api/config/config";
 import ExternalLink from "@/components/ExternalLink";
 import Relationships from "@/components/app/results/ListResults/ResultItem/Relationships";
 import { CategoryContext } from "@/contexts/CategoryContext";
-import { Link } from "@/i18n";
 import ExternalLinkIcon from "@/resources/images/svg/ExternalLinkIcon";
 import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
 import type { Category, ResultItem as ResultItemType } from "@/types/types";
 import { detailsItemAtom } from "@/utils/atoms";
 
+import { Link } from "@/i18n/routing";
+
 type Props = {
   item: ResultItemType;
   idx: string;
diff --git a/src/components/app/results/ListResults/ResultItem/Relationships.tsx b/src/components/app/results/ListResults/ResultItem/Relationships.tsx
index 655ff2c..27d7ad1 100644
--- a/src/components/app/results/ListResults/ResultItem/Relationships.tsx
+++ b/src/components/app/results/ListResults/ResultItem/Relationships.tsx
@@ -3,9 +3,10 @@ import { useTranslations } from "next-intl";
 import React, { useEffect, useRef, useState } from "react";
 
 import { INSTITUTE_ROR_LOGOS } from "@/app/api/config/config";
-import { Link } from "@/i18n";
 import ExternalLinkIcon from "@/resources/images/svg/ExternalLinkIcon";
 
+import { Link } from "@/i18n/routing";
+
 type Props = {
   filteredArr: { label: string; type: string; id: string }[];
   type: "Parent" | "Child" | "Related";
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index aec4e0f..5f43cd4 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -4,45 +4,48 @@ import { useTranslations } from "next-intl";
 
 import ExternalLink from "@/components/ExternalLink";
 import HMCLogoInv from "@/resources/images/logo/HMC_Logo_invert_S.png";
+import BlueskyLogo from "@/resources/images/svg/BlueskyLogo";
 import GitlabLogo from "@/resources/images/svg/GitlabLogo";
 import HelmholtzSvg from "@/resources/images/svg/HelmholtzSvg";
 import LinkedInLogo from "@/resources/images/svg/LinkedInLogo";
 import MailEnvelope from "@/resources/images/svg/MailEnvelope";
 import MastodonLogo from "@/resources/images/svg/MastodonLogo";
 import MattermostLogo from "@/resources/images/svg/MattermostLogo";
-import TwitterLogo from "@/resources/images/svg/TwitterLogo";
-import YouTubeLogo from "@/resources/images/svg/YouTubeLogo";
 
 export default function Footer() {
   const t = useTranslations("Footer");
   return (
     <>
-      {/* footer goes here */}
       <div className="text-primary-helmholtz-weiss w-full flex-wrap divide-y divide-secondary-helmholtz-webblassblau bg-primary-helmholtz-dunkelblau px-20 py-5 drop-shadow">
-        <div className="mx-auto my-3 grid grid-cols-1 gap-y-20 md:grid-cols-2  justify-between ">
+        {/* branding row */}
+        <div className="my-3 grid grid-cols-1 gap-y-10">
+          {/* logo row */}
+          <div id="logo-row" className="flex flex-wrap gap-y-10 md:justify-between">
+            <ExternalLink href="https://www.helmholtz.de/en/">
+              <HelmholtzSvg />
+              <div id="logo-tagline" className="-mt-5">
+                {t("helmholtz_phrase")}
+              </div>
+            </ExternalLink>
+
+            <ExternalLink href="https://helmholtz-metadaten.de/en">
+              <Image className="max-w-60 md:max-w-72" src={HMCLogoInv} alt="hmc logo" />
+            </ExternalLink>
+          </div>
+          {/* social links row*/}
           <div
-            id="helmholtz-content"
-            className="flex flex-col gap-4 md:gap-6 col-span-1 justify-between "
+            id="links-row"
+            className="flex flex-col gap-6 md:flex-row md:gap-4 md:justify-between"
           >
-            <div id="logo-row">
-              <ExternalLink href="https://www.helmholtz.de/en/">
-                <HelmholtzSvg />
-                <div id="logo-tagline" className="-mt-5">
-                  {t("helmholtz_phrase")}
-                </div>
-              </ExternalLink>
-            </div>
-            <div id="social-row" className="grid grid-cols-2 gap-4 md:flex md:space-x-5 ">
-              <ExternalLink href="https://twitter.com/helmholtz_de/">
-                <TwitterLogo />
-
-                <span>{t("twitter")}</span>
-              </ExternalLink>
-              <ExternalLink href="https://www.youtube.com/user/helmholtzTV/">
-                <YouTubeLogo />
-                <span>{t("youtube")}</span>
+            <div
+              id="social-links"
+              className="grid grid-cols-3 md:max-w-64 md:gap-6 md:grid-cols-3 lg:flex"
+            >
+              <ExternalLink href="https://bsky.app/profile/helmholtzhmc.bsky.social">
+                <BlueskyLogo />
+                <span>{t("bluesky")}</span>
               </ExternalLink>
-              <ExternalLink href="https://www.linkedin.com/company/helmholtz-association/">
+              <ExternalLink href="https://www.linkedin.com/company/helmholtz-metadata-collaboration-hmc">
                 <LinkedInLogo />
                 <span>{t("linkedin")}</span>
               </ExternalLink>
@@ -51,19 +54,9 @@ export default function Footer() {
                 <span>{t("mastodon")}</span>
               </ExternalLink>
             </div>
-          </div>
-
-          {/* hmc content */}
-          <div className="flex flex-col gap-4 md:gap-6 col-span-1 justify-between content-end">
-            <div className="flex md:justify-end">
-              <ExternalLink href="https://helmholtz-metadaten.de/en">
-                <Image className="max-w-60  md:max-w-72" src={HMCLogoInv} alt="hmc logo" />
-              </ExternalLink>
-            </div>
-
             <div
-              id="social-row"
-              className="grid grid-cols-2 gap-4 md:flex md:content-end md:justify-end md:space-x-5"
+              id="contact-links"
+              className="grid grid-cols-3 md:max-w-64 md:grid-cols-3 lg:gap-6 lg:flex"
             >
               <ExternalLink href="mailto:HMC@fz-juelich.de">
                 <MailEnvelope />
@@ -96,7 +89,7 @@ export default function Footer() {
 
           {/* copyright info */}
           <div className="pt-4 md:pt-0 md:basis-1/3 flex justify-end cursor-default text-sm">
-            {t("copyright")}
+            {t("copyright", { year: new Date().getFullYear() })}
           </div>
         </div>
       </div>
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index db5d3a2..4a33106 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -2,10 +2,11 @@ import Image from "next/image";
 import { useTranslations } from "next-intl";
 
 import Search from "@/components/layout/Search";
-import { Link } from "@/i18n";
 import logoHGF from "@/resources/images/logo/Logo_HGF-KG_text_brightback.png";
 import unhideLogo from "@/resources/images/logo/unhide_header.png";
 
+import { Link } from "@/i18n/routing";
+
 const Header = () => {
   const t = useTranslations("Search");
   const s = useTranslations("Intro");
diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index 9ebb5e1..eb313e2 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -1,24 +1,21 @@
 "use client";
 
-import { useSearchParams, useRouter } from "next/navigation";
+import { useSearchParams } from "next/navigation";
 import { Suspense, useEffect, useState, useContext } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
-import { Link } from "@/i18n";
-import ClearIcon from "@/resources/images/svg/ClearIcon";
 import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
-import SearchIcon from "@/resources/images/svg/SearchIcon";
+
+import SearchForm from "@/components/layout/SearchForm";
+import { Link } from "@/i18n/routing";
 
 type Props = {
   exampleTrigger: string;
-  placeholder: string;
 };
 
-const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
+const SearchComponent = ({ exampleTrigger }: Props) => {
   const resultParams = useSearchParams();
-  const router = useRouter();
 
-  const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
   const paramsCategory = resultParams.get("category") ?? "datasets";
   const detailsText = resultParams.get("detailsText") ?? undefined;
 
@@ -27,10 +24,6 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
 
   const { visibleCategories, setPrimaryActiveCategory } = useContext(CategoryContext);
 
-  useEffect(() => {
-    setSearchQuery(resultParams.get("searchText") ?? "");
-  }, [resultParams]);
-
   useEffect(() => {
     setExamples(
       [
@@ -53,54 +46,9 @@ const SearchComponent = ({ exampleTrigger, placeholder }: Props) => {
     );
   }, []);
 
-  const handleClear = () => {
-    setSearchQuery("");
-    setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
-  };
-
-  const submitSearch = (event: any) => {
-    event.preventDefault();
-    setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
-    router.push(
-      `/results?category=${paramsCategory}` + (searchQuery ? `&searchText=${searchQuery}` : "")
-    );
-  };
-
   return (
-    <div className="">
-      <form
-        onSubmit={(e) => submitSearch(e)}
-        className="flex items-center py-1 border-b-2 border-b-primary-helmholtz-dunkelblau"
-      >
-        <div className="basis-1/3 md:basis-11/12 py-2">
-          <input
-            className="pl-2 text-primary-helmholtz-dunkelblau placeholder:italic focus:outline-none bg-transparent"
-            placeholder={placeholder}
-            value={searchQuery}
-            onChange={(e) => setSearchQuery(e.target.value)}
-          />
-        </div>
-
-        {/* search option buttons */}
-        {/* TODO: refactor this into a single reusable component? */}
-        <div className="flex md:basis-1/12 justify-end divide-x-2">
-          {searchQuery !== "" ? (
-            <Link
-              className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
-              onClick={handleClear}
-              href={`/results?category=${paramsCategory}`}
-            >
-              <ClearIcon />
-            </Link>
-          ) : null}
-
-          <div className="pl-2">
-            <button className="float-right rounded-full bg-secondary-helmholtz-mint px-3 py-3 text-primary-helmholtz-dunkelblau shadow transition duration-300 hover:bg-primary-helmholtz-hellblau">
-              <SearchIcon />
-            </button>
-          </div>
-        </div>
-      </form>
+    <div>
+      <SearchForm />
 
       <div className="flex md:basis-11/12 space-x-2 pl-2 pt-3 items-center truncate">
         <div className="font-bold text-primary-helmholtz-dunkelblau pr-2">{exampleTrigger}</div>
diff --git a/src/components/layout/SearchForm.tsx b/src/components/layout/SearchForm.tsx
new file mode 100644
index 0000000..03ff200
--- /dev/null
+++ b/src/components/layout/SearchForm.tsx
@@ -0,0 +1,84 @@
+"use client";
+
+import { useSearchParams, useRouter } from "next/navigation";
+import { useTranslations } from "next-intl";
+import { Suspense, useEffect, useState, useContext } from "react";
+
+import { CategoryContext } from "@/contexts/CategoryContext";
+import ClearIcon from "@/resources/images/svg/ClearIcon";
+import SearchIcon from "@/resources/images/svg/SearchIcon";
+
+import { Link } from "@/i18n/routing";
+
+const SearchFormComponent = () => {
+  const resultParams = useSearchParams();
+  const router = useRouter();
+  const t = useTranslations("Search");
+
+  const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
+  const paramsCategory = resultParams.get("category") ?? "datasets";
+
+  const { visibleCategories, setPrimaryActiveCategory } = useContext(CategoryContext);
+
+  useEffect(() => {
+    setSearchQuery(resultParams.get("searchText") ?? "");
+  }, [resultParams]);
+
+  const handleClear = () => {
+    setSearchQuery("");
+    setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
+  };
+
+  const submitSearch = (event: any) => {
+    event.preventDefault();
+    setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
+    router.push(
+      `/results?category=${paramsCategory}` + (searchQuery ? `&searchText=${searchQuery}` : "")
+    );
+  };
+  return (
+    <div>
+      <form
+        onSubmit={(e) => submitSearch(e)}
+        className="flex items-center py-1 border-b-2 border-b-primary-helmholtz-dunkelblau justify-between"
+      >
+        <div className="basis-9/12 md:basis-10/12">
+          <input
+            className="pl-2 text-primary-helmholtz-dunkelblau placeholder:italic focus:outline-none bg-transparent"
+            placeholder={t("placeholder")}
+            value={searchQuery}
+            onChange={(e) => setSearchQuery(e.target.value)}
+          />
+        </div>
+
+        {/* search option buttons */}
+        {/* TODO: refactor this into a single reusable component? */}
+        <div className="flex basis-2/12 md:basis-2/12 justify-end divide-x-2">
+          {searchQuery !== "" ? (
+            <Link
+              className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
+              onClick={handleClear}
+              href={`/results?category=${paramsCategory}`}
+            >
+              <ClearIcon />
+            </Link>
+          ) : null}
+
+          <div className="pl-2">
+            <button className="rounded-full bg-secondary-helmholtz-mint px-3 py-3 text-primary-helmholtz-dunkelblau shadow transition duration-300 hover:bg-primary-helmholtz-hellblau">
+              <SearchIcon />
+            </button>
+          </div>
+        </div>
+      </form>
+    </div>
+  );
+};
+
+export default function SearchForm() {
+  return (
+    <Suspense>
+      <SearchFormComponent />
+    </Suspense>
+  );
+}
diff --git a/src/i18n.ts b/src/i18n.ts
deleted file mode 100644
index 4e2b6c8..0000000
--- a/src/i18n.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// need to wait for @types/next-intl
-// @ts-ignore
-import { LocalePrefix } from "next-intl/dist/types/src/shared/types";
-import { createSharedPathnamesNavigation } from "next-intl/navigation";
-import { getRequestConfig } from "next-intl/server";
-
-export const i18nConfig = {
-  locales: ["en", "de"],
-  defaultLocale: "en",
-  localeDetection: false,
-  localePrefix: "as-needed" as LocalePrefix,
-};
-
-export default getRequestConfig(async ({ locale }) => {
-  return {
-    messages: (await import(`@/locales/${locale}.json`)).default,
-  };
-});
-
-export const { Link, redirect, usePathname, useRouter } =
-  createSharedPathnamesNavigation(i18nConfig);
diff --git a/src/locales/de.json b/src/locales/de.json
index 8517cc4..a2c1542 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -83,17 +83,19 @@
   },
   "Footer": {
     "helmholtz_phrase": "Spitzenforschung für große Herausforderungen.",
-    "twitter": "Twitter",
+    "twitter": "X",
     "youtube": "YouTube",
     "linkedin": "LinkedIn",
     "mastodon": "Mastodon",
     "email": "E-Mail",
     "gitlab": "GitLab",
     "mattermost": "Mattermost",
+    "bluesky": "BlueSky",
+    "instagram": "Instagram",
     "privacy_protection": "Datenschutz",
     "legal_information": "Rechtliche Informationen",
     "accessibility": "Barrierefreiheit",
-    "copyright": "© 2023 Helmholtz Metadata Collaboration"
+    "copyright": "© {year} Helmholtz Metadata Collaboration"
   },
   "PrivacyPolicy": {
     "main_heading": "Datenschutzhinweise gemäß Art. 13, 14 und 21 der Allgemeinen Datenschutzverordnung GDPR",
diff --git a/src/locales/en.json b/src/locales/en.json
index 6235bc8..5551e80 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -83,17 +83,19 @@
   },
   "Footer": {
     "helmholtz_phrase": "Research for grand challenges.",
-    "twitter": "Twitter",
+    "twitter": "X",
     "youtube": "YouTube",
     "linkedin": "LinkedIn",
     "mastodon": "Mastodon",
     "email": "Email",
     "gitlab": "GitLab",
     "mattermost": "Mattermost",
+    "bluesky": "BlueSky",
+    "instagram": "Instagram",
     "privacy_protection": "Privacy protection",
     "legal_information": "Legal information",
     "accessibility": "Accessibility",
-    "copyright": "© 2023 Helmholtz Metadata Collaboration"
+    "copyright": "© {year} Helmholtz Metadata Collaboration"
   },
   "PrivacyPolicy": {
     "main_heading": "Data protection information in accordance with Art. 13, 14 and 21 of the General Data Protection Regulation GDPR",
diff --git a/src/middleware.ts b/src/middleware.ts
index 359c512..58c5c78 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -1,8 +1,8 @@
 import createMiddleware from "next-intl/middleware";
 
-import { i18nConfig } from "@/i18n";
+import { routing } from "@/i18n/routing";
 
-export default createMiddleware(i18nConfig);
+export default createMiddleware(routing);
 
 export const config = {
   matcher: "/((?!api|_next/static|_next/image|favicon-*|site|apple-touch-icon).*)",
diff --git a/src/resources/images/svg/BlueskyLogo.tsx b/src/resources/images/svg/BlueskyLogo.tsx
new file mode 100644
index 0000000..2569e3b
--- /dev/null
+++ b/src/resources/images/svg/BlueskyLogo.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+
+const BlueskyLogo = () => {
+  return (
+    <svg
+      width="600"
+      height="530"
+      version="1.1"
+      xmlns="http://www.w3.org/2000/svg"
+      viewBox="0 0 600 530"
+      className="w-6 h-6"
+    >
+      <path
+        d="m135.72 44.03c66.496 49.921 138.02 151.14 164.28 205.46 26.262-54.316 97.782-155.54 164.28-205.46 47.98-36.021 125.72-63.892 125.72 24.795 0 17.712-10.155 148.79-16.111 170.07-20.703 73.984-96.144 92.854-163.25 81.433 117.3 19.964 147.14 86.092 82.697 152.22-122.39 125.59-175.91-31.511-189.63-71.766-2.514-7.3797-3.6904-10.832-3.7077-7.8964-0.0174-2.9357-1.1937 0.51669-3.7077 7.8964-13.714 40.255-67.233 197.36-189.63 71.766-64.444-66.128-34.605-132.26 82.697-152.22-67.108 11.421-142.55-7.4491-163.25-81.433-5.9562-21.282-16.111-152.36-16.111-170.07 0-88.687 77.742-60.816 125.72-24.795z"
+        fill="#ffffff"
+      />
+    </svg>
+  );
+};
+
+export default BlueskyLogo;
diff --git a/src/resources/images/svg/TwitterLogo.tsx b/src/resources/images/svg/TwitterLogo.tsx
deleted file mode 100644
index 12682b1..0000000
--- a/src/resources/images/svg/TwitterLogo.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-const Logo = () => (
-  <svg
-    className="h-6 w-6"
-    aria-hidden="true"
-    xmlns="http://www.w3.org/2000/svg"
-    fill="currentColor"
-    viewBox="0 0 20 16"
-  >
-    <path
-      fillRule="evenodd"
-      d="M 19.99925,1.893426 C 19.263509,2.215172 18.472269,2.43192 17.642779,2.529418 18.490269,2.029925 19.141261,1.237935 19.447257,0.295196 18.654517,0.757941 17.776278,1.093936 16.841789,1.275434 16.094049,0.490194 15.026812,-3e-4 13.847077,-3e-4 11.19811,-3e-4 9.251134,2.43267 9.849627,4.958638 6.4401695,4.79064 3.4169573,3.18266 1.3927326,0.739191 0.317996,2.554918 0.8354896,4.929388 2.6617167,6.132373 1.9897251,6.111374 1.356733,5.929876 0.80399,5.62688 0.758991,7.498106 2.1217235,9.248584 4.0949488,9.63858 3.517456,9.793078 2.8852139,9.829077 2.241722,9.70758 2.7637155,11.31181 4.2786965,12.479545 6.0749241,12.512545 4.3499456,13.843777 2.1764728,14.43852 0,14.185773 1.8157273,15.331758 3.9734503,16 6.2901714,16 13.908576,16 18.212772,9.666329 17.952526,3.98515 18.754266,3.415157 19.450257,2.703416 20,1.894176 Z"
-    />
-  </svg>
-);
-
-export default Logo;
diff --git a/src/resources/images/svg/YouTubeLogo.tsx b/src/resources/images/svg/YouTubeLogo.tsx
deleted file mode 100644
index 99b4bf0..0000000
--- a/src/resources/images/svg/YouTubeLogo.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-const Logo = () => (
-  <svg
-    className="h-6 w-6"
-    aria-hidden="true"
-    xmlns="http://www.w3.org/2000/svg"
-    fill="currentColor"
-    viewBox="0 0 20 16"
-  >
-    <path
-      fillRule="evenodd"
-      transform="translate(-2.000000, -4.000000)"
-      d="M18.3458333,4.65343552 C15.3425,4.44843838 8.65333333,4.4492717 5.65416667,4.65343552 C2.40666667,4.87509911 2.02416667,6.83673849 2,12 C2.02416667,17.154095 2.40333333,19.1240676 5.65416667,19.3465645 C8.65416667,19.5507283 15.3425,19.5515616 18.3458333,19.3465645 C21.5933333,19.1249009 21.9758333,17.1632615 22,12 C21.9758333,6.84590503 21.5966667,4.87593243 18.3458333,4.65343552 Z M9.5,15.333287 L9.5,8.66671303 L16.1666667,11.9941667 L9.5,15.333287 L9.5,15.333287 Z"
-    />
-  </svg>
-);
-
-export default Logo;
diff --git a/tsconfig.json b/tsconfig.json
index ff836d9..1eb4d30 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -22,7 +22,8 @@
       "@/*": ["./src/*"],
       "@api/*": ["./src/app/api/*"]
     },
-    "downlevelIteration": true
+    "downlevelIteration": true,
+    "target": "ES2017"
   },
   "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
   "exclude": ["node_modules"]
diff --git a/yarn.lock b/yarn.lock
index 00f1331..24e40e0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -436,7 +436,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1":
+"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.6.1":
   version: 4.12.1
   resolution: "@eslint-community/regexpp@npm:4.12.1"
   checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6
@@ -467,6 +467,58 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@floating-ui/core@npm:^1.6.0":
+  version: 1.6.9
+  resolution: "@floating-ui/core@npm:1.6.9"
+  dependencies:
+    "@floating-ui/utils": "npm:^0.2.9"
+  checksum: 10c0/77debdfc26bc36c6f5ae1f26ab3c15468215738b3f5682af4e1915602fa21ba33ad210273f31c9d2da1c531409929e1afb1138b1608c6b54a0f5853ee84c340d
+  languageName: node
+  linkType: hard
+
+"@floating-ui/dom@npm:^1.0.0":
+  version: 1.6.13
+  resolution: "@floating-ui/dom@npm:1.6.13"
+  dependencies:
+    "@floating-ui/core": "npm:^1.6.0"
+    "@floating-ui/utils": "npm:^0.2.9"
+  checksum: 10c0/272242d2eb6238ffcee0cb1f3c66e0eafae804d5d7b449db5ecf904bc37d31ad96cf575a9e650b93c1190f64f49a684b1559d10e05ed3ec210628b19116991a9
+  languageName: node
+  linkType: hard
+
+"@floating-ui/react-dom@npm:^2.1.2":
+  version: 2.1.2
+  resolution: "@floating-ui/react-dom@npm:2.1.2"
+  dependencies:
+    "@floating-ui/dom": "npm:^1.0.0"
+  peerDependencies:
+    react: ">=16.8.0"
+    react-dom: ">=16.8.0"
+  checksum: 10c0/e855131c74e68cab505f7f44f92cd4e2efab1c125796db3116c54c0859323adae4bf697bf292ee83ac77b9335a41ad67852193d7aeace90aa2e1c4a640cafa60
+  languageName: node
+  linkType: hard
+
+"@floating-ui/react@npm:^0.26.16":
+  version: 0.26.28
+  resolution: "@floating-ui/react@npm:0.26.28"
+  dependencies:
+    "@floating-ui/react-dom": "npm:^2.1.2"
+    "@floating-ui/utils": "npm:^0.2.8"
+    tabbable: "npm:^6.0.0"
+  peerDependencies:
+    react: ">=16.8.0"
+    react-dom: ">=16.8.0"
+  checksum: 10c0/a42df129e1e976fe8ba3f4c8efdda265a0196c1b66b83f2b9b27423d08dcc765406f893aeff9d830e70e3f14a9d4c490867eb4c32983317cbaa33863b0fae6f6
+  languageName: node
+  linkType: hard
+
+"@floating-ui/utils@npm:^0.2.8, @floating-ui/utils@npm:^0.2.9":
+  version: 0.2.9
+  resolution: "@floating-ui/utils@npm:0.2.9"
+  checksum: 10c0/48bbed10f91cb7863a796cc0d0e917c78d11aeb89f98d03fc38d79e7eb792224a79f538ed8a2d5d5584511d4ca6354ef35f1712659fd569868e342df4398ad6f
+  languageName: node
+  linkType: hard
+
 "@formatjs/ecma402-abstract@npm:2.0.0":
   version: 2.0.0
   resolution: "@formatjs/ecma402-abstract@npm:2.0.0"
@@ -525,25 +577,27 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@headlessui/react@npm:^1.7.18":
-  version: 1.7.19
-  resolution: "@headlessui/react@npm:1.7.19"
+"@headlessui/react@npm:^2":
+  version: 2.2.0
+  resolution: "@headlessui/react@npm:2.2.0"
   dependencies:
-    "@tanstack/react-virtual": "npm:^3.0.0-beta.60"
-    client-only: "npm:^0.0.1"
+    "@floating-ui/react": "npm:^0.26.16"
+    "@react-aria/focus": "npm:^3.17.1"
+    "@react-aria/interactions": "npm:^3.21.3"
+    "@tanstack/react-virtual": "npm:^3.8.1"
   peerDependencies:
-    react: ^16 || ^17 || ^18
-    react-dom: ^16 || ^17 || ^18
-  checksum: 10c0/c0ece0db6ca15092439177a5322de50b60fa5fd90354ae0f999b3e56abab0065ed54fa7b4b69994ec1bdc23adc6ae9919d7dd57f97922d0b9bb6515d27e3a7e5
+    react: ^18 || ^19 || ^19.0.0-rc
+    react-dom: ^18 || ^19 || ^19.0.0-rc
+  checksum: 10c0/af64ef2868405e629f55925d06ab6723b40a9d0802c1bd91e8ae834b6d50e51e0942bcdee44f4b51ce3b82be7c23df49e93713a29486a899d0d56b7ddce78039
   languageName: node
   linkType: hard
 
-"@headlessui/tailwindcss@npm:^0.1.3":
-  version: 0.1.3
-  resolution: "@headlessui/tailwindcss@npm:0.1.3"
+"@headlessui/tailwindcss@npm:^0.2.2":
+  version: 0.2.2
+  resolution: "@headlessui/tailwindcss@npm:0.2.2"
   peerDependencies:
-    tailwindcss: ^3.0
-  checksum: 10c0/55e1d8528fe995d13afafdb07d86f11dd367d6c7ba1a9780872495a8392170f2aa7c908be450a4c533b33b840849ed6399f6ff23243ea7df6eb4651340855609
+    tailwindcss: ^3.0 || ^4.0
+  checksum: 10c0/97fdaad196b1343ad0bbe7375ecda873c87e0fa3c4c9eca05e3ec20cbe28e40cb982deaee5035d6f621a9a717e0bc0a42b18cb6271aa1b59af5d0dd8d028fce7
   languageName: node
   linkType: hard
 
@@ -1072,81 +1126,74 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@next/env@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/env@npm:14.1.0"
-  checksum: 10c0/f45ce1e3dad87cdbddc58b06bd411f44a6d21dfc2c344d02a5e1b07f56fbc9a39e192c0b0917df9f2e9e4e2156306a8c78f173ca4b53932c2793e67797462a23
+"@next/env@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/env@npm:15.1.7"
+  checksum: 10c0/ad7761078552d8c88fe3c87224a3761d1bca82a15c747f417f561f92a4521898f227e3e7d2e8e65227a5ac8364ea8a2351c1febec5b5aa2ac1dcf016dd065edd
   languageName: node
   linkType: hard
 
-"@next/eslint-plugin-next@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/eslint-plugin-next@npm:14.1.0"
+"@next/eslint-plugin-next@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/eslint-plugin-next@npm:15.1.7"
   dependencies:
-    glob: "npm:10.3.10"
-  checksum: 10c0/d8753d8258bef471ba1296f760b092c0a17e89ddc937bf16b9399725d05b6426e58e3c8eb4efb8e8f027025804ecea3b714a7b7c75682d019e53ea8d181b8632
+    fast-glob: "npm:3.3.1"
+  checksum: 10c0/22603c52e39a46c7883454fd49b91745d11a6b38b4fcb42bfc4ba0b2b3a9beb37875ecbf4291fff7ed809400ae69676a5e34a2de3d227e6576a96cb00aaee53c
   languageName: node
   linkType: hard
 
-"@next/swc-darwin-arm64@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-darwin-arm64@npm:14.1.0"
+"@next/swc-darwin-arm64@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/swc-darwin-arm64@npm:15.1.7"
   conditions: os=darwin & cpu=arm64
   languageName: node
   linkType: hard
 
-"@next/swc-darwin-x64@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-darwin-x64@npm:14.1.0"
+"@next/swc-darwin-x64@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/swc-darwin-x64@npm:15.1.7"
   conditions: os=darwin & cpu=x64
   languageName: node
   linkType: hard
 
-"@next/swc-linux-arm64-gnu@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-linux-arm64-gnu@npm:14.1.0"
+"@next/swc-linux-arm64-gnu@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/swc-linux-arm64-gnu@npm:15.1.7"
   conditions: os=linux & cpu=arm64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@next/swc-linux-arm64-musl@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-linux-arm64-musl@npm:14.1.0"
+"@next/swc-linux-arm64-musl@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/swc-linux-arm64-musl@npm:15.1.7"
   conditions: os=linux & cpu=arm64 & libc=musl
   languageName: node
   linkType: hard
 
-"@next/swc-linux-x64-gnu@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-linux-x64-gnu@npm:14.1.0"
+"@next/swc-linux-x64-gnu@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/swc-linux-x64-gnu@npm:15.1.7"
   conditions: os=linux & cpu=x64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@next/swc-linux-x64-musl@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-linux-x64-musl@npm:14.1.0"
+"@next/swc-linux-x64-musl@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/swc-linux-x64-musl@npm:15.1.7"
   conditions: os=linux & cpu=x64 & libc=musl
   languageName: node
   linkType: hard
 
-"@next/swc-win32-arm64-msvc@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-win32-arm64-msvc@npm:14.1.0"
+"@next/swc-win32-arm64-msvc@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/swc-win32-arm64-msvc@npm:15.1.7"
   conditions: os=win32 & cpu=arm64
   languageName: node
   linkType: hard
 
-"@next/swc-win32-ia32-msvc@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-win32-ia32-msvc@npm:14.1.0"
-  conditions: os=win32 & cpu=ia32
-  languageName: node
-  linkType: hard
-
-"@next/swc-win32-x64-msvc@npm:14.1.0":
-  version: 14.1.0
-  resolution: "@next/swc-win32-x64-msvc@npm:14.1.0"
+"@next/swc-win32-x64-msvc@npm:15.1.7":
+  version: 15.1.7
+  resolution: "@next/swc-win32-x64-msvc@npm:15.1.7"
   conditions: os=win32 & cpu=x64
   languageName: node
   linkType: hard
@@ -1221,6 +1268,84 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@react-aria/focus@npm:^3.17.1":
+  version: 3.19.1
+  resolution: "@react-aria/focus@npm:3.19.1"
+  dependencies:
+    "@react-aria/interactions": "npm:^3.23.0"
+    "@react-aria/utils": "npm:^3.27.0"
+    "@react-types/shared": "npm:^3.27.0"
+    "@swc/helpers": "npm:^0.5.0"
+    clsx: "npm:^2.0.0"
+  peerDependencies:
+    react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+    react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+  checksum: 10c0/3cc0c971dea11a187cbda3ff35b43a8f6d85d1b889c70dc3ffd4ede3aaa0d6038809f868be95f23d84c04c4738ec654bf97fb8de501f2ee0765e1e5ba1482102
+  languageName: node
+  linkType: hard
+
+"@react-aria/interactions@npm:^3.21.3, @react-aria/interactions@npm:^3.23.0":
+  version: 3.23.0
+  resolution: "@react-aria/interactions@npm:3.23.0"
+  dependencies:
+    "@react-aria/ssr": "npm:^3.9.7"
+    "@react-aria/utils": "npm:^3.27.0"
+    "@react-types/shared": "npm:^3.27.0"
+    "@swc/helpers": "npm:^0.5.0"
+  peerDependencies:
+    react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+    react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+  checksum: 10c0/913c6e0c585a8a58af4efc6c5081093b1588d12e92e5f072471481e64c8690f16f233ee41765e79956c7f83400c17e6e1da7e82437581e393a3344d691177891
+  languageName: node
+  linkType: hard
+
+"@react-aria/ssr@npm:^3.9.7":
+  version: 3.9.7
+  resolution: "@react-aria/ssr@npm:3.9.7"
+  dependencies:
+    "@swc/helpers": "npm:^0.5.0"
+  peerDependencies:
+    react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+  checksum: 10c0/37168cd81b1e8223aedb906c1333381f3c436dadf58cbd675606ced314605ce5c49eee5c831309648bfbab78a8598c344be636a85962c742ebf11ae7e87ee93e
+  languageName: node
+  linkType: hard
+
+"@react-aria/utils@npm:^3.27.0":
+  version: 3.27.0
+  resolution: "@react-aria/utils@npm:3.27.0"
+  dependencies:
+    "@react-aria/ssr": "npm:^3.9.7"
+    "@react-stately/utils": "npm:^3.10.5"
+    "@react-types/shared": "npm:^3.27.0"
+    "@swc/helpers": "npm:^0.5.0"
+    clsx: "npm:^2.0.0"
+  peerDependencies:
+    react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+    react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+  checksum: 10c0/28f12eb6e416567244ffa88a91392d10a719b68fb6e5f14871aa934c163ab36aa70f79ba3fda46d8eb9dd047c0fa2af7fd08e9d05eefde2395ebbe3c260d63da
+  languageName: node
+  linkType: hard
+
+"@react-stately/utils@npm:^3.10.5":
+  version: 3.10.5
+  resolution: "@react-stately/utils@npm:3.10.5"
+  dependencies:
+    "@swc/helpers": "npm:^0.5.0"
+  peerDependencies:
+    react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+  checksum: 10c0/188500fef26361515d1a6c506ad347f68c82a1d61a13a09109080966a195e1cf97686991143e0d5684fd0b1a3c6a0a1e0149c92c0665bcc34c2026cd9730af1c
+  languageName: node
+  linkType: hard
+
+"@react-types/shared@npm:^3.27.0":
+  version: 3.27.0
+  resolution: "@react-types/shared@npm:3.27.0"
+  peerDependencies:
+    react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
+  checksum: 10c0/7d710d776dcb6a832d3dc5ec281a81cd00a5b5aec1ecfcf7799c73bdb62c8739e574e29dafe04f7710a0ea568e4e11621091658a9c73d3191e1b0f5f3ff21f95
+  languageName: node
+  linkType: hard
+
 "@rtsao/scc@npm:^1.1.0":
   version: 1.1.0
   resolution: "@rtsao/scc@npm:1.1.0"
@@ -1228,10 +1353,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rushstack/eslint-patch@npm:^1.3.3":
-  version: 1.10.4
-  resolution: "@rushstack/eslint-patch@npm:1.10.4"
-  checksum: 10c0/de312bd7a3cb0f313c9720029eb719d8762fe54946cce2d33ac142b1cbb5817c4a5a92518dfa476c26311602d37f5a8f7caa90a0c73e3d6a56f9a05d2799c172
+"@rushstack/eslint-patch@npm:^1.10.3":
+  version: 1.10.5
+  resolution: "@rushstack/eslint-patch@npm:1.10.5"
+  checksum: 10c0/ea66e8be3a78a48d06e8ff33221cef5749386589236bbcd24013577d2b4e1035e3dc919740c6e0f16d44c1e0b62d06d00898508fc74cc2adb0ac6b667aa5a8e4
   languageName: node
   linkType: hard
 
@@ -1260,31 +1385,38 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@swc/helpers@npm:0.5.2":
-  version: 0.5.2
-  resolution: "@swc/helpers@npm:0.5.2"
+"@swc/counter@npm:0.1.3":
+  version: 0.1.3
+  resolution: "@swc/counter@npm:0.1.3"
+  checksum: 10c0/8424f60f6bf8694cfd2a9bca45845bce29f26105cda8cf19cdb9fd3e78dc6338699e4db77a89ae449260bafa1cc6bec307e81e7fb96dbf7dcfce0eea55151356
+  languageName: node
+  linkType: hard
+
+"@swc/helpers@npm:0.5.15, @swc/helpers@npm:^0.5.0":
+  version: 0.5.15
+  resolution: "@swc/helpers@npm:0.5.15"
   dependencies:
-    tslib: "npm:^2.4.0"
-  checksum: 10c0/b6fa49bcf6c00571d0eb7837b163f8609960d4d77538160585e27ed167361e9776bd6e5eb9646ffac2fb4d43c58df9ca50dab9d96ab097e6591bc82a75fd1164
+    tslib: "npm:^2.8.0"
+  checksum: 10c0/33002f74f6f885f04c132960835fdfc474186983ea567606db62e86acd0680ca82f34647e8e610f4e1e422d1c16fce729dde22cd3b797ab1fd9061a825dabca4
   languageName: node
   linkType: hard
 
-"@tanstack/react-virtual@npm:^3.0.0-beta.60":
-  version: 3.11.1
-  resolution: "@tanstack/react-virtual@npm:3.11.1"
+"@tanstack/react-virtual@npm:^3.8.1":
+  version: 3.13.0
+  resolution: "@tanstack/react-virtual@npm:3.13.0"
   dependencies:
-    "@tanstack/virtual-core": "npm:3.10.9"
+    "@tanstack/virtual-core": "npm:3.13.0"
   peerDependencies:
     react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
     react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
-  checksum: 10c0/5eafab335de0c65daa8ad8fe732c506bea922861032aad35732ca65ff16fdbb111bd4e72fafc0d3f4449a793ceba4ed0fcadb27390bda6f5c8ba4a55737d556d
+  checksum: 10c0/89eaf76294be3cfe294304859db2b0f7364a9bf8934849e42cb8ef08435dfb28f5504d57b08b30426a811f127f8f6320ec6c14aa7fcd304bde4fbfeaef04bc36
   languageName: node
   linkType: hard
 
-"@tanstack/virtual-core@npm:3.10.9":
-  version: 3.10.9
-  resolution: "@tanstack/virtual-core@npm:3.10.9"
-  checksum: 10c0/960cde330235daff5403e0da23837ac30a0f95a02c8866692f8382d03b675d6802e129436b0b3e7e737d8a5347ef2cadec448ba4c0878462228c094440d0207b
+"@tanstack/virtual-core@npm:3.13.0":
+  version: 3.13.0
+  resolution: "@tanstack/virtual-core@npm:3.13.0"
+  checksum: 10c0/88ff5fbd59b449e2ec2cafa9cac1ea6f449d84a593fb869ad9e8a467813e9060d4065517c5657c2de765c165453bb336751641cf670cb27143d51791bbfedc3a
   languageName: node
   linkType: hard
 
@@ -1478,13 +1610,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/json-schema@npm:^7.0.12":
-  version: 7.0.15
-  resolution: "@types/json-schema@npm:7.0.15"
-  checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db
-  languageName: node
-  linkType: hard
-
 "@types/json5@npm:^0.0.29":
   version: 0.0.29
   resolution: "@types/json5@npm:0.0.29"
@@ -1492,7 +1617,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/node@npm:*, @types/node@npm:^20":
+"@types/node@npm:*":
   version: 20.17.5
   resolution: "@types/node@npm:20.17.5"
   dependencies:
@@ -1501,6 +1626,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/node@npm:^20":
+  version: 20.16.5
+  resolution: "@types/node@npm:20.16.5"
+  dependencies:
+    undici-types: "npm:~6.19.2"
+  checksum: 10c0/6af7994129815010bcbc4cf8221865559c8116ff43e74a6549525c2108267596fc2d18aff5d5ecfe089fb60a119f975631343e2c65c52bfa0955ed9dc56733d6
+  languageName: node
+  linkType: hard
+
 "@types/prop-types@npm:*":
   version: 15.7.14
   resolution: "@types/prop-types@npm:15.7.14"
@@ -1527,13 +1661,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/semver@npm:^7.5.0":
-  version: 7.5.8
-  resolution: "@types/semver@npm:7.5.8"
-  checksum: 10c0/8663ff927234d1c5fcc04b33062cb2b9fcfbe0f5f351ed26c4d1e1581657deebd506b41ff7fdf89e787e3d33ce05854bc01686379b89e9c49b564c4cfa988efa
-  languageName: node
-  linkType: hard
-
 "@types/stack-utils@npm:^2.0.0":
   version: 2.0.3
   resolution: "@types/stack-utils@npm:2.0.3"
@@ -1573,126 +1700,115 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@typescript-eslint/eslint-plugin@npm:^6.0.0":
-  version: 6.21.0
-  resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0"
+"@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/eslint-plugin@npm:^8.9.0":
+  version: 8.24.1
+  resolution: "@typescript-eslint/eslint-plugin@npm:8.24.1"
   dependencies:
-    "@eslint-community/regexpp": "npm:^4.5.1"
-    "@typescript-eslint/scope-manager": "npm:6.21.0"
-    "@typescript-eslint/type-utils": "npm:6.21.0"
-    "@typescript-eslint/utils": "npm:6.21.0"
-    "@typescript-eslint/visitor-keys": "npm:6.21.0"
-    debug: "npm:^4.3.4"
+    "@eslint-community/regexpp": "npm:^4.10.0"
+    "@typescript-eslint/scope-manager": "npm:8.24.1"
+    "@typescript-eslint/type-utils": "npm:8.24.1"
+    "@typescript-eslint/utils": "npm:8.24.1"
+    "@typescript-eslint/visitor-keys": "npm:8.24.1"
     graphemer: "npm:^1.4.0"
-    ignore: "npm:^5.2.4"
+    ignore: "npm:^5.3.1"
     natural-compare: "npm:^1.4.0"
-    semver: "npm:^7.5.4"
-    ts-api-utils: "npm:^1.0.1"
+    ts-api-utils: "npm:^2.0.1"
   peerDependencies:
-    "@typescript-eslint/parser": ^6.0.0 || ^6.0.0-alpha
-    eslint: ^7.0.0 || ^8.0.0
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  checksum: 10c0/f911a79ee64d642f814a3b6cdb0d324b5f45d9ef955c5033e78903f626b7239b4aa773e464a38c3e667519066169d983538f2bf8e5d00228af587c9d438fb344
+    "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0
+    eslint: ^8.57.0 || ^9.0.0
+    typescript: ">=4.8.4 <5.8.0"
+  checksum: 10c0/fe5f56f248370f40322a7cb2d96fbab724a7a8892895e3d41027c9a1df309916433633e04df84a1d3f9535d282953738b1ad627d8af37ab288a39a6e411afd76
   languageName: node
   linkType: hard
 
-"@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0, @typescript-eslint/parser@npm:^6.0.0":
-  version: 6.21.0
-  resolution: "@typescript-eslint/parser@npm:6.21.0"
+"@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/parser@npm:^8.9.0":
+  version: 8.24.1
+  resolution: "@typescript-eslint/parser@npm:8.24.1"
   dependencies:
-    "@typescript-eslint/scope-manager": "npm:6.21.0"
-    "@typescript-eslint/types": "npm:6.21.0"
-    "@typescript-eslint/typescript-estree": "npm:6.21.0"
-    "@typescript-eslint/visitor-keys": "npm:6.21.0"
+    "@typescript-eslint/scope-manager": "npm:8.24.1"
+    "@typescript-eslint/types": "npm:8.24.1"
+    "@typescript-eslint/typescript-estree": "npm:8.24.1"
+    "@typescript-eslint/visitor-keys": "npm:8.24.1"
     debug: "npm:^4.3.4"
   peerDependencies:
-    eslint: ^7.0.0 || ^8.0.0
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  checksum: 10c0/a8f99820679decd0d115c0af61903fb1de3b1b5bec412dc72b67670bf636de77ab07f2a68ee65d6da7976039bbf636907f9d5ca546db3f0b98a31ffbc225bc7d
+    eslint: ^8.57.0 || ^9.0.0
+    typescript: ">=4.8.4 <5.8.0"
+  checksum: 10c0/9de557698c8debf3de06b6adf6aa06a8345e0e38600e5ccbeda62270d1a4a757dfa191db89d4e86cf373103a11bef1965c9d9889f622c51f4f26d1bf12394ae3
   languageName: node
   linkType: hard
 
-"@typescript-eslint/scope-manager@npm:6.21.0":
-  version: 6.21.0
-  resolution: "@typescript-eslint/scope-manager@npm:6.21.0"
+"@typescript-eslint/scope-manager@npm:8.24.1":
+  version: 8.24.1
+  resolution: "@typescript-eslint/scope-manager@npm:8.24.1"
   dependencies:
-    "@typescript-eslint/types": "npm:6.21.0"
-    "@typescript-eslint/visitor-keys": "npm:6.21.0"
-  checksum: 10c0/eaf868938d811cbbea33e97e44ba7050d2b6892202cea6a9622c486b85ab1cf801979edf78036179a8ba4ac26f1dfdf7fcc83a68c1ff66be0b3a8e9a9989b526
+    "@typescript-eslint/types": "npm:8.24.1"
+    "@typescript-eslint/visitor-keys": "npm:8.24.1"
+  checksum: 10c0/779880743ed7ab67fe477f1ad5648bbd77ad69b4663b5a42024112004c8f231049b1e4eeb67e260005769c3bb005049e00a80b885e19d593ffb080bd39f4fa94
   languageName: node
   linkType: hard
 
-"@typescript-eslint/type-utils@npm:6.21.0":
-  version: 6.21.0
-  resolution: "@typescript-eslint/type-utils@npm:6.21.0"
+"@typescript-eslint/type-utils@npm:8.24.1":
+  version: 8.24.1
+  resolution: "@typescript-eslint/type-utils@npm:8.24.1"
   dependencies:
-    "@typescript-eslint/typescript-estree": "npm:6.21.0"
-    "@typescript-eslint/utils": "npm:6.21.0"
+    "@typescript-eslint/typescript-estree": "npm:8.24.1"
+    "@typescript-eslint/utils": "npm:8.24.1"
     debug: "npm:^4.3.4"
-    ts-api-utils: "npm:^1.0.1"
+    ts-api-utils: "npm:^2.0.1"
   peerDependencies:
-    eslint: ^7.0.0 || ^8.0.0
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  checksum: 10c0/7409c97d1c4a4386b488962739c4f1b5b04dc60cf51f8cd88e6b12541f84d84c6b8b67e491a147a2c95f9ec486539bf4519fb9d418411aef6537b9c156468117
+    eslint: ^8.57.0 || ^9.0.0
+    typescript: ">=4.8.4 <5.8.0"
+  checksum: 10c0/ba248bc12068383374d9d077f9cca1815f347ea008d04d08ad7a54dbef70189a0da7872246f8369e6d30938fa7e408dadcda0ae71041be68fc836c886dd9c3ab
   languageName: node
   linkType: hard
 
-"@typescript-eslint/types@npm:6.21.0":
-  version: 6.21.0
-  resolution: "@typescript-eslint/types@npm:6.21.0"
-  checksum: 10c0/020631d3223bbcff8a0da3efbdf058220a8f48a3de221563996ad1dcc30d6c08dadc3f7608cc08830d21c0d565efd2db19b557b9528921c78aabb605eef2d74d
+"@typescript-eslint/types@npm:8.24.1":
+  version: 8.24.1
+  resolution: "@typescript-eslint/types@npm:8.24.1"
+  checksum: 10c0/ebb40ce16c746ef236dbcc25cb2e6950753ca6fb34d04ed7d477016370de1fdaf7402ed4569673c6ff14bf60af7124ff45c6ddd9328d2f8c94dc04178368e2a3
   languageName: node
   linkType: hard
 
-"@typescript-eslint/typescript-estree@npm:6.21.0":
-  version: 6.21.0
-  resolution: "@typescript-eslint/typescript-estree@npm:6.21.0"
+"@typescript-eslint/typescript-estree@npm:8.24.1":
+  version: 8.24.1
+  resolution: "@typescript-eslint/typescript-estree@npm:8.24.1"
   dependencies:
-    "@typescript-eslint/types": "npm:6.21.0"
-    "@typescript-eslint/visitor-keys": "npm:6.21.0"
+    "@typescript-eslint/types": "npm:8.24.1"
+    "@typescript-eslint/visitor-keys": "npm:8.24.1"
     debug: "npm:^4.3.4"
-    globby: "npm:^11.1.0"
+    fast-glob: "npm:^3.3.2"
     is-glob: "npm:^4.0.3"
-    minimatch: "npm:9.0.3"
-    semver: "npm:^7.5.4"
-    ts-api-utils: "npm:^1.0.1"
-  peerDependenciesMeta:
-    typescript:
-      optional: true
-  checksum: 10c0/af1438c60f080045ebb330155a8c9bb90db345d5069cdd5d01b67de502abb7449d6c75500519df829f913a6b3f490ade3e8215279b6bdc63d0fb0ae61034df5f
+    minimatch: "npm:^9.0.4"
+    semver: "npm:^7.6.0"
+    ts-api-utils: "npm:^2.0.1"
+  peerDependencies:
+    typescript: ">=4.8.4 <5.8.0"
+  checksum: 10c0/8eeeae6e8de1cd83f2eddd52293e9c31a655e0974cc2d410f00ba2b6fd6bb9aec1c346192d5784d64d0d1b15a55e56e35550788c04dda87e0f1a99b21a3eb709
   languageName: node
   linkType: hard
 
-"@typescript-eslint/utils@npm:6.21.0":
-  version: 6.21.0
-  resolution: "@typescript-eslint/utils@npm:6.21.0"
+"@typescript-eslint/utils@npm:8.24.1":
+  version: 8.24.1
+  resolution: "@typescript-eslint/utils@npm:8.24.1"
   dependencies:
     "@eslint-community/eslint-utils": "npm:^4.4.0"
-    "@types/json-schema": "npm:^7.0.12"
-    "@types/semver": "npm:^7.5.0"
-    "@typescript-eslint/scope-manager": "npm:6.21.0"
-    "@typescript-eslint/types": "npm:6.21.0"
-    "@typescript-eslint/typescript-estree": "npm:6.21.0"
-    semver: "npm:^7.5.4"
+    "@typescript-eslint/scope-manager": "npm:8.24.1"
+    "@typescript-eslint/types": "npm:8.24.1"
+    "@typescript-eslint/typescript-estree": "npm:8.24.1"
   peerDependencies:
-    eslint: ^7.0.0 || ^8.0.0
-  checksum: 10c0/ab2df3833b2582d4e5467a484d08942b4f2f7208f8e09d67de510008eb8001a9b7460f2f9ba11c12086fd3cdcac0c626761c7995c2c6b5657d5fa6b82030a32d
+    eslint: ^8.57.0 || ^9.0.0
+    typescript: ">=4.8.4 <5.8.0"
+  checksum: 10c0/b3300d5c7e18ec524a46bf683052539f24df0d8c709e39e3bde9dfc6c65180610c46b875f1f4eaad5e311193a56acdfd7111a73f1e8aec4108e9cd19561bf8b8
   languageName: node
   linkType: hard
 
-"@typescript-eslint/visitor-keys@npm:6.21.0":
-  version: 6.21.0
-  resolution: "@typescript-eslint/visitor-keys@npm:6.21.0"
+"@typescript-eslint/visitor-keys@npm:8.24.1":
+  version: 8.24.1
+  resolution: "@typescript-eslint/visitor-keys@npm:8.24.1"
   dependencies:
-    "@typescript-eslint/types": "npm:6.21.0"
-    eslint-visitor-keys: "npm:^3.4.1"
-  checksum: 10c0/7395f69739cfa1cb83c1fb2fad30afa2a814756367302fb4facd5893eff66abc807e8d8f63eba94ed3b0fe0c1c996ac9a1680bcbf0f83717acedc3f2bb724fbf
+    "@typescript-eslint/types": "npm:8.24.1"
+    eslint-visitor-keys: "npm:^4.2.0"
+  checksum: 10c0/ba09412fb4b1605aa73c890909c9a8dba2aa72e00ccd7d69baad17c564eedd77f489a06b1686985c7f0c49724787b82d76dcf4c146c4de44ef2c8776a9b6ad2b
   languageName: node
   linkType: hard
 
@@ -1911,6 +2027,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"array-buffer-byte-length@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "array-buffer-byte-length@npm:1.0.2"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    is-array-buffer: "npm:^3.0.5"
+  checksum: 10c0/74e1d2d996941c7a1badda9cabb7caab8c449db9086407cad8a1b71d2604cc8abf105db8ca4e02c04579ec58b7be40279ddb09aea4784832984485499f48432d
+  languageName: node
+  linkType: hard
+
 "array-includes@npm:^3.1.6, array-includes@npm:^3.1.8":
   version: 3.1.8
   resolution: "array-includes@npm:3.1.8"
@@ -1925,13 +2051,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"array-union@npm:^2.1.0":
-  version: 2.1.0
-  resolution: "array-union@npm:2.1.0"
-  checksum: 10c0/429897e68110374f39b771ec47a7161fc6a8fc33e196857c0a396dc75df0b5f65e4d046674db764330b6bb66b39ef48dd7c53b6a2ee75cfb0681e0c1a7033962
-  languageName: node
-  linkType: hard
-
 "array.prototype.findlast@npm:^1.2.5":
   version: 1.2.5
   resolution: "array.prototype.findlast@npm:1.2.5"
@@ -1984,6 +2103,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"array.prototype.flatmap@npm:^1.3.3":
+  version: 1.3.3
+  resolution: "array.prototype.flatmap@npm:1.3.3"
+  dependencies:
+    call-bind: "npm:^1.0.8"
+    define-properties: "npm:^1.2.1"
+    es-abstract: "npm:^1.23.5"
+    es-shim-unscopables: "npm:^1.0.2"
+  checksum: 10c0/ba899ea22b9dc9bf276e773e98ac84638ed5e0236de06f13d63a90b18ca9e0ec7c97d622d899796e3773930b946cd2413d098656c0c5d8cc58c6f25c21e6bd54
+  languageName: node
+  linkType: hard
+
 "array.prototype.tosorted@npm:^1.1.4":
   version: 1.1.4
   resolution: "array.prototype.tosorted@npm:1.1.4"
@@ -2013,6 +2144,21 @@ __metadata:
   languageName: node
   linkType: hard
 
+"arraybuffer.prototype.slice@npm:^1.0.4":
+  version: 1.0.4
+  resolution: "arraybuffer.prototype.slice@npm:1.0.4"
+  dependencies:
+    array-buffer-byte-length: "npm:^1.0.1"
+    call-bind: "npm:^1.0.8"
+    define-properties: "npm:^1.2.1"
+    es-abstract: "npm:^1.23.5"
+    es-errors: "npm:^1.3.0"
+    get-intrinsic: "npm:^1.2.6"
+    is-array-buffer: "npm:^3.0.4"
+  checksum: 10c0/2f2459caa06ae0f7f615003f9104b01f6435cc803e11bd2a655107d52a1781dc040532dc44d93026b694cc18793993246237423e13a5337e86b43ed604932c06
+  languageName: node
+  linkType: hard
+
 "ast-types-flow@npm:^0.0.8":
   version: 0.0.8
   resolution: "ast-types-flow@npm:0.0.8"
@@ -2270,13 +2416,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"call-bound@npm:^1.0.2":
-  version: 1.0.2
-  resolution: "call-bound@npm:1.0.2"
+"call-bound@npm:^1.0.2, call-bound@npm:^1.0.3":
+  version: 1.0.3
+  resolution: "call-bound@npm:1.0.3"
   dependencies:
-    call-bind: "npm:^1.0.8"
-    get-intrinsic: "npm:^1.2.5"
-  checksum: 10c0/19761e1ce55578f9c41bed06b162de22058d0228cd9c14215d8db73716594ef61b87e5e7486855ea04becd86f55ed08ed81e2c25a4a56962ca12e6b0e772f141
+    call-bind-apply-helpers: "npm:^1.0.1"
+    get-intrinsic: "npm:^1.2.6"
+  checksum: 10c0/45257b8e7621067304b30dbd638e856cac913d31e8e00a80d6cf172911acd057846572d0b256b45e652d515db6601e2974a1b1a040e91b4fc36fb3dd86fa69cf
   languageName: node
   linkType: hard
 
@@ -2382,7 +2528,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"client-only@npm:0.0.1, client-only@npm:^0.0.1":
+"client-only@npm:0.0.1":
   version: 0.0.1
   resolution: "client-only@npm:0.0.1"
   checksum: 10c0/9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358
@@ -2400,6 +2546,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"clsx@npm:^2.0.0":
+  version: 2.1.1
+  resolution: "clsx@npm:2.1.1"
+  checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839
+  languageName: node
+  linkType: hard
+
 "co@npm:^4.6.0":
   version: 4.6.0
   resolution: "co@npm:4.6.0"
@@ -2513,7 +2666,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3":
+"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3":
+  version: 7.0.3
+  resolution: "cross-spawn@npm:7.0.3"
+  dependencies:
+    path-key: "npm:^3.1.0"
+    shebang-command: "npm:^2.0.0"
+    which: "npm:^2.0.1"
+  checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750
+  languageName: node
+  linkType: hard
+
+"cross-spawn@npm:^7.0.2":
   version: 7.0.6
   resolution: "cross-spawn@npm:7.0.6"
   dependencies:
@@ -2599,6 +2763,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"data-view-buffer@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "data-view-buffer@npm:1.0.2"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    es-errors: "npm:^1.3.0"
+    is-data-view: "npm:^1.0.2"
+  checksum: 10c0/7986d40fc7979e9e6241f85db8d17060dd9a71bd53c894fa29d126061715e322a4cd47a00b0b8c710394854183d4120462b980b8554012acc1c0fa49df7ad38c
+  languageName: node
+  linkType: hard
+
 "data-view-byte-length@npm:^1.0.1":
   version: 1.0.1
   resolution: "data-view-byte-length@npm:1.0.1"
@@ -2610,6 +2785,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"data-view-byte-length@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "data-view-byte-length@npm:1.0.2"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    es-errors: "npm:^1.3.0"
+    is-data-view: "npm:^1.0.2"
+  checksum: 10c0/f8a4534b5c69384d95ac18137d381f18a5cfae1f0fc1df0ef6feef51ef0d568606d970b69e02ea186c6c0f0eac77fe4e6ad96fec2569cc86c3afcc7475068c55
+  languageName: node
+  linkType: hard
+
 "data-view-byte-offset@npm:^1.0.0":
   version: 1.0.0
   resolution: "data-view-byte-offset@npm:1.0.0"
@@ -2621,6 +2807,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"data-view-byte-offset@npm:^1.0.1":
+  version: 1.0.1
+  resolution: "data-view-byte-offset@npm:1.0.1"
+  dependencies:
+    call-bound: "npm:^1.0.2"
+    es-errors: "npm:^1.3.0"
+    is-data-view: "npm:^1.0.1"
+  checksum: 10c0/fa7aa40078025b7810dcffc16df02c480573b7b53ef1205aa6a61533011005c1890e5ba17018c692ce7c900212b547262d33279fde801ad9843edc0863bf78c4
+  languageName: node
+  linkType: hard
+
 "debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4":
   version: 4.3.7
   resolution: "debug@npm:4.3.7"
@@ -2784,15 +2981,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"dir-glob@npm:^3.0.1":
-  version: 3.0.1
-  resolution: "dir-glob@npm:3.0.1"
-  dependencies:
-    path-type: "npm:^4.0.0"
-  checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c
-  languageName: node
-  linkType: hard
-
 "dlv@npm:^1.1.3":
   version: 1.1.3
   resolution: "dlv@npm:1.1.3"
@@ -2841,14 +3029,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"dunder-proto@npm:^1.0.0":
-  version: 1.0.0
-  resolution: "dunder-proto@npm:1.0.0"
+"dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1":
+  version: 1.0.1
+  resolution: "dunder-proto@npm:1.0.1"
   dependencies:
-    call-bind-apply-helpers: "npm:^1.0.0"
+    call-bind-apply-helpers: "npm:^1.0.1"
     es-errors: "npm:^1.3.0"
     gopd: "npm:^1.2.0"
-  checksum: 10c0/b321e5cbf64f0a4c786b0b3dc187eb5197a83f6e05a1e11b86db25251b3ae6747c4b805d9e0a4fbf481d22a86a539dc75f82d883daeac7fc2ce4bd72ff5ef5a2
+  checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031
   languageName: node
   linkType: hard
 
@@ -2936,7 +3124,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"es-abstract@npm:^1.17.5, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5":
+"es-abstract@npm:^1.17.5, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3":
   version: 1.23.5
   resolution: "es-abstract@npm:1.23.5"
   dependencies:
@@ -2990,6 +3178,65 @@ __metadata:
   languageName: node
   linkType: hard
 
+"es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6, es-abstract@npm:^1.23.9":
+  version: 1.23.9
+  resolution: "es-abstract@npm:1.23.9"
+  dependencies:
+    array-buffer-byte-length: "npm:^1.0.2"
+    arraybuffer.prototype.slice: "npm:^1.0.4"
+    available-typed-arrays: "npm:^1.0.7"
+    call-bind: "npm:^1.0.8"
+    call-bound: "npm:^1.0.3"
+    data-view-buffer: "npm:^1.0.2"
+    data-view-byte-length: "npm:^1.0.2"
+    data-view-byte-offset: "npm:^1.0.1"
+    es-define-property: "npm:^1.0.1"
+    es-errors: "npm:^1.3.0"
+    es-object-atoms: "npm:^1.0.0"
+    es-set-tostringtag: "npm:^2.1.0"
+    es-to-primitive: "npm:^1.3.0"
+    function.prototype.name: "npm:^1.1.8"
+    get-intrinsic: "npm:^1.2.7"
+    get-proto: "npm:^1.0.0"
+    get-symbol-description: "npm:^1.1.0"
+    globalthis: "npm:^1.0.4"
+    gopd: "npm:^1.2.0"
+    has-property-descriptors: "npm:^1.0.2"
+    has-proto: "npm:^1.2.0"
+    has-symbols: "npm:^1.1.0"
+    hasown: "npm:^2.0.2"
+    internal-slot: "npm:^1.1.0"
+    is-array-buffer: "npm:^3.0.5"
+    is-callable: "npm:^1.2.7"
+    is-data-view: "npm:^1.0.2"
+    is-regex: "npm:^1.2.1"
+    is-shared-array-buffer: "npm:^1.0.4"
+    is-string: "npm:^1.1.1"
+    is-typed-array: "npm:^1.1.15"
+    is-weakref: "npm:^1.1.0"
+    math-intrinsics: "npm:^1.1.0"
+    object-inspect: "npm:^1.13.3"
+    object-keys: "npm:^1.1.1"
+    object.assign: "npm:^4.1.7"
+    own-keys: "npm:^1.0.1"
+    regexp.prototype.flags: "npm:^1.5.3"
+    safe-array-concat: "npm:^1.1.3"
+    safe-push-apply: "npm:^1.0.0"
+    safe-regex-test: "npm:^1.1.0"
+    set-proto: "npm:^1.0.0"
+    string.prototype.trim: "npm:^1.2.10"
+    string.prototype.trimend: "npm:^1.0.9"
+    string.prototype.trimstart: "npm:^1.0.8"
+    typed-array-buffer: "npm:^1.0.3"
+    typed-array-byte-length: "npm:^1.0.3"
+    typed-array-byte-offset: "npm:^1.0.4"
+    typed-array-length: "npm:^1.0.7"
+    unbox-primitive: "npm:^1.1.0"
+    which-typed-array: "npm:^1.1.18"
+  checksum: 10c0/1de229c9e08fe13c17fe5abaec8221545dfcd57e51f64909599a6ae896df84b8fd2f7d16c60cb00d7bf495b9298ca3581aded19939d4b7276854a4b066f8422b
+  languageName: node
+  linkType: hard
+
 "es-define-property@npm:^1.0.0, es-define-property@npm:^1.0.1":
   version: 1.0.1
   resolution: "es-define-property@npm:1.0.1"
@@ -3021,26 +3268,27 @@ __metadata:
   languageName: node
   linkType: hard
 
-"es-iterator-helpers@npm:^1.1.0":
-  version: 1.2.0
-  resolution: "es-iterator-helpers@npm:1.2.0"
+"es-iterator-helpers@npm:^1.2.1":
+  version: 1.2.1
+  resolution: "es-iterator-helpers@npm:1.2.1"
   dependencies:
-    call-bind: "npm:^1.0.7"
+    call-bind: "npm:^1.0.8"
+    call-bound: "npm:^1.0.3"
     define-properties: "npm:^1.2.1"
-    es-abstract: "npm:^1.23.3"
+    es-abstract: "npm:^1.23.6"
     es-errors: "npm:^1.3.0"
     es-set-tostringtag: "npm:^2.0.3"
     function-bind: "npm:^1.1.2"
-    get-intrinsic: "npm:^1.2.4"
+    get-intrinsic: "npm:^1.2.6"
     globalthis: "npm:^1.0.4"
-    gopd: "npm:^1.0.1"
+    gopd: "npm:^1.2.0"
     has-property-descriptors: "npm:^1.0.2"
-    has-proto: "npm:^1.0.3"
-    has-symbols: "npm:^1.0.3"
-    internal-slot: "npm:^1.0.7"
-    iterator.prototype: "npm:^1.1.3"
-    safe-array-concat: "npm:^1.1.2"
-  checksum: 10c0/2bd60580dfeae353f5b80445d2808da745e97eeacdb663a8c4d99a12046873830a06d377e9d5e88fe54eece7c94319a5ce5a01220e24d71394ceca8d3ef621d7
+    has-proto: "npm:^1.2.0"
+    has-symbols: "npm:^1.1.0"
+    internal-slot: "npm:^1.1.0"
+    iterator.prototype: "npm:^1.1.4"
+    safe-array-concat: "npm:^1.1.3"
+  checksum: 10c0/97e3125ca472d82d8aceea11b790397648b52c26d8768ea1c1ee6309ef45a8755bb63225a43f3150c7591cffc17caf5752459f1e70d583b4184370a8f04ebd2f
   languageName: node
   linkType: hard
 
@@ -3064,6 +3312,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"es-set-tostringtag@npm:^2.1.0":
+  version: 2.1.0
+  resolution: "es-set-tostringtag@npm:2.1.0"
+  dependencies:
+    es-errors: "npm:^1.3.0"
+    get-intrinsic: "npm:^1.2.6"
+    has-tostringtag: "npm:^1.0.2"
+    hasown: "npm:^2.0.2"
+  checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af
+  languageName: node
+  linkType: hard
+
 "es-shim-unscopables@npm:^1.0.0, es-shim-unscopables@npm:^1.0.2":
   version: 1.0.2
   resolution: "es-shim-unscopables@npm:1.0.2"
@@ -3073,7 +3333,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"es-to-primitive@npm:^1.2.1":
+"es-to-primitive@npm:^1.2.1, es-to-primitive@npm:^1.3.0":
   version: 1.3.0
   resolution: "es-to-primitive@npm:1.3.0"
   dependencies:
@@ -3123,59 +3383,60 @@ __metadata:
   languageName: node
   linkType: hard
 
-"eslint-config-next@npm:14.1.0":
-  version: 14.1.0
-  resolution: "eslint-config-next@npm:14.1.0"
+"eslint-config-next@npm:^15":
+  version: 15.1.7
+  resolution: "eslint-config-next@npm:15.1.7"
   dependencies:
-    "@next/eslint-plugin-next": "npm:14.1.0"
-    "@rushstack/eslint-patch": "npm:^1.3.3"
-    "@typescript-eslint/parser": "npm:^5.4.2 || ^6.0.0"
+    "@next/eslint-plugin-next": "npm:15.1.7"
+    "@rushstack/eslint-patch": "npm:^1.10.3"
+    "@typescript-eslint/eslint-plugin": "npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+    "@typescript-eslint/parser": "npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0"
     eslint-import-resolver-node: "npm:^0.3.6"
     eslint-import-resolver-typescript: "npm:^3.5.2"
-    eslint-plugin-import: "npm:^2.28.1"
-    eslint-plugin-jsx-a11y: "npm:^6.7.1"
-    eslint-plugin-react: "npm:^7.33.2"
-    eslint-plugin-react-hooks: "npm:^4.5.0 || 5.0.0-canary-7118f5dd7-20230705"
+    eslint-plugin-import: "npm:^2.31.0"
+    eslint-plugin-jsx-a11y: "npm:^6.10.0"
+    eslint-plugin-react: "npm:^7.37.0"
+    eslint-plugin-react-hooks: "npm:^5.0.0"
   peerDependencies:
-    eslint: ^7.23.0 || ^8.0.0
+    eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
     typescript: ">=3.3.1"
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10c0/8e3fc5fb99d8d8d03651a44487bd7739fd880cf6698292f548ebdb3886c52b3030758018970401f6220430e7d003a1100a62de47f86f7216d3c86ba0e1cd9cd1
+  checksum: 10c0/de949da02e58962eb215fc98ec56cce0a6afeaada550c7213a99466bbb391b58c2d9412d55e9d2756a7b12b7cde60b7d8157337d6dd3f74f131e41404deb0091
   languageName: node
   linkType: hard
 
-"eslint-config-prettier@npm:^8.8.0":
-  version: 8.10.0
-  resolution: "eslint-config-prettier@npm:8.10.0"
+"eslint-config-prettier@npm:^9.1.0":
+  version: 9.1.0
+  resolution: "eslint-config-prettier@npm:9.1.0"
   peerDependencies:
     eslint: ">=7.0.0"
   bin:
     eslint-config-prettier: bin/cli.js
-  checksum: 10c0/19f8c497d9bdc111a17a61b25ded97217be3755bbc4714477dfe535ed539dddcaf42ef5cf8bb97908b058260cf89a3d7c565cb0be31096cbcd39f4c2fa5fe43c
+  checksum: 10c0/6d332694b36bc9ac6fdb18d3ca2f6ac42afa2ad61f0493e89226950a7091e38981b66bac2b47ba39d15b73fff2cd32c78b850a9cf9eed9ca9a96bfb2f3a2f10d
   languageName: node
   linkType: hard
 
-"eslint-config-universe@npm:^12.0.0":
-  version: 12.1.0
-  resolution: "eslint-config-universe@npm:12.1.0"
+"eslint-config-universe@npm:^14":
+  version: 14.0.0
+  resolution: "eslint-config-universe@npm:14.0.0"
   dependencies:
-    "@typescript-eslint/eslint-plugin": "npm:^6.0.0"
-    "@typescript-eslint/parser": "npm:^6.0.0"
-    eslint-config-prettier: "npm:^8.8.0"
-    eslint-plugin-import: "npm:^2.27.5"
+    "@typescript-eslint/eslint-plugin": "npm:^8.9.0"
+    "@typescript-eslint/parser": "npm:^8.9.0"
+    eslint-config-prettier: "npm:^9.1.0"
+    eslint-plugin-import: "npm:^2.30.0"
     eslint-plugin-node: "npm:^11.1.0"
-    eslint-plugin-prettier: "npm:^5.0.0"
-    eslint-plugin-react: "npm:^7.32.2"
-    eslint-plugin-react-hooks: "npm:^4.6.0"
+    eslint-plugin-prettier: "npm:^5.2.1"
+    eslint-plugin-react: "npm:^7.36.1"
+    eslint-plugin-react-hooks: "npm:^4.6.2"
   peerDependencies:
-    eslint: ">=8.10"
+    eslint: ">=8.10 <9"
     prettier: ">=3"
   peerDependenciesMeta:
     prettier:
       optional: true
-  checksum: 10c0/453f1121408b29c93e9da3182185e7d0970e2dcbb6b61b328c12f15cc571ce06e13ac888d1a2bf2765b3a0e6dd20d4e357548452a8f7010f70cb501730848ba7
+  checksum: 10c0/a8620c20d4a9ff924823d443032f8e965fbd55af53dc025cc2ee6f2e9953c24fb3426e1792b0e6220d672d2f3aa2026995712399ecc74dac2326830310d388b4
   languageName: node
   linkType: hard
 
@@ -3239,7 +3500,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"eslint-plugin-i18next@npm:^6.0.3":
+"eslint-plugin-i18next@npm:^6":
   version: 6.1.1
   resolution: "eslint-plugin-i18next@npm:6.1.1"
   dependencies:
@@ -3249,7 +3510,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"eslint-plugin-import@npm:^2.27.5, eslint-plugin-import@npm:^2.28.1":
+"eslint-plugin-import@npm:^2.30.0, eslint-plugin-import@npm:^2.31.0":
   version: 2.31.0
   resolution: "eslint-plugin-import@npm:2.31.0"
   dependencies:
@@ -3278,7 +3539,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"eslint-plugin-jsx-a11y@npm:^6.7.1":
+"eslint-plugin-jsx-a11y@npm:^6.10.0":
   version: 6.10.2
   resolution: "eslint-plugin-jsx-a11y@npm:6.10.2"
   dependencies:
@@ -3319,9 +3580,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"eslint-plugin-prettier@npm:^5.0.0":
-  version: 5.2.1
-  resolution: "eslint-plugin-prettier@npm:5.2.1"
+"eslint-plugin-prettier@npm:^5.2.1":
+  version: 5.2.3
+  resolution: "eslint-plugin-prettier@npm:5.2.3"
   dependencies:
     prettier-linter-helpers: "npm:^1.0.0"
     synckit: "npm:^0.9.1"
@@ -3335,75 +3596,66 @@ __metadata:
       optional: true
     eslint-config-prettier:
       optional: true
-  checksum: 10c0/4bc8bbaf5bb556c9c501dcdff369137763c49ccaf544f9fa91400360ed5e3a3f1234ab59690e06beca5b1b7e6f6356978cdd3b02af6aba3edea2ffe69ca6e8b2
+  checksum: 10c0/60d9c03491ec6080ac1d71d0bee1361539ff6beb9b91ac98cfa7176c9ed52b7dbe7119ebee5b441b479d447d17d802a4a492ee06095ef2f22c460e3dd6459302
   languageName: node
   linkType: hard
 
-"eslint-plugin-react-hooks@npm:^4.5.0 || 5.0.0-canary-7118f5dd7-20230705":
-  version: 5.0.0-canary-7118f5dd7-20230705
-  resolution: "eslint-plugin-react-hooks@npm:5.0.0-canary-7118f5dd7-20230705"
+"eslint-plugin-react-hooks@npm:^4.6.2":
+  version: 4.6.2
+  resolution: "eslint-plugin-react-hooks@npm:4.6.2"
   peerDependencies:
     eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
-  checksum: 10c0/554c4e426bfeb126155510dcba8345391426af147ee629f1c56c9ef6af08340d11008213e4e15b0138830af2c4439d7158da2091987f7efb01aeab662c44b274
+  checksum: 10c0/4844e58c929bc05157fb70ba1e462e34f1f4abcbc8dd5bbe5b04513d33e2699effb8bca668297976ceea8e7ebee4e8fc29b9af9d131bcef52886feaa2308b2cc
   languageName: node
   linkType: hard
 
-"eslint-plugin-react-hooks@npm:^4.6.0":
-  version: 4.6.2
-  resolution: "eslint-plugin-react-hooks@npm:4.6.2"
+"eslint-plugin-react-hooks@npm:^5.0.0":
+  version: 5.1.0
+  resolution: "eslint-plugin-react-hooks@npm:5.1.0"
   peerDependencies:
-    eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
-  checksum: 10c0/4844e58c929bc05157fb70ba1e462e34f1f4abcbc8dd5bbe5b04513d33e2699effb8bca668297976ceea8e7ebee4e8fc29b9af9d131bcef52886feaa2308b2cc
+    eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+  checksum: 10c0/37ef76e1d916d46ab8e93a596078efcf2162e2c653614437e0c54e31d02a5dadabec22802fab717effe257aeb4bdc20c2a710666a89ab1cf07e01e614dde75d8
   languageName: node
   linkType: hard
 
-"eslint-plugin-react@npm:^7.32.2, eslint-plugin-react@npm:^7.33.2":
-  version: 7.37.2
-  resolution: "eslint-plugin-react@npm:7.37.2"
+"eslint-plugin-react@npm:^7.36.1, eslint-plugin-react@npm:^7.37.0":
+  version: 7.37.4
+  resolution: "eslint-plugin-react@npm:7.37.4"
   dependencies:
     array-includes: "npm:^3.1.8"
     array.prototype.findlast: "npm:^1.2.5"
-    array.prototype.flatmap: "npm:^1.3.2"
+    array.prototype.flatmap: "npm:^1.3.3"
     array.prototype.tosorted: "npm:^1.1.4"
     doctrine: "npm:^2.1.0"
-    es-iterator-helpers: "npm:^1.1.0"
+    es-iterator-helpers: "npm:^1.2.1"
     estraverse: "npm:^5.3.0"
     hasown: "npm:^2.0.2"
     jsx-ast-utils: "npm:^2.4.1 || ^3.0.0"
     minimatch: "npm:^3.1.2"
     object.entries: "npm:^1.1.8"
     object.fromentries: "npm:^2.0.8"
-    object.values: "npm:^1.2.0"
+    object.values: "npm:^1.2.1"
     prop-types: "npm:^15.8.1"
     resolve: "npm:^2.0.0-next.5"
     semver: "npm:^6.3.1"
-    string.prototype.matchall: "npm:^4.0.11"
+    string.prototype.matchall: "npm:^4.0.12"
     string.prototype.repeat: "npm:^1.0.0"
   peerDependencies:
     eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
-  checksum: 10c0/01c498f263c201698bf653973760f86a07fa0cdec56c044f3eaa5ddaae71c64326015dfa5fde76ca8c5386ffe789fc79932624b614e13b6a1ad789fee3f7c491
+  checksum: 10c0/4acbbdb19669dfa9a162ed8847c3ad1918f6aea1ceb675ee320b5d903b4e463fdef25e15233295b6d0a726fef2ea8b015c527da769c7690932ddc52d5b82ba12
   languageName: node
   linkType: hard
 
-"eslint-plugin-unused-imports@npm:^3.1.0":
-  version: 3.2.0
-  resolution: "eslint-plugin-unused-imports@npm:3.2.0"
-  dependencies:
-    eslint-rule-composer: "npm:^0.3.0"
+"eslint-plugin-unused-imports@npm:^4":
+  version: 4.1.4
+  resolution: "eslint-plugin-unused-imports@npm:4.1.4"
   peerDependencies:
-    "@typescript-eslint/eslint-plugin": 6 - 7
-    eslint: 8
+    "@typescript-eslint/eslint-plugin": ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0
+    eslint: ^9.0.0 || ^8.0.0
   peerDependenciesMeta:
     "@typescript-eslint/eslint-plugin":
       optional: true
-  checksum: 10c0/70c93efaa4dccd1172db3858b27968184c97cb8b7ffb2d9e6ffb09d9509863c70651b533b48eec4d10bc7f633d7f50fd190fdd5b36e8cac2c4efd5cecb5d5d98
-  languageName: node
-  linkType: hard
-
-"eslint-rule-composer@npm:^0.3.0":
-  version: 0.3.0
-  resolution: "eslint-rule-composer@npm:0.3.0"
-  checksum: 10c0/1f0c40d209e1503a955101a0dbba37e7fc67c8aaa47a5b9ae0b0fcbae7022c86e52b3df2b1b9ffd658e16cd80f31fff92e7222460a44d8251e61d49e0af79a07
+  checksum: 10c0/3899f64b0e8b23fa6b81e2754fc10f93d8741e051d70390a8100ca39af7878bde8625f234b76111af69562ef2512104b52c3703e986ccb3ac9adc07911896acf
   languageName: node
   linkType: hard
 
@@ -3440,6 +3692,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"eslint-visitor-keys@npm:^4.2.0":
+  version: 4.2.0
+  resolution: "eslint-visitor-keys@npm:4.2.0"
+  checksum: 10c0/2ed81c663b147ca6f578312919483eb040295bbab759e5a371953456c636c5b49a559883e2677112453728d66293c0a4c90ab11cab3428cf02a0236d2e738269
+  languageName: node
+  linkType: hard
+
 "eslint@npm:^8":
   version: 8.57.1
   resolution: "eslint@npm:8.57.1"
@@ -3599,7 +3858,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2":
+"fast-glob@npm:3.3.1":
+  version: 3.3.1
+  resolution: "fast-glob@npm:3.3.1"
+  dependencies:
+    "@nodelib/fs.stat": "npm:^2.0.2"
+    "@nodelib/fs.walk": "npm:^1.2.3"
+    glob-parent: "npm:^5.1.2"
+    merge2: "npm:^1.3.0"
+    micromatch: "npm:^4.0.4"
+  checksum: 10c0/b68431128fb6ce4b804c5f9622628426d990b66c75b21c0d16e3d80e2d1398bf33f7e1724e66a2e3f299285dcf5b8d745b122d0304e7dd66f5231081f33ec67c
+  languageName: node
+  linkType: hard
+
+"fast-glob@npm:^3.3.2":
   version: 3.3.2
   resolution: "fast-glob@npm:3.3.2"
   dependencies:
@@ -3798,6 +4070,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"function.prototype.name@npm:^1.1.8":
+  version: 1.1.8
+  resolution: "function.prototype.name@npm:1.1.8"
+  dependencies:
+    call-bind: "npm:^1.0.8"
+    call-bound: "npm:^1.0.3"
+    define-properties: "npm:^1.2.1"
+    functions-have-names: "npm:^1.2.3"
+    hasown: "npm:^2.0.2"
+    is-callable: "npm:^1.2.7"
+  checksum: 10c0/e920a2ab52663005f3cbe7ee3373e3c71c1fb5558b0b0548648cdf3e51961085032458e26c71ff1a8c8c20e7ee7caeb03d43a5d1fa8610c459333323a2e71253
+  languageName: node
+  linkType: hard
+
 "functions-have-names@npm:^1.2.3":
   version: 1.2.3
   resolution: "functions-have-names@npm:1.2.3"
@@ -3819,7 +4105,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6":
+"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4":
   version: 1.2.6
   resolution: "get-intrinsic@npm:1.2.6"
   dependencies:
@@ -3837,6 +4123,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7":
+  version: 1.2.7
+  resolution: "get-intrinsic@npm:1.2.7"
+  dependencies:
+    call-bind-apply-helpers: "npm:^1.0.1"
+    es-define-property: "npm:^1.0.1"
+    es-errors: "npm:^1.3.0"
+    es-object-atoms: "npm:^1.0.0"
+    function-bind: "npm:^1.1.2"
+    get-proto: "npm:^1.0.0"
+    gopd: "npm:^1.2.0"
+    has-symbols: "npm:^1.1.0"
+    hasown: "npm:^2.0.2"
+    math-intrinsics: "npm:^1.1.0"
+  checksum: 10c0/b475dec9f8bff6f7422f51ff4b7b8d0b68e6776ee83a753c1d627e3008c3442090992788038b37eff72e93e43dceed8c1acbdf2d6751672687ec22127933080d
+  languageName: node
+  linkType: hard
+
 "get-package-type@npm:^0.1.0":
   version: 0.1.0
   resolution: "get-package-type@npm:0.1.0"
@@ -3844,6 +4148,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"get-proto@npm:^1.0.0, get-proto@npm:^1.0.1":
+  version: 1.0.1
+  resolution: "get-proto@npm:1.0.1"
+  dependencies:
+    dunder-proto: "npm:^1.0.1"
+    es-object-atoms: "npm:^1.0.0"
+  checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c
+  languageName: node
+  linkType: hard
+
 "get-stream@npm:^6.0.0":
   version: 6.0.1
   resolution: "get-stream@npm:6.0.1"
@@ -3862,6 +4176,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"get-symbol-description@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "get-symbol-description@npm:1.1.0"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    es-errors: "npm:^1.3.0"
+    get-intrinsic: "npm:^1.2.6"
+  checksum: 10c0/d6a7d6afca375779a4b307738c9e80dbf7afc0bdbe5948768d54ab9653c865523d8920e670991a925936eb524b7cb6a6361d199a760b21d0ca7620194455aa4b
+  languageName: node
+  linkType: hard
+
 "get-tsconfig@npm:^4.7.5":
   version: 4.8.1
   resolution: "get-tsconfig@npm:4.8.1"
@@ -3889,21 +4214,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"glob@npm:10.3.10":
-  version: 10.3.10
-  resolution: "glob@npm:10.3.10"
-  dependencies:
-    foreground-child: "npm:^3.1.0"
-    jackspeak: "npm:^2.3.5"
-    minimatch: "npm:^9.0.1"
-    minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0"
-    path-scurry: "npm:^1.10.1"
-  bin:
-    glob: dist/esm/bin.mjs
-  checksum: 10c0/13d8a1feb7eac7945f8c8480e11cd4a44b24d26503d99a8d8ac8d5aefbf3e9802a2b6087318a829fad04cb4e829f25c5f4f1110c68966c498720dd261c7e344d
-  languageName: node
-  linkType: hard
-
 "glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7":
   version: 10.4.5
   resolution: "glob@npm:10.4.5"
@@ -3960,20 +4270,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"globby@npm:^11.1.0":
-  version: 11.1.0
-  resolution: "globby@npm:11.1.0"
-  dependencies:
-    array-union: "npm:^2.1.0"
-    dir-glob: "npm:^3.0.1"
-    fast-glob: "npm:^3.2.9"
-    ignore: "npm:^5.2.0"
-    merge2: "npm:^1.4.1"
-    slash: "npm:^3.0.0"
-  checksum: 10c0/b39511b4afe4bd8a7aead3a27c4ade2b9968649abab0a6c28b1a90141b96ca68ca5db1302f7c7bd29eab66bf51e13916b8e0a3d0ac08f75e1e84a39b35691189
-  languageName: node
-  linkType: hard
-
 "gopd@npm:^1.0.1, gopd@npm:^1.2.0":
   version: 1.2.0
   resolution: "gopd@npm:1.2.0"
@@ -3981,7 +4277,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9":
+"graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9":
   version: 4.2.11
   resolution: "graceful-fs@npm:4.2.11"
   checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2
@@ -4018,7 +4314,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"has-proto@npm:^1.0.3":
+"has-proto@npm:^1.0.3, has-proto@npm:^1.2.0":
   version: 1.2.0
   resolution: "has-proto@npm:1.2.0"
   dependencies:
@@ -4123,7 +4419,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"husky@npm:^9.0.11":
+"husky@npm:^9":
   version: 9.1.7
   resolution: "husky@npm:9.1.7"
   bin:
@@ -4141,7 +4437,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4":
+"ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.3.1":
   version: 5.3.2
   resolution: "ignore@npm:5.3.2"
   checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337
@@ -4212,6 +4508,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"internal-slot@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "internal-slot@npm:1.1.0"
+  dependencies:
+    es-errors: "npm:^1.3.0"
+    hasown: "npm:^2.0.2"
+    side-channel: "npm:^1.1.0"
+  checksum: 10c0/03966f5e259b009a9bf1a78d60da920df198af4318ec004f57b8aef1dd3fe377fbc8cce63a96e8c810010302654de89f9e19de1cd8ad0061d15be28a695465c7
+  languageName: node
+  linkType: hard
+
 "intl-messageformat@npm:^10.5.14":
   version: 10.5.14
   resolution: "intl-messageformat@npm:10.5.14"
@@ -4254,6 +4561,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-array-buffer@npm:^3.0.5":
+  version: 3.0.5
+  resolution: "is-array-buffer@npm:3.0.5"
+  dependencies:
+    call-bind: "npm:^1.0.8"
+    call-bound: "npm:^1.0.3"
+    get-intrinsic: "npm:^1.2.6"
+  checksum: 10c0/c5c9f25606e86dbb12e756694afbbff64bc8b348d1bc989324c037e1068695131930199d6ad381952715dad3a9569333817f0b1a72ce5af7f883ce802e49c83d
+  languageName: node
+  linkType: hard
+
 "is-arrayish@npm:^0.2.1":
   version: 0.2.1
   resolution: "is-arrayish@npm:0.2.1"
@@ -4305,6 +4623,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-boolean-object@npm:^1.2.1":
+  version: 1.2.2
+  resolution: "is-boolean-object@npm:1.2.2"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    has-tostringtag: "npm:^1.0.2"
+  checksum: 10c0/36ff6baf6bd18b3130186990026f5a95c709345c39cd368468e6c1b6ab52201e9fd26d8e1f4c066357b4938b0f0401e1a5000e08257787c1a02f3a719457001e
+  languageName: node
+  linkType: hard
+
 "is-bun-module@npm:^1.0.2":
   version: 1.3.0
   resolution: "is-bun-module@npm:1.3.0"
@@ -4330,7 +4658,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"is-data-view@npm:^1.0.1":
+"is-data-view@npm:^1.0.1, is-data-view@npm:^1.0.2":
   version: 1.0.2
   resolution: "is-data-view@npm:1.0.2"
   dependencies:
@@ -4423,6 +4751,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-number-object@npm:^1.1.1":
+  version: 1.1.1
+  resolution: "is-number-object@npm:1.1.1"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    has-tostringtag: "npm:^1.0.2"
+  checksum: 10c0/97b451b41f25135ff021d85c436ff0100d84a039bb87ffd799cbcdbea81ef30c464ced38258cdd34f080be08fc3b076ca1f472086286d2aa43521d6ec6a79f53
+  languageName: node
+  linkType: hard
+
 "is-number@npm:^7.0.0":
   version: 7.0.0
   resolution: "is-number@npm:7.0.0"
@@ -4472,6 +4810,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-shared-array-buffer@npm:^1.0.4":
+  version: 1.0.4
+  resolution: "is-shared-array-buffer@npm:1.0.4"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+  checksum: 10c0/65158c2feb41ff1edd6bbd6fd8403a69861cf273ff36077982b5d4d68e1d59278c71691216a4a64632bd76d4792d4d1d2553901b6666d84ade13bba5ea7bc7db
+  languageName: node
+  linkType: hard
+
 "is-stream@npm:^2.0.0":
   version: 2.0.1
   resolution: "is-stream@npm:2.0.1"
@@ -4489,7 +4836,17 @@ __metadata:
   languageName: node
   linkType: hard
 
-"is-symbol@npm:^1.0.4, is-symbol@npm:^1.1.0":
+"is-string@npm:^1.1.1":
+  version: 1.1.1
+  resolution: "is-string@npm:1.1.1"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    has-tostringtag: "npm:^1.0.2"
+  checksum: 10c0/2f518b4e47886bb81567faba6ffd0d8a8333cf84336e2e78bf160693972e32ad00fe84b0926491cc598dee576fdc55642c92e62d0cbe96bf36f643b6f956f94d
+  languageName: node
+  linkType: hard
+
+"is-symbol@npm:^1.0.4, is-symbol@npm:^1.1.0, is-symbol@npm:^1.1.1":
   version: 1.1.1
   resolution: "is-symbol@npm:1.1.1"
   dependencies:
@@ -4509,6 +4866,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-typed-array@npm:^1.1.14, is-typed-array@npm:^1.1.15":
+  version: 1.1.15
+  resolution: "is-typed-array@npm:1.1.15"
+  dependencies:
+    which-typed-array: "npm:^1.1.16"
+  checksum: 10c0/415511da3669e36e002820584e264997ffe277ff136643a3126cc949197e6ca3334d0f12d084e83b1994af2e9c8141275c741cf2b7da5a2ff62dd0cac26f76c4
+  languageName: node
+  linkType: hard
+
 "is-weakmap@npm:^2.0.2":
   version: 2.0.2
   resolution: "is-weakmap@npm:2.0.2"
@@ -4525,6 +4891,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-weakref@npm:^1.1.0":
+  version: 1.1.1
+  resolution: "is-weakref@npm:1.1.1"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+  checksum: 10c0/8e0a9c07b0c780949a100e2cab2b5560a48ecd4c61726923c1a9b77b6ab0aa0046c9e7fb2206042296817045376dee2c8ab1dabe08c7c3dfbf195b01275a085b
+  languageName: node
+  linkType: hard
+
 "is-weakset@npm:^2.0.3":
   version: 2.0.3
   resolution: "is-weakset@npm:2.0.3"
@@ -4631,30 +5006,17 @@ __metadata:
   languageName: node
   linkType: hard
 
-"iterator.prototype@npm:^1.1.3":
-  version: 1.1.4
-  resolution: "iterator.prototype@npm:1.1.4"
+"iterator.prototype@npm:^1.1.4":
+  version: 1.1.5
+  resolution: "iterator.prototype@npm:1.1.5"
   dependencies:
     define-data-property: "npm:^1.1.4"
     es-object-atoms: "npm:^1.0.0"
     get-intrinsic: "npm:^1.2.6"
+    get-proto: "npm:^1.0.0"
     has-symbols: "npm:^1.1.0"
-    reflect.getprototypeof: "npm:^1.0.8"
     set-function-name: "npm:^2.0.2"
-  checksum: 10c0/e63fcb5c1094192f43795b836fae9149a7dc2d445425958045e8e193df428407f909efca21bfdf0d885668ae8204681984afac7dd75478118e62f3cd3959c538
-  languageName: node
-  linkType: hard
-
-"jackspeak@npm:^2.3.5":
-  version: 2.3.6
-  resolution: "jackspeak@npm:2.3.6"
-  dependencies:
-    "@isaacs/cliui": "npm:^8.0.2"
-    "@pkgjs/parseargs": "npm:^0.11.0"
-  dependenciesMeta:
-    "@pkgjs/parseargs":
-      optional: true
-  checksum: 10c0/f01d8f972d894cd7638bc338e9ef5ddb86f7b208ce177a36d718eac96ec86638a6efa17d0221b10073e64b45edc2ce15340db9380b1f5d5c5d000cbc517dc111
+  checksum: 10c0/f7a262808e1b41049ab55f1e9c29af7ec1025a000d243b83edf34ce2416eedd56079b117fa59376bb4a724110690f13aa8427f2ee29a09eec63a7e72367626d0
   languageName: node
   linkType: hard
 
@@ -5140,21 +5502,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jotai@npm:^2.10.3":
-  version: 2.10.3
-  resolution: "jotai@npm:2.10.3"
-  peerDependencies:
-    "@types/react": ">=17.0.0"
-    react: ">=17.0.0"
-  peerDependenciesMeta:
-    "@types/react":
-      optional: true
-    react:
-      optional: true
-  checksum: 10c0/64f6536aaa91f77dacd8d9714fb846f254bfed6e5354b3005375433d72844f3ae1d6d893ee4dd423d5bddd109d393dd338e562da914605b31190989a3d47db35
-  languageName: node
-  linkType: hard
-
 "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
   version: 4.0.0
   resolution: "js-tokens@npm:4.0.0"
@@ -5489,6 +5836,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"math-intrinsics@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "math-intrinsics@npm:1.1.0"
+  checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f
+  languageName: node
+  linkType: hard
+
 "media-typer@npm:0.3.0":
   version: 0.3.0
   resolution: "media-typer@npm:0.3.0"
@@ -5510,7 +5864,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"merge2@npm:^1.3.0, merge2@npm:^1.4.1":
+"merge2@npm:^1.3.0":
   version: 1.4.1
   resolution: "merge2@npm:1.4.1"
   checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb
@@ -5573,15 +5927,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"minimatch@npm:9.0.3":
-  version: 9.0.3
-  resolution: "minimatch@npm:9.0.3"
-  dependencies:
-    brace-expansion: "npm:^2.0.1"
-  checksum: 10c0/85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac
-  languageName: node
-  linkType: hard
-
 "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
   version: 3.1.2
   resolution: "minimatch@npm:3.1.2"
@@ -5591,7 +5936,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"minimatch@npm:^9.0.1, minimatch@npm:^9.0.4":
+"minimatch@npm:^9.0.4":
   version: 9.0.5
   resolution: "minimatch@npm:9.0.5"
   dependencies:
@@ -5741,44 +6086,46 @@ __metadata:
   languageName: node
   linkType: hard
 
-"next-intl@npm:^3.19.1":
-  version: 3.26.1
-  resolution: "next-intl@npm:3.26.1"
+"next-intl@npm:^3":
+  version: 3.26.4
+  resolution: "next-intl@npm:3.26.4"
   dependencies:
     "@formatjs/intl-localematcher": "npm:^0.5.4"
     negotiator: "npm:^1.0.0"
-    use-intl: "npm:^3.26.1"
+    use-intl: "npm:^3.26.4"
   peerDependencies:
     next: ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0
     react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
-  checksum: 10c0/dfb67e132d62feda76be6d84ed8100edcbad2599b1cc355a2f854cdd5ab6722d767585f7dce79c759aca27dadef5b9a3273dedff6bb12d397409cfd15cc5cd78
+  checksum: 10c0/89c59aa8014788f097ddf6359d669f0036be57e1783369db7aaa55df554093701d059affd1fa38f81d4bafc483d64273991d13885b21ab04c5c9c13253d33fe6
   languageName: node
   linkType: hard
 
-"next@npm:14.1.0":
-  version: 14.1.0
-  resolution: "next@npm:14.1.0"
+"next@npm:^15":
+  version: 15.1.7
+  resolution: "next@npm:15.1.7"
   dependencies:
-    "@next/env": "npm:14.1.0"
-    "@next/swc-darwin-arm64": "npm:14.1.0"
-    "@next/swc-darwin-x64": "npm:14.1.0"
-    "@next/swc-linux-arm64-gnu": "npm:14.1.0"
-    "@next/swc-linux-arm64-musl": "npm:14.1.0"
-    "@next/swc-linux-x64-gnu": "npm:14.1.0"
-    "@next/swc-linux-x64-musl": "npm:14.1.0"
-    "@next/swc-win32-arm64-msvc": "npm:14.1.0"
-    "@next/swc-win32-ia32-msvc": "npm:14.1.0"
-    "@next/swc-win32-x64-msvc": "npm:14.1.0"
-    "@swc/helpers": "npm:0.5.2"
+    "@next/env": "npm:15.1.7"
+    "@next/swc-darwin-arm64": "npm:15.1.7"
+    "@next/swc-darwin-x64": "npm:15.1.7"
+    "@next/swc-linux-arm64-gnu": "npm:15.1.7"
+    "@next/swc-linux-arm64-musl": "npm:15.1.7"
+    "@next/swc-linux-x64-gnu": "npm:15.1.7"
+    "@next/swc-linux-x64-musl": "npm:15.1.7"
+    "@next/swc-win32-arm64-msvc": "npm:15.1.7"
+    "@next/swc-win32-x64-msvc": "npm:15.1.7"
+    "@swc/counter": "npm:0.1.3"
+    "@swc/helpers": "npm:0.5.15"
     busboy: "npm:1.6.0"
     caniuse-lite: "npm:^1.0.30001579"
-    graceful-fs: "npm:^4.2.11"
     postcss: "npm:8.4.31"
-    styled-jsx: "npm:5.1.1"
+    sharp: "npm:^0.33.5"
+    styled-jsx: "npm:5.1.6"
   peerDependencies:
     "@opentelemetry/api": ^1.1.0
-    react: ^18.2.0
-    react-dom: ^18.2.0
+    "@playwright/test": ^1.41.2
+    babel-plugin-react-compiler: "*"
+    react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+    react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
     sass: ^1.3.0
   dependenciesMeta:
     "@next/swc-darwin-arm64":
@@ -5795,18 +6142,22 @@ __metadata:
       optional: true
     "@next/swc-win32-arm64-msvc":
       optional: true
-    "@next/swc-win32-ia32-msvc":
-      optional: true
     "@next/swc-win32-x64-msvc":
       optional: true
+    sharp:
+      optional: true
   peerDependenciesMeta:
     "@opentelemetry/api":
       optional: true
+    "@playwright/test":
+      optional: true
+    babel-plugin-react-compiler:
+      optional: true
     sass:
       optional: true
   bin:
     next: dist/bin/next
-  checksum: 10c0/dbb1ef8d22eec29a9127d28ed46eb34f14e3f7f7b4e4b91dc96027feb4d9ead554a804275484d9a54026e6e55d632d3997561e598c1fb8e8956e77614f39765f
+  checksum: 10c0/9d0f26c3742fb4339b931124607f267558357f2a9cd1cde4ea7d5755cea56a2f751b5898e1babd686ae97ee1f6043c94177f1dcc9c69db50b61d27e441970dfe
   languageName: node
   linkType: hard
 
@@ -5975,6 +6326,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"object.assign@npm:^4.1.7":
+  version: 4.1.7
+  resolution: "object.assign@npm:4.1.7"
+  dependencies:
+    call-bind: "npm:^1.0.8"
+    call-bound: "npm:^1.0.3"
+    define-properties: "npm:^1.2.1"
+    es-object-atoms: "npm:^1.0.0"
+    has-symbols: "npm:^1.1.0"
+    object-keys: "npm:^1.1.1"
+  checksum: 10c0/3b2732bd860567ea2579d1567525168de925a8d852638612846bd8082b3a1602b7b89b67b09913cbb5b9bd6e95923b2ae73580baa9d99cb4e990564e8cbf5ddc
+  languageName: node
+  linkType: hard
+
 "object.entries@npm:^1.1.8":
   version: 1.1.8
   resolution: "object.entries@npm:1.1.8"
@@ -6020,6 +6385,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"object.values@npm:^1.2.1":
+  version: 1.2.1
+  resolution: "object.values@npm:1.2.1"
+  dependencies:
+    call-bind: "npm:^1.0.8"
+    call-bound: "npm:^1.0.3"
+    define-properties: "npm:^1.2.1"
+    es-object-atoms: "npm:^1.0.0"
+  checksum: 10c0/3c47814fdc64842ae3d5a74bc9d06bdd8d21563c04d9939bf6716a9c00596a4ebc342552f8934013d1ec991c74e3671b26710a0c51815f0b603795605ab6b2c9
+  languageName: node
+  linkType: hard
+
 "once@npm:^1.3.0":
   version: 1.4.0
   resolution: "once@npm:1.4.0"
@@ -6052,6 +6429,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"own-keys@npm:^1.0.1":
+  version: 1.0.1
+  resolution: "own-keys@npm:1.0.1"
+  dependencies:
+    get-intrinsic: "npm:^1.2.6"
+    object-keys: "npm:^1.1.1"
+    safe-push-apply: "npm:^1.0.0"
+  checksum: 10c0/6dfeb3455bff92ec3f16a982d4e3e65676345f6902d9f5ded1d8265a6318d0200ce461956d6d1c70053c7fe9f9fe65e552faac03f8140d37ef0fdd108e67013a
+  languageName: node
+  linkType: hard
+
 "p-limit@npm:^2.2.0":
   version: 2.3.0
   resolution: "p-limit@npm:2.3.0"
@@ -6174,7 +6562,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"path-scurry@npm:^1.10.1, path-scurry@npm:^1.11.1":
+"path-scurry@npm:^1.11.1":
   version: 1.11.1
   resolution: "path-scurry@npm:1.11.1"
   dependencies:
@@ -6184,13 +6572,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"path-type@npm:^4.0.0":
-  version: 4.0.0
-  resolution: "path-type@npm:4.0.0"
-  checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c
-  languageName: node
-  linkType: hard
-
 "picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.0, picocolors@npm:^1.1.1":
   version: 1.1.1
   resolution: "picocolors@npm:1.1.1"
@@ -6305,7 +6686,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"postcss@npm:8.4.31, postcss@npm:^8":
+"postcss@npm:8.4.31":
   version: 8.4.31
   resolution: "postcss@npm:8.4.31"
   dependencies:
@@ -6316,6 +6697,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"postcss@npm:^8":
+  version: 8.4.47
+  resolution: "postcss@npm:8.4.47"
+  dependencies:
+    nanoid: "npm:^3.3.7"
+    picocolors: "npm:^1.1.0"
+    source-map-js: "npm:^1.2.1"
+  checksum: 10c0/929f68b5081b7202709456532cee2a145c1843d391508c5a09de2517e8c4791638f71dd63b1898dba6712f8839d7a6da046c72a5e44c162e908f5911f57b5f44
+  languageName: node
+  linkType: hard
+
 "postcss@npm:^8.4.47":
   version: 8.4.49
   resolution: "postcss@npm:8.4.49"
@@ -6489,14 +6881,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-paginate@npm:^8.2.0":
-  version: 8.2.0
-  resolution: "react-paginate@npm:8.2.0"
+"react-paginate@npm:^8":
+  version: 8.3.0
+  resolution: "react-paginate@npm:8.3.0"
   dependencies:
     prop-types: "npm:^15"
   peerDependencies:
-    react: ^16 || ^17 || ^18
-  checksum: 10c0/3d436f5e56e6b14b809767718da46f22d4315221c972c4990e7c22de3b8074235852fa2ad00f6849aad50fa0df16aa14adc2243e767fe4851f0c1ba154ef8b15
+    react: ^16 || ^17 || ^18 || ^19
+  checksum: 10c0/2529fe1d5235fb50195b78c2edf4ab72c5dda88d38445e68521822cd2ea1e47d490a6af9ac2e4d0507db3d094cded4cdd4b5db2b12e535b2c435b123e9427eb7
   languageName: node
   linkType: hard
 
@@ -6537,19 +6929,19 @@ __metadata:
   languageName: node
   linkType: hard
 
-"reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.8":
-  version: 1.0.8
-  resolution: "reflect.getprototypeof@npm:1.0.8"
+"reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9":
+  version: 1.0.10
+  resolution: "reflect.getprototypeof@npm:1.0.10"
   dependencies:
     call-bind: "npm:^1.0.8"
     define-properties: "npm:^1.2.1"
-    dunder-proto: "npm:^1.0.0"
-    es-abstract: "npm:^1.23.5"
+    es-abstract: "npm:^1.23.9"
     es-errors: "npm:^1.3.0"
-    get-intrinsic: "npm:^1.2.4"
-    gopd: "npm:^1.2.0"
-    which-builtin-type: "npm:^1.2.0"
-  checksum: 10c0/720479dd7a72a20d66efaca507ed7c7e18403d24ce764f436130464d4a516a12ed8a9a2714dcabc3e1296f9a31f914ba1095e2371619df23d3ac56c4f8c8bae1
+    es-object-atoms: "npm:^1.0.0"
+    get-intrinsic: "npm:^1.2.7"
+    get-proto: "npm:^1.0.1"
+    which-builtin-type: "npm:^1.2.1"
+  checksum: 10c0/7facec28c8008876f8ab98e80b7b9cb4b1e9224353fd4756dda5f2a4ab0d30fa0a5074777c6df24e1e0af463a2697513b0a11e548d99cf52f21f7bc6ba48d3ac
   languageName: node
   linkType: hard
 
@@ -6560,7 +6952,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.2, regexp.prototype.flags@npm:^1.5.3":
+"regexp.prototype.flags@npm:^1.5.1":
   version: 1.5.3
   resolution: "regexp.prototype.flags@npm:1.5.3"
   dependencies:
@@ -6572,6 +6964,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"regexp.prototype.flags@npm:^1.5.3":
+  version: 1.5.4
+  resolution: "regexp.prototype.flags@npm:1.5.4"
+  dependencies:
+    call-bind: "npm:^1.0.8"
+    define-properties: "npm:^1.2.1"
+    es-errors: "npm:^1.3.0"
+    get-proto: "npm:^1.0.1"
+    gopd: "npm:^1.2.0"
+    set-function-name: "npm:^2.0.2"
+  checksum: 10c0/83b88e6115b4af1c537f8dabf5c3744032cb875d63bc05c288b1b8c0ef37cbe55353f95d8ca817e8843806e3e150b118bc624e4279b24b4776b4198232735a77
+  languageName: node
+  linkType: hard
+
 "regexpp@npm:^3.0.0":
   version: 3.2.0
   resolution: "regexpp@npm:3.2.0"
@@ -6734,7 +7140,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"safe-array-concat@npm:^1.1.2":
+"safe-array-concat@npm:^1.1.2, safe-array-concat@npm:^1.1.3":
   version: 1.1.3
   resolution: "safe-array-concat@npm:1.1.3"
   dependencies:
@@ -6754,6 +7160,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"safe-push-apply@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "safe-push-apply@npm:1.0.0"
+  dependencies:
+    es-errors: "npm:^1.3.0"
+    isarray: "npm:^2.0.5"
+  checksum: 10c0/831f1c9aae7436429e7862c7e46f847dfe490afac20d0ee61bae06108dbf5c745a0de3568ada30ccdd3eeb0864ca8331b2eef703abd69bfea0745b21fd320750
+  languageName: node
+  linkType: hard
+
 "safe-regex-test@npm:^1.0.3, safe-regex-test@npm:^1.1.0":
   version: 1.1.0
   resolution: "safe-regex-test@npm:1.1.0"
@@ -6808,6 +7224,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"semver@npm:^7.6.0":
+  version: 7.7.1
+  resolution: "semver@npm:7.7.1"
+  bin:
+    semver: bin/semver.js
+  checksum: 10c0/fd603a6fb9c399c6054015433051bdbe7b99a940a8fb44b85c2b524c4004b023d7928d47cb22154f8d054ea7ee8597f586605e05b52047f048278e4ac56ae958
+  languageName: node
+  linkType: hard
+
 "set-function-length@npm:^1.2.2":
   version: 1.2.2
   resolution: "set-function-length@npm:1.2.2"
@@ -6834,7 +7259,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"sharp@npm:^0.33.4":
+"set-proto@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "set-proto@npm:1.0.0"
+  dependencies:
+    dunder-proto: "npm:^1.0.1"
+    es-errors: "npm:^1.3.0"
+    es-object-atoms: "npm:^1.0.0"
+  checksum: 10c0/ca5c3ccbba479d07c30460e367e66337cec825560b11e8ba9c5ebe13a2a0d6021ae34eddf94ff3dfe17a3104dc1f191519cb6c48378b503e5c3f36393938776a
+  languageName: node
+  linkType: hard
+
+"sharp@npm:^0.33, sharp@npm:^0.33.5":
   version: 0.33.5
   resolution: "sharp@npm:0.33.5"
   dependencies:
@@ -6954,7 +7390,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"side-channel@npm:^1.0.4, side-channel@npm:^1.0.6":
+"side-channel@npm:^1.0.4, side-channel@npm:^1.1.0":
   version: 1.1.0
   resolution: "side-channel@npm:1.1.0"
   dependencies:
@@ -7154,23 +7590,24 @@ __metadata:
   languageName: node
   linkType: hard
 
-"string.prototype.matchall@npm:^4.0.11":
-  version: 4.0.11
-  resolution: "string.prototype.matchall@npm:4.0.11"
+"string.prototype.matchall@npm:^4.0.12":
+  version: 4.0.12
+  resolution: "string.prototype.matchall@npm:4.0.12"
   dependencies:
-    call-bind: "npm:^1.0.7"
+    call-bind: "npm:^1.0.8"
+    call-bound: "npm:^1.0.3"
     define-properties: "npm:^1.2.1"
-    es-abstract: "npm:^1.23.2"
+    es-abstract: "npm:^1.23.6"
     es-errors: "npm:^1.3.0"
     es-object-atoms: "npm:^1.0.0"
-    get-intrinsic: "npm:^1.2.4"
-    gopd: "npm:^1.0.1"
-    has-symbols: "npm:^1.0.3"
-    internal-slot: "npm:^1.0.7"
-    regexp.prototype.flags: "npm:^1.5.2"
+    get-intrinsic: "npm:^1.2.6"
+    gopd: "npm:^1.2.0"
+    has-symbols: "npm:^1.1.0"
+    internal-slot: "npm:^1.1.0"
+    regexp.prototype.flags: "npm:^1.5.3"
     set-function-name: "npm:^2.0.2"
-    side-channel: "npm:^1.0.6"
-  checksum: 10c0/915a2562ac9ab5e01b7be6fd8baa0b2b233a0a9aa975fcb2ec13cc26f08fb9a3e85d5abdaa533c99c6fc4c5b65b914eba3d80c4aff9792a4c9fed403f28f7d9d
+    side-channel: "npm:^1.1.0"
+  checksum: 10c0/1a53328ada73f4a77f1fdf1c79414700cf718d0a8ef6672af5603e709d26a24f2181208144aed7e858b1bcc1a0d08567a570abfb45567db4ae47637ed2c2f85c
   languageName: node
   linkType: hard
 
@@ -7184,7 +7621,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"string.prototype.trim@npm:^1.2.9":
+"string.prototype.trim@npm:^1.2.10, string.prototype.trim@npm:^1.2.9":
   version: 1.2.10
   resolution: "string.prototype.trim@npm:1.2.10"
   dependencies:
@@ -7199,7 +7636,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"string.prototype.trimend@npm:^1.0.8":
+"string.prototype.trimend@npm:^1.0.8, string.prototype.trimend@npm:^1.0.9":
   version: 1.0.9
   resolution: "string.prototype.trimend@npm:1.0.9"
   dependencies:
@@ -7277,19 +7714,19 @@ __metadata:
   languageName: node
   linkType: hard
 
-"styled-jsx@npm:5.1.1":
-  version: 5.1.1
-  resolution: "styled-jsx@npm:5.1.1"
+"styled-jsx@npm:5.1.6":
+  version: 5.1.6
+  resolution: "styled-jsx@npm:5.1.6"
   dependencies:
     client-only: "npm:0.0.1"
   peerDependencies:
-    react: ">= 16.8.0 || 17.x.x || ^18.0.0-0"
+    react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
   peerDependenciesMeta:
     "@babel/core":
       optional: true
     babel-plugin-macros:
       optional: true
-  checksum: 10c0/42655cdadfa5388f8a48bb282d6b450df7d7b8cf066ac37038bd0499d3c9f084815ebd9ff9dfa12a218fd4441338851db79603498d7557207009c1cf4d609835
+  checksum: 10c0/ace50e7ea5ae5ae6a3b65a50994c51fca6ae7df9c7ecfd0104c36be0b4b3a9c5c1a2374d16e2a11e256d0b20be6d47256d768ecb4f91ab390f60752a075780f5
   languageName: node
   linkType: hard
 
@@ -7353,6 +7790,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"tabbable@npm:^6.0.0":
+  version: 6.2.0
+  resolution: "tabbable@npm:6.2.0"
+  checksum: 10c0/ced8b38f05f2de62cd46836d77c2646c42b8c9713f5bd265daf0e78ff5ac73d3ba48a7ca45f348bafeef29b23da7187c72250742d37627883ef89cbd7fa76898
+  languageName: node
+  linkType: hard
+
 "tailwindcss@npm:^3.3.0":
   version: 3.4.16
   resolution: "tailwindcss@npm:3.4.16"
@@ -7494,12 +7938,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ts-api-utils@npm:^1.0.1":
-  version: 1.4.3
-  resolution: "ts-api-utils@npm:1.4.3"
+"ts-api-utils@npm:^2.0.1":
+  version: 2.0.1
+  resolution: "ts-api-utils@npm:2.0.1"
   peerDependencies:
-    typescript: ">=4.2.0"
-  checksum: 10c0/e65dc6e7e8141140c23e1dc94984bf995d4f6801919c71d6dc27cf0cd51b100a91ffcfe5217626193e5bea9d46831e8586febdc7e172df3f1091a7384299e23a
+    typescript: ">=4.8.4"
+  checksum: 10c0/23fd56a958b332cac00150a652e4c84730df30571bd2faa1ba6d7b511356d1a61656621492bb6c7f15dd6e18847a1408357a0e406671d358115369a17f5bfedd
   languageName: node
   linkType: hard
 
@@ -7560,13 +8004,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"tslib@npm:2, tslib@npm:^2.4.0, tslib@npm:^2.6.2":
+"tslib@npm:2, tslib@npm:^2.8.0":
   version: 2.8.1
   resolution: "tslib@npm:2.8.1"
   checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
   languageName: node
   linkType: hard
 
+"tslib@npm:^2.4.0, tslib@npm:^2.6.2":
+  version: 2.7.0
+  resolution: "tslib@npm:2.7.0"
+  checksum: 10c0/469e1d5bf1af585742128827000711efa61010b699cb040ab1800bcd3ccdd37f63ec30642c9e07c4439c1db6e46345582614275daca3e0f4abae29b0083f04a6
+  languageName: node
+  linkType: hard
+
 "type-check@npm:^0.4.0, type-check@npm:~0.4.0":
   version: 0.4.0
   resolution: "type-check@npm:0.4.0"
@@ -7618,6 +8069,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"typed-array-buffer@npm:^1.0.3":
+  version: 1.0.3
+  resolution: "typed-array-buffer@npm:1.0.3"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    es-errors: "npm:^1.3.0"
+    is-typed-array: "npm:^1.1.14"
+  checksum: 10c0/1105071756eb248774bc71646bfe45b682efcad93b55532c6ffa4518969fb6241354e4aa62af679ae83899ec296d69ef88f1f3763657cdb3a4d29321f7b83079
+  languageName: node
+  linkType: hard
+
 "typed-array-byte-length@npm:^1.0.1":
   version: 1.0.1
   resolution: "typed-array-byte-length@npm:1.0.1"
@@ -7631,6 +8093,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"typed-array-byte-length@npm:^1.0.3":
+  version: 1.0.3
+  resolution: "typed-array-byte-length@npm:1.0.3"
+  dependencies:
+    call-bind: "npm:^1.0.8"
+    for-each: "npm:^0.3.3"
+    gopd: "npm:^1.2.0"
+    has-proto: "npm:^1.2.0"
+    is-typed-array: "npm:^1.1.14"
+  checksum: 10c0/6ae083c6f0354f1fce18b90b243343b9982affd8d839c57bbd2c174a5d5dc71be9eb7019ffd12628a96a4815e7afa85d718d6f1e758615151d5f35df841ffb3e
+  languageName: node
+  linkType: hard
+
 "typed-array-byte-offset@npm:^1.0.2":
   version: 1.0.3
   resolution: "typed-array-byte-offset@npm:1.0.3"
@@ -7646,7 +8121,22 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typed-array-length@npm:^1.0.6":
+"typed-array-byte-offset@npm:^1.0.4":
+  version: 1.0.4
+  resolution: "typed-array-byte-offset@npm:1.0.4"
+  dependencies:
+    available-typed-arrays: "npm:^1.0.7"
+    call-bind: "npm:^1.0.8"
+    for-each: "npm:^0.3.3"
+    gopd: "npm:^1.2.0"
+    has-proto: "npm:^1.2.0"
+    is-typed-array: "npm:^1.1.15"
+    reflect.getprototypeof: "npm:^1.0.9"
+  checksum: 10c0/3d805b050c0c33b51719ee52de17c1cd8e6a571abdf0fffb110e45e8dd87a657e8b56eee94b776b13006d3d347a0c18a730b903cf05293ab6d92e99ff8f77e53
+  languageName: node
+  linkType: hard
+
+"typed-array-length@npm:^1.0.6, typed-array-length@npm:^1.0.7":
   version: 1.0.7
   resolution: "typed-array-length@npm:1.0.7"
   dependencies:
@@ -7692,6 +8182,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"unbox-primitive@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "unbox-primitive@npm:1.1.0"
+  dependencies:
+    call-bound: "npm:^1.0.3"
+    has-bigints: "npm:^1.0.2"
+    has-symbols: "npm:^1.1.0"
+    which-boxed-primitive: "npm:^1.1.1"
+  checksum: 10c0/7dbd35ab02b0e05fe07136c72cb9355091242455473ec15057c11430129bab38b7b3624019b8778d02a881c13de44d63cd02d122ee782fb519e1de7775b5b982
+  languageName: node
+  linkType: hard
+
 "undici-types@npm:~6.19.2":
   version: 6.19.8
   resolution: "undici-types@npm:6.19.8"
@@ -7703,8 +8205,8 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "unhide-ui@workspace:."
   dependencies:
-    "@headlessui/react": "npm:^1.7.18"
-    "@headlessui/tailwindcss": "npm:^0.1.3"
+    "@headlessui/react": "npm:^2"
+    "@headlessui/tailwindcss": "npm:^0.2.2"
     "@testing-library/jest-dom": "npm:^6.4.2"
     "@testing-library/react": "npm:^14.2.1"
     "@types/isomorphic-fetch": "npm:^0.0.39"
@@ -7715,29 +8217,28 @@ __metadata:
     "@types/test-listen": "npm:^1.1.2"
     autoprefixer: "npm:^10.0.1"
     eslint: "npm:^8"
-    eslint-config-next: "npm:14.1.0"
-    eslint-config-universe: "npm:^12.0.0"
-    eslint-plugin-i18next: "npm:^6.0.3"
-    eslint-plugin-unused-imports: "npm:^3.1.0"
-    husky: "npm:^9.0.11"
+    eslint-config-next: "npm:^15"
+    eslint-config-universe: "npm:^14"
+    eslint-plugin-i18next: "npm:^6"
+    eslint-plugin-unused-imports: "npm:^4"
+    husky: "npm:^9"
     isomorphic-fetch: "npm:^3.0.0"
     jest: "npm:^29.7.0"
     jest-environment-jsdom: "npm:^29.7.0"
-    jotai: "npm:^2.10.3"
-    next: "npm:14.1.0"
-    next-intl: "npm:^3.19.1"
+    next: "npm:^15"
+    next-intl: "npm:^3"
     node-mocks-http: "npm:^1.14.1"
     postcss: "npm:^8"
     prettier: "npm:^3.2.5"
     react: "npm:^18"
     react-dom: "npm:^18"
-    react-paginate: "npm:^8.2.0"
-    sharp: "npm:^0.33.4"
+    react-paginate: "npm:^8"
+    sharp: "npm:^0.33"
     tailwindcss: "npm:^3.3.0"
     test-listen: "npm:^1.1.0"
     ts-node: "npm:^10.9.2"
     typescript: "npm:^5"
-    zod: "npm:^3.22.4"
+    zod: "npm:^3"
   languageName: unknown
   linkType: soft
 
@@ -7799,15 +8300,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"use-intl@npm:^3.26.1":
-  version: 3.26.1
-  resolution: "use-intl@npm:3.26.1"
+"use-intl@npm:^3.26.4":
+  version: 3.26.4
+  resolution: "use-intl@npm:3.26.4"
   dependencies:
     "@formatjs/fast-memoize": "npm:^2.2.0"
     intl-messageformat: "npm:^10.5.14"
   peerDependencies:
     react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
-  checksum: 10c0/a6dd5681f393642da5b90e804237220eb7100102b78683def51cb8ffae6c7d7a6924699a0964ea62ab3198ea8b05182fe58b1832a4a700d4f948667641e66a3e
+  checksum: 10c0/c59cb1a20b1f6cbbfe5e1a3b1d04a2f85c088e55d1ba2ae2be9d9db3c52ac15c4f6e03f70f0e333b6a735e6661de3ded74774a12890ef02b9f1479d6d6ee2f74
   languageName: node
   linkType: hard
 
@@ -7911,7 +8412,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0":
+"which-boxed-primitive@npm:^1.0.2":
   version: 1.1.0
   resolution: "which-boxed-primitive@npm:1.1.0"
   dependencies:
@@ -7924,7 +8425,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"which-builtin-type@npm:^1.2.0":
+"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1":
+  version: 1.1.1
+  resolution: "which-boxed-primitive@npm:1.1.1"
+  dependencies:
+    is-bigint: "npm:^1.1.0"
+    is-boolean-object: "npm:^1.2.1"
+    is-number-object: "npm:^1.1.1"
+    is-string: "npm:^1.1.1"
+    is-symbol: "npm:^1.1.1"
+  checksum: 10c0/aceea8ede3b08dede7dce168f3883323f7c62272b49801716e8332ff750e7ae59a511ae088840bc6874f16c1b7fd296c05c949b0e5b357bfe3c431b98c417abe
+  languageName: node
+  linkType: hard
+
+"which-builtin-type@npm:^1.2.1":
   version: 1.2.1
   resolution: "which-builtin-type@npm:1.2.1"
   dependencies:
@@ -7957,7 +8471,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.16":
+"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15":
   version: 1.1.16
   resolution: "which-typed-array@npm:1.1.16"
   dependencies:
@@ -7970,6 +8484,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18":
+  version: 1.1.18
+  resolution: "which-typed-array@npm:1.1.18"
+  dependencies:
+    available-typed-arrays: "npm:^1.0.7"
+    call-bind: "npm:^1.0.8"
+    call-bound: "npm:^1.0.3"
+    for-each: "npm:^0.3.3"
+    gopd: "npm:^1.2.0"
+    has-tostringtag: "npm:^1.0.2"
+  checksum: 10c0/0412f4a91880ca1a2a63056187c2e3de6b129b2b5b6c17bc3729f0f7041047ae48fb7424813e51506addb2c97320003ee18b8c57469d2cde37983ef62126143c
+  languageName: node
+  linkType: hard
+
 "which@npm:^2.0.1":
   version: 2.0.2
   resolution: "which@npm:2.0.2"
@@ -8140,9 +8668,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"zod@npm:^3.22.4":
-  version: 3.24.1
-  resolution: "zod@npm:3.24.1"
-  checksum: 10c0/0223d21dbaa15d8928fe0da3b54696391d8e3e1e2d0283a1a070b5980a1dbba945ce631c2d1eccc088fdbad0f2dfa40155590bf83732d3ac4fcca2cc9237591b
+"zod@npm:^3":
+  version: 3.24.2
+  resolution: "zod@npm:3.24.2"
+  checksum: 10c0/c638c7220150847f13ad90635b3e7d0321b36cce36f3fc6050ed960689594c949c326dfe2c6fa87c14b126ee5d370ccdebd6efb304f41ef5557a4aaca2824565
   languageName: node
   linkType: hard
-- 
GitLab


From b84f04eea33a34e6bdba6cf7ed9708a51d3c13c7 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 25 Feb 2025 14:37:06 +0100
Subject: [PATCH 13/48] rewired next-intl for package updates, got app running
 with latest changes from dev

---
 package.json                      |  1 +
 src/app/[locale]/layout.tsx       |  8 ++++++++
 src/components/app/Categories.tsx |  1 -
 src/components/layout/Search.tsx  |  5 ++---
 src/i18n/request.ts               | 18 ++++++++++++++++++
 src/i18n/routing.ts               | 14 ++++++++++++++
 yarn.lock                         | 16 ++++++++++++++++
 7 files changed, 59 insertions(+), 4 deletions(-)
 create mode 100644 src/i18n/request.ts
 create mode 100644 src/i18n/routing.ts

diff --git a/package.json b/package.json
index 8e202ee..c8dc2b4 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
   "dependencies": {
     "@headlessui/react": "^2",
     "@headlessui/tailwindcss": "^0.2.2",
+    "jotai": "^2.12.1",
     "next": "^15",
     "next-intl": "^3",
     "react": "^18",
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index b4080db..efb2bd3 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -1,5 +1,6 @@
 import type { Metadata, Viewport } from "next";
 import "./globals.css";
+import { notFound } from "next/navigation";
 import { useTranslations, useMessages, NextIntlClientProvider } from "next-intl";
 import React from "react";
 
@@ -7,6 +8,7 @@ import Banner from "@/components/layout/Banner";
 import Footer from "@/components/layout/Footer";
 import Header from "@/components/layout/Header";
 import { CategoryProvider } from "@/contexts/CategoryContext";
+import { routing } from "@/i18n/routing";
 
 const env = process.env.NEXT_PUBLIC_SHOW_BANNER;
 const showBanner = env && (env.toLowerCase() === "true" || env === "1");
@@ -43,6 +45,12 @@ export default function RootLayout({
   params: Promise<{ locale: string }>;
 }>) {
   const { locale } = React.use(params);
+
+  // Ensure that the incoming `locale` is valid
+  if (!routing.locales.includes(locale as any)) {
+    notFound();
+  }
+
   const t = useTranslations("Layout");
   const messages = useMessages();
 
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index 566b179..f8ebc8c 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -7,7 +7,6 @@ import { useState, useContext, useEffect } from "react";
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
 import { CategoryContext } from "@/contexts/CategoryContext";
-
 import { Link } from "@/i18n/routing";
 
 const Categories = () => {
diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index eb313e2..5f2014d 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -3,11 +3,10 @@
 import { useSearchParams } from "next/navigation";
 import { Suspense, useEffect, useState, useContext } from "react";
 
-import { CategoryContext } from "@/contexts/CategoryContext";
-import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
-
 import SearchForm from "@/components/layout/SearchForm";
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
+import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
 
 type Props = {
   exampleTrigger: string;
diff --git a/src/i18n/request.ts b/src/i18n/request.ts
new file mode 100644
index 0000000..452a1d3
--- /dev/null
+++ b/src/i18n/request.ts
@@ -0,0 +1,18 @@
+import { getRequestConfig } from "next-intl/server";
+
+import { routing } from "./routing";
+
+export default getRequestConfig(async ({ requestLocale }) => {
+  // This typically corresponds to the `[locale]` segment
+  let locale = await requestLocale;
+
+  // Ensure that a valid locale is used
+  if (!locale || !routing.locales.includes(locale as any)) {
+    locale = routing.defaultLocale;
+  }
+
+  return {
+    locale,
+    messages: (await import(`../locales/${locale}.json`)).default,
+  };
+});
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
new file mode 100644
index 0000000..4fc53c0
--- /dev/null
+++ b/src/i18n/routing.ts
@@ -0,0 +1,14 @@
+import { createNavigation } from "next-intl/navigation";
+import { defineRouting } from "next-intl/routing";
+
+export const routing = defineRouting({
+  // A list of all locales that are supported
+  locales: ["en", "de"],
+
+  // Used when no locale matches
+  defaultLocale: "en",
+});
+
+// Lightweight wrappers around Next.js' navigation APIs
+// that will consider the routing configuration
+export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing);
diff --git a/yarn.lock b/yarn.lock
index 24e40e0..44a5353 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5502,6 +5502,21 @@ __metadata:
   languageName: node
   linkType: hard
 
+"jotai@npm:^2.12.1":
+  version: 2.12.1
+  resolution: "jotai@npm:2.12.1"
+  peerDependencies:
+    "@types/react": ">=17.0.0"
+    react: ">=17.0.0"
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+    react:
+      optional: true
+  checksum: 10c0/8ef622b7d626919d87595d7382467cb81698a14925befdd27abc3b03d441077eca35328d51d10dc6fe3e91ee4d4e9ba7456bacbdf36940161bd2afeba861ac83
+  languageName: node
+  linkType: hard
+
 "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
   version: 4.0.0
   resolution: "js-tokens@npm:4.0.0"
@@ -8225,6 +8240,7 @@ __metadata:
     isomorphic-fetch: "npm:^3.0.0"
     jest: "npm:^29.7.0"
     jest-environment-jsdom: "npm:^29.7.0"
+    jotai: "npm:^2.12.1"
     next: "npm:^15"
     next-intl: "npm:^3"
     node-mocks-http: "npm:^1.14.1"
-- 
GitLab


From 96b1d9b50df02bc5605fafb86514c2beff1a9c0a Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 25 Feb 2025 14:45:57 +0100
Subject: [PATCH 14/48] changes that get the app working with updated packages,
 context driven architecture and updated footer

---
 src/i18n/request.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/i18n/request.ts b/src/i18n/request.ts
index 452a1d3..7f61a46 100644
--- a/src/i18n/request.ts
+++ b/src/i18n/request.ts
@@ -1,6 +1,6 @@
 import { getRequestConfig } from "next-intl/server";
 
-import { routing } from "./routing";
+import { routing } from "@/i18n/routing";
 
 export default getRequestConfig(async ({ requestLocale }) => {
   // This typically corresponds to the `[locale]` segment
-- 
GitLab


From 07e6a5d73342af69c8836d3172a2b4da92f4808e Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 25 Feb 2025 15:37:05 +0100
Subject: [PATCH 15/48] removed relative import of locales in request
 definition and simplified routing file in i18n

---
 src/i18n/request.ts |  2 +-
 src/i18n/routing.ts | 16 +++++++---------
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/src/i18n/request.ts b/src/i18n/request.ts
index 7f61a46..73f0e42 100644
--- a/src/i18n/request.ts
+++ b/src/i18n/request.ts
@@ -13,6 +13,6 @@ export default getRequestConfig(async ({ requestLocale }) => {
 
   return {
     locale,
-    messages: (await import(`../locales/${locale}.json`)).default,
+    messages: (await import(`@/locales/${locale}.json`)).default,
   };
 });
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
index 4fc53c0..7d9aa72 100644
--- a/src/i18n/routing.ts
+++ b/src/i18n/routing.ts
@@ -1,14 +1,12 @@
 import { createNavigation } from "next-intl/navigation";
-import { defineRouting } from "next-intl/routing";
+import { defineRouting, LocalePrefix } from "next-intl/routing";
 
-export const routing = defineRouting({
-  // A list of all locales that are supported
+export const i18nConfig = {
   locales: ["en", "de"],
-
-  // Used when no locale matches
   defaultLocale: "en",
-});
+  localeDetection: false,
+  localePrefix: "as-needed" as LocalePrefix,
+};
+export const routing = defineRouting(i18nConfig);
 
-// Lightweight wrappers around Next.js' navigation APIs
-// that will consider the routing configuration
-export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing);
+export const { Link } = createNavigation(routing);
-- 
GitLab


From fe8c5f9ccc74f4b83f675812a6f49fbe1d5d3d8e Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:22:06 +0100
Subject: [PATCH 16/48] testing state values for an effect capturing navigation
 via search from details page

---
 src/app/[locale]/results/details/page.tsx                | 1 -
 src/components/app/results/CategoryBar.tsx               | 3 +--
 src/components/app/results/ListResults/ResultItem.tsx    | 3 +--
 .../app/results/ListResults/ResultItem/Relationships.tsx | 3 +--
 src/components/layout/Header.tsx                         | 3 +--
 src/components/layout/Search.tsx                         | 6 +++---
 src/components/layout/SearchForm.tsx                     | 9 +++++----
 src/contexts/CategoryContext.tsx                         | 9 +++++++++
 8 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/src/app/[locale]/results/details/page.tsx b/src/app/[locale]/results/details/page.tsx
index c481f9b..ce02e26 100644
--- a/src/app/[locale]/results/details/page.tsx
+++ b/src/app/[locale]/results/details/page.tsx
@@ -3,7 +3,6 @@ import React, { Suspense } from "react";
 import BackBar from "@/components/app/results/BackBar";
 import DetailsItemCard from "@/components/app/results/Details/DetailsItemCard";
 import RelatedResults from "@/components/app/results/Details/RelatedResults";
-
 import SearchForm from "@/components/layout/SearchForm";
 
 const DetailsComponent = () => {
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index d464cb1..bf6cbc1 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -5,9 +5,8 @@ import { useTranslations } from "next-intl";
 import { Suspense, useContext } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
-import type { Category } from "@/types/types";
-
 import { Link } from "@/i18n/routing";
+import type { Category } from "@/types/types";
 
 type Props = {
   activeCategory?: Category;
diff --git a/src/components/app/results/ListResults/ResultItem.tsx b/src/components/app/results/ListResults/ResultItem.tsx
index 57be19d..dbdae8f 100644
--- a/src/components/app/results/ListResults/ResultItem.tsx
+++ b/src/components/app/results/ListResults/ResultItem.tsx
@@ -10,13 +10,12 @@ import { INSTITUTE_ROR_LOGOS } from "@/app/api/config/config";
 import ExternalLink from "@/components/ExternalLink";
 import Relationships from "@/components/app/results/ListResults/ResultItem/Relationships";
 import { CategoryContext } from "@/contexts/CategoryContext";
+import { Link } from "@/i18n/routing";
 import ExternalLinkIcon from "@/resources/images/svg/ExternalLinkIcon";
 import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
 import type { Category, ResultItem as ResultItemType } from "@/types/types";
 import { detailsItemAtom } from "@/utils/atoms";
 
-import { Link } from "@/i18n/routing";
-
 type Props = {
   item: ResultItemType;
   idx: string;
diff --git a/src/components/app/results/ListResults/ResultItem/Relationships.tsx b/src/components/app/results/ListResults/ResultItem/Relationships.tsx
index 27d7ad1..de00642 100644
--- a/src/components/app/results/ListResults/ResultItem/Relationships.tsx
+++ b/src/components/app/results/ListResults/ResultItem/Relationships.tsx
@@ -3,9 +3,8 @@ import { useTranslations } from "next-intl";
 import React, { useEffect, useRef, useState } from "react";
 
 import { INSTITUTE_ROR_LOGOS } from "@/app/api/config/config";
-import ExternalLinkIcon from "@/resources/images/svg/ExternalLinkIcon";
-
 import { Link } from "@/i18n/routing";
+import ExternalLinkIcon from "@/resources/images/svg/ExternalLinkIcon";
 
 type Props = {
   filteredArr: { label: string; type: string; id: string }[];
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index 4a33106..fc902c4 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -2,11 +2,10 @@ import Image from "next/image";
 import { useTranslations } from "next-intl";
 
 import Search from "@/components/layout/Search";
+import { Link } from "@/i18n/routing";
 import logoHGF from "@/resources/images/logo/Logo_HGF-KG_text_brightback.png";
 import unhideLogo from "@/resources/images/logo/unhide_header.png";
 
-import { Link } from "@/i18n/routing";
-
 const Header = () => {
   const t = useTranslations("Search");
   const s = useTranslations("Intro");
diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index 5f2014d..e369efa 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -60,13 +60,13 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
                 `category=${detailsText ? "datasets" : paramsCategory}&searchText=${query}`
               }
               className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
-              onClick={() =>
+              onClick={() => {
                 setPrimaryActiveCategory(
                   visibleCategories.filter(
                     (cat) => cat.id === (detailsText ? "datasets" : paramsCategory)
                   )[0]
-                )
-              }
+                );
+              }}
             >
               <div className="basis-1">{query}</div>
               <RightArrowIcon />
diff --git a/src/components/layout/SearchForm.tsx b/src/components/layout/SearchForm.tsx
index 03ff200..84a74c3 100644
--- a/src/components/layout/SearchForm.tsx
+++ b/src/components/layout/SearchForm.tsx
@@ -5,11 +5,10 @@ import { useTranslations } from "next-intl";
 import { Suspense, useEffect, useState, useContext } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
+import { Link } from "@/i18n/routing";
 import ClearIcon from "@/resources/images/svg/ClearIcon";
 import SearchIcon from "@/resources/images/svg/SearchIcon";
 
-import { Link } from "@/i18n/routing";
-
 const SearchFormComponent = () => {
   const resultParams = useSearchParams();
   const router = useRouter();
@@ -17,6 +16,7 @@ const SearchFormComponent = () => {
 
   const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
   const paramsCategory = resultParams.get("category") ?? "datasets";
+  const detailsText = resultParams.get("detailsText") ?? undefined;
 
   const { visibleCategories, setPrimaryActiveCategory } = useContext(CategoryContext);
 
@@ -31,11 +31,12 @@ const SearchFormComponent = () => {
 
   const submitSearch = (event: any) => {
     event.preventDefault();
-    setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
     router.push(
-      `/results?category=${paramsCategory}` + (searchQuery ? `&searchText=${searchQuery}` : "")
+      `/results?category=${detailsText ? "datasets" : paramsCategory}` +
+        (searchQuery ? `&searchText=${searchQuery}` : "")
     );
   };
+
   return (
     <div>
       <form
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index bb96698..b951ac0 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -107,6 +107,15 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   // Effects affecting the categories in the details view
 
   useEffect(() => {
+    // State when we navigate searching from details view
+    if (inDetailsView && searchText && visibleCategories.length === 7) {
+      console.log("change category now?");
+      setPrimaryActiveCategory(null);
+    }
+  }, [inDetailsView, searchText, visibleCategories]);
+
+  useEffect(() => {
+    //console.log("ive run with ", primaryActiveCategory);
     if (!primaryActiveCategory && !inDetailsView) {
       setPrimaryActiveCategory(visibleCategories[0]);
     }
-- 
GitLab


From 389538e812a69a778e7dbbe1fe9f42a6ea9a9436 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 26 Feb 2025 10:26:57 +0100
Subject: [PATCH 17/48] resetting category state on navigation to landing page
 via click of Header icon

---
 src/components/layout/Header.tsx | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index fc902c4..3e5f384 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -1,7 +1,10 @@
+"use client";
 import Image from "next/image";
 import { useTranslations } from "next-intl";
+import { useContext } from "react";
 
 import Search from "@/components/layout/Search";
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import logoHGF from "@/resources/images/logo/Logo_HGF-KG_text_brightback.png";
 import unhideLogo from "@/resources/images/logo/unhide_header.png";
@@ -9,12 +12,20 @@ import unhideLogo from "@/resources/images/logo/unhide_header.png";
 const Header = () => {
   const t = useTranslations("Search");
   const s = useTranslations("Intro");
+
+  const { setPrimaryActiveCategory, setRelatedActiveCategory } = useContext(CategoryContext);
+
+  const handleReset = () => {
+    setPrimaryActiveCategory(null);
+    setRelatedActiveCategory(null);
+  };
+
   return (
     <div className="flex items-center justify-between px-4 md:px-12 lg:px-16">
       <div className=" basis-1/3 md:basis-1/8">
         {/* logo */}
         <div className="">
-          <Link href="/">
+          <Link href="/" onClick={handleReset}>
             <Image
               className="max-w-36 md:max-w-56 lg:max-w-64 xl:max-w-72"
               src={unhideLogo}
@@ -28,7 +39,7 @@ const Header = () => {
       {/* secondary nav */}
       <div className="hidden lg:flex lg:basis-1/2 xl:basis-2/3 ">
         <div className="mx-auto flex grow items-center justify-end">
-          <Search exampleTrigger={t("try")} placeholder={t("placeholder")} />
+          <Search exampleTrigger={t("try")} />
         </div>
       </div>
 
-- 
GitLab


From d597af89e43525866e9b1e7183c04b21dbe55e02 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 26 Feb 2025 10:58:42 +0100
Subject: [PATCH 18/48] context management for change via searchbar in results
 view

---
 src/components/layout/Search.tsx     | 10 +++++-----
 src/components/layout/SearchForm.tsx |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index e369efa..349af16 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -61,11 +61,11 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
               }
               className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
               onClick={() => {
-                setPrimaryActiveCategory(
-                  visibleCategories.filter(
-                    (cat) => cat.id === (detailsText ? "datasets" : paramsCategory)
-                  )[0]
-                );
+                if (detailsText) {
+                  setPrimaryActiveCategory(
+                    visibleCategories.filter((cat) => cat.id === "datasets")[0]
+                  );
+                }
               }}
             >
               <div className="basis-1">{query}</div>
diff --git a/src/components/layout/SearchForm.tsx b/src/components/layout/SearchForm.tsx
index 84a74c3..d4f20b5 100644
--- a/src/components/layout/SearchForm.tsx
+++ b/src/components/layout/SearchForm.tsx
@@ -26,7 +26,7 @@ const SearchFormComponent = () => {
 
   const handleClear = () => {
     setSearchQuery("");
-    setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
+    // setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
   };
 
   const submitSearch = (event: any) => {
-- 
GitLab


From 753cec0cb34d020a3447834c91daad632d98c8d9 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 26 Feb 2025 14:32:32 +0100
Subject: [PATCH 19/48] handling context and params when navigating back to
 results page via search bar

---
 src/components/layout/Search.tsx     | 12 +------
 src/components/layout/SearchForm.tsx | 16 ++-------
 src/contexts/CategoryContext.tsx     | 53 +++++++++++++++-------------
 src/types/types.ts                   |  3 +-
 4 files changed, 33 insertions(+), 51 deletions(-)

diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index 349af16..d6e2489 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -1,10 +1,9 @@
 "use client";
 
 import { useSearchParams } from "next/navigation";
-import { Suspense, useEffect, useState, useContext } from "react";
+import { Suspense, useEffect, useState } from "react";
 
 import SearchForm from "@/components/layout/SearchForm";
-import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
 
@@ -21,8 +20,6 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
   // not really random but randomly enough for us…
   const [exampleSearch, setExamples] = useState<string[]>([]);
 
-  const { visibleCategories, setPrimaryActiveCategory } = useContext(CategoryContext);
-
   useEffect(() => {
     setExamples(
       [
@@ -60,13 +57,6 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
                 `category=${detailsText ? "datasets" : paramsCategory}&searchText=${query}`
               }
               className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
-              onClick={() => {
-                if (detailsText) {
-                  setPrimaryActiveCategory(
-                    visibleCategories.filter((cat) => cat.id === "datasets")[0]
-                  );
-                }
-              }}
             >
               <div className="basis-1">{query}</div>
               <RightArrowIcon />
diff --git a/src/components/layout/SearchForm.tsx b/src/components/layout/SearchForm.tsx
index d4f20b5..b535249 100644
--- a/src/components/layout/SearchForm.tsx
+++ b/src/components/layout/SearchForm.tsx
@@ -2,9 +2,8 @@
 
 import { useSearchParams, useRouter } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { Suspense, useEffect, useState, useContext } from "react";
+import { Suspense, useEffect, useState } from "react";
 
-import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import ClearIcon from "@/resources/images/svg/ClearIcon";
 import SearchIcon from "@/resources/images/svg/SearchIcon";
@@ -18,17 +17,10 @@ const SearchFormComponent = () => {
   const paramsCategory = resultParams.get("category") ?? "datasets";
   const detailsText = resultParams.get("detailsText") ?? undefined;
 
-  const { visibleCategories, setPrimaryActiveCategory } = useContext(CategoryContext);
-
   useEffect(() => {
     setSearchQuery(resultParams.get("searchText") ?? "");
   }, [resultParams]);
 
-  const handleClear = () => {
-    setSearchQuery("");
-    // setPrimaryActiveCategory(visibleCategories.filter((cat) => cat.id === paramsCategory)[0]);
-  };
-
   const submitSearch = (event: any) => {
     event.preventDefault();
     router.push(
@@ -52,14 +44,12 @@ const SearchFormComponent = () => {
           />
         </div>
 
-        {/* search option buttons */}
-        {/* TODO: refactor this into a single reusable component? */}
         <div className="flex basis-2/12 md:basis-2/12 justify-end divide-x-2">
           {searchQuery !== "" ? (
             <Link
               className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
-              onClick={handleClear}
-              href={`/results?category=${paramsCategory}`}
+              onClick={() => setSearchQuery("")}
+              href={`/results?category=${detailsText ? "datasets" : paramsCategory}`}
             >
               <ClearIcon />
             </Link>
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index b951ac0..6c7dfcf 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -23,6 +23,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const detailsText = resultParams.get("detailsText") ?? undefined;
 
   const [baseCategories, setBaseCategories] = useState<Category[]>([]);
+  const [totalNoOfCategories, setTotalNoOfCategories] = useState<number>(0);
 
   // Function to set the category images
   const fetchCategoriesAndSetImages = async () => {
@@ -49,6 +50,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
       try {
         const allCategories = await fetchCategoriesAndSetImages();
         setVisibleCategories(allCategories);
+        setTotalNoOfCategories(allCategories.length);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
       }
@@ -66,6 +68,31 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [detailsText]);
 
+  useEffect(() => {
+    if (!primaryActiveCategory && !inDetailsView) {
+      setPrimaryActiveCategory(visibleCategories[0]);
+    }
+  }, [inDetailsView, primaryActiveCategory, visibleCategories]);
+
+  // Effect runs when navigating into details view or in details view
+  useEffect(() => {
+    if (inDetailsView) {
+      setRelatedActiveCategory(visibleCategories[0]);
+    }
+  }, [visibleCategories, inDetailsView]);
+
+  // Effect runs when navigating out from details view
+  useEffect(() => {
+    if (!inDetailsView && visibleCategories.length < totalNoOfCategories) {
+      // You are no longer in details view and have a search text but you are navigating from details to results view and visibile category is not yet updated
+      // this state exists because inDetailsView is searchParam based that updates and maintains the most relevant state during navigation
+      // but we are using the small gap during navigation wherein visibleCategories is being updated via the above effect,
+      // this intermediate state points to us that we navigated from the details view
+      setPrimaryActiveCategory(null);
+      setRelatedActiveCategory(null);
+    }
+  }, [inDetailsView, visibleCategories, totalNoOfCategories]);
+
   useEffect(() => {
     const setCategoryBarCategories = async () => {
       try {
@@ -104,39 +131,15 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     fetchAndUpdateCategoryBarCount();
   }, [searchText, detailsText, baseCategories]);
 
-  // Effects affecting the categories in the details view
-
-  useEffect(() => {
-    // State when we navigate searching from details view
-    if (inDetailsView && searchText && visibleCategories.length === 7) {
-      console.log("change category now?");
-      setPrimaryActiveCategory(null);
-    }
-  }, [inDetailsView, searchText, visibleCategories]);
-
-  useEffect(() => {
-    //console.log("ive run with ", primaryActiveCategory);
-    if (!primaryActiveCategory && !inDetailsView) {
-      setPrimaryActiveCategory(visibleCategories[0]);
-    }
-  }, [inDetailsView, primaryActiveCategory, visibleCategories]);
-
-  useEffect(() => {
-    if (inDetailsView) {
-      setRelatedActiveCategory(visibleCategories[0]);
-    }
-  }, [visibleCategories, inDetailsView]);
-
   return (
     <CategoryContext.Provider
       value={{
+        inDetailsView,
         visibleCategories,
-        setVisibleCategories,
         primaryActiveCategory,
         setPrimaryActiveCategory,
         relatedActiveCategory,
         setRelatedActiveCategory,
-        inDetailsView,
       }}
     >
       {children}
diff --git a/src/types/types.ts b/src/types/types.ts
index 0804134..c4065e0 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -184,13 +184,12 @@ export type Category = {
 };
 
 export interface CategoryContextType {
+  inDetailsView?: boolean;
   visibleCategories: Category[];
-  setVisibleCategories: (categories: Category[]) => void;
   primaryActiveCategory: Category | null;
   setPrimaryActiveCategory: (category: Category | null) => void;
   relatedActiveCategory: Category | null;
   setRelatedActiveCategory: (category: Category | null) => void;
-  inDetailsView?: boolean;
 }
 
 // -------- SEARCH API ---------
-- 
GitLab


From fafb723d3610a60ac33e33f56b9020bc0aa1c519 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 26 Feb 2025 16:12:49 +0100
Subject: [PATCH 20/48] refactored code to create a basic component that shows
 results based on categories

---
 .../app/results/CategorizedResults.tsx        | 39 ++++++++++++++++
 .../app/results/Details/RelatedResults.tsx    | 46 ++-----------------
 src/i18n/routing.ts                           |  3 +-
 3 files changed, 46 insertions(+), 42 deletions(-)
 create mode 100644 src/components/app/results/CategorizedResults.tsx

diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
new file mode 100644
index 0000000..41a156e
--- /dev/null
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -0,0 +1,39 @@
+"use client";
+import React, { useState, useContext, useEffect, Suspense } from "react";
+import CategoryBar from "@/components/app/results/CategoryBar";
+import ListResults from "@/components/app/results/ListResults";
+import { CategoryContext } from "@/contexts/CategoryContext";
+import { Category } from "@/types/types";
+
+const CategorizedResultsComponent = () => {
+  const [activeCategory, setActiveCategory] = useState<Category>();
+  const [resultsLoading, setResultsLoading] = useState(false);
+  const { relatedActiveCategory } = useContext(CategoryContext);
+
+  useEffect(() => {
+    if (relatedActiveCategory !== null) {
+      setActiveCategory(relatedActiveCategory);
+    }
+  }, [relatedActiveCategory]);
+
+  return (
+    <>
+      <CategoryBar activeCategory={activeCategory} />
+      {activeCategory && (
+        <ListResults
+          activeCategory={activeCategory}
+          resultsLoading={resultsLoading}
+          setResultsLoading={setResultsLoading}
+        />
+      )}
+    </>
+  );
+};
+
+export default function CategorizedResults() {
+  return (
+    <Suspense>
+      <CategorizedResultsComponent />
+    </Suspense>
+  );
+}
diff --git a/src/components/app/results/Details/RelatedResults.tsx b/src/components/app/results/Details/RelatedResults.tsx
index 5ad0e95..a2badcb 100644
--- a/src/components/app/results/Details/RelatedResults.tsx
+++ b/src/components/app/results/Details/RelatedResults.tsx
@@ -1,51 +1,15 @@
-"use client";
 import { useTranslations } from "next-intl";
-import React, { useState, useEffect, useContext } from "react";
-
-import CategoryBar from "@/components/app/results/CategoryBar";
-import ListResults from "@/components/app/results/ListResults";
-import Error from "@/components/layout/Error";
-import Spinner from "@/components/layout/Spinner";
-import { CategoryContext } from "@/contexts/CategoryContext";
-import { Category } from "@/types/types";
+import CategorizedResults from "@/components/app/results/CategorizedResults";
 
 const RelatedResults = () => {
   const d = useTranslations("DetailsView");
-  const { relatedActiveCategory } = useContext(CategoryContext);
-  const [activeCategory, setActiveCategory] = useState<Category>();
-
-  // Following will be refactored in a new MR that looks in loading and error state handling
-  const [categoryLoading, setCategoryLoading] = useState(false);
-  const [resultsLoading, setResultsLoading] = useState(false);
-  const [hasBcError, setHasBcError] = useState(false);
-  const [hasSearchError, setHasSearchError] = useState(false);
-
-  useEffect(() => {
-    if (relatedActiveCategory !== null) {
-      setActiveCategory(relatedActiveCategory);
-    }
-  }, [relatedActiveCategory]);
 
   return (
     <div className="m-4 space-y-2 rounded-xl border-2 border-primary-helmholtz-hellblau/20 px-5 py-4 shadow-lg shadow-secondary-helmholtz-highlightblau bg-white">
-      <>
-        {!categoryLoading && !resultsLoading ? (
-          <p className="text-sm font-semibold text-primary-helmholtz-dunkelblau cursor-default">
-            {d("other_items")}
-          </p>
-        ) : null}
-        <CategoryBar activeCategory={activeCategory} />
-        {activeCategory && (
-          <ListResults
-            activeCategory={activeCategory}
-            resultsLoading={resultsLoading}
-            setResultsLoading={setResultsLoading}
-          />
-        )}
-      </>
-
-      {(categoryLoading || resultsLoading) ?? <Spinner />}
-      {(hasBcError || hasSearchError) ?? <Error />}
+      <p className="text-sm font-semibold text-primary-helmholtz-dunkelblau cursor-default">
+        {d("other_items")}
+      </p>
+      <CategorizedResults />
     </div>
   );
 };
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
index 7d9aa72..ee5d06d 100644
--- a/src/i18n/routing.ts
+++ b/src/i18n/routing.ts
@@ -4,7 +4,8 @@ import { defineRouting, LocalePrefix } from "next-intl/routing";
 export const i18nConfig = {
   locales: ["en", "de"],
   defaultLocale: "en",
-  localeDetection: false,
+  // the following setting causes surprising side effects of category bar does not show the right language in details view
+  // localeDetection: false,
   localePrefix: "as-needed" as LocalePrefix,
 };
 export const routing = defineRouting(i18nConfig);
-- 
GitLab


From cfe7d644fe26d982d5b61bbd5a7bead19fa8e5ff Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 26 Feb 2025 16:39:03 +0100
Subject: [PATCH 21/48] extended categorized results component to handle
 different view states depending on inDetailsView flag

---
 src/app/[locale]/results/details/page.tsx     |  6 +--
 src/app/[locale]/results/page.tsx             | 49 ++---------------
 .../app/results/CategorizedResults.tsx        | 52 +++++++++++++++----
 .../app/results/Details/RelatedResults.tsx    |  1 +
 4 files changed, 48 insertions(+), 60 deletions(-)

diff --git a/src/app/[locale]/results/details/page.tsx b/src/app/[locale]/results/details/page.tsx
index ce02e26..7526d50 100644
--- a/src/app/[locale]/results/details/page.tsx
+++ b/src/app/[locale]/results/details/page.tsx
@@ -3,15 +3,13 @@ import React, { Suspense } from "react";
 import BackBar from "@/components/app/results/BackBar";
 import DetailsItemCard from "@/components/app/results/Details/DetailsItemCard";
 import RelatedResults from "@/components/app/results/Details/RelatedResults";
-import SearchForm from "@/components/layout/SearchForm";
+import MobileSearch from "@/components/layout/MobileSearch";
 
 const DetailsComponent = () => {
   return (
     <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
       <BackBar />
-      <div id="smallScreenDetailsSearch">
-        <SearchForm />
-      </div>
+      <MobileSearch />
       <DetailsItemCard />
       <RelatedResults />
     </div>
diff --git a/src/app/[locale]/results/page.tsx b/src/app/[locale]/results/page.tsx
index 5a3dd66..a2e24e6 100644
--- a/src/app/[locale]/results/page.tsx
+++ b/src/app/[locale]/results/page.tsx
@@ -1,56 +1,13 @@
 "use client";
 
-import React, { Suspense, useState, useContext, useEffect } from "react";
+import React, { Suspense } from "react";
 
-import BackBar from "@/components/app/results/BackBar";
-import CategoryBar from "@/components/app/results/CategoryBar";
-import ListResults from "@/components/app/results/ListResults";
-import Error from "@/components/layout/Error";
-import MobileSearch from "@/components/layout/MobileSearch";
-import Spinner from "@/components/layout/Spinner";
-import { CategoryContext } from "@/contexts/CategoryContext";
-import type { Category } from "@/types/types";
+import CategorizedResults from "@/components/app/results/CategorizedResults";
 
 const ResultsComponent = () => {
-  const { primaryActiveCategory } = useContext(CategoryContext);
-  const [activeCategory, setActiveCategory] = useState<Category>();
-
-  // Following will be refactored in a new MR that looks in loading and error state handling
-  const [hasBcError, setHasBcError] = useState(false);
-  const [hasSearchError, setHasSearchError] = useState(false);
-  const [categoryLoading, setCategoryLoading] = useState(false);
-  const [resultsLoading, setResultsLoading] = useState(true);
-
-  useEffect(() => {
-    if (primaryActiveCategory !== null) {
-      setActiveCategory(primaryActiveCategory);
-    }
-  }, [primaryActiveCategory]);
-
   return (
     <div className="grow flex flex-col justify-start">
-      <>
-        <CategoryBar activeCategory={activeCategory} />
-        <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
-          {!categoryLoading && !resultsLoading ? (
-            <>
-              <BackBar />
-              <MobileSearch />
-            </>
-          ) : null}
-
-          {activeCategory && (
-            <ListResults
-              activeCategory={activeCategory}
-              resultsLoading={resultsLoading}
-              setResultsLoading={setResultsLoading}
-            />
-          )}
-        </div>
-      </>
-
-      {(categoryLoading || resultsLoading) && <Spinner />}
-      {(hasBcError || hasSearchError) && <Error />}
+      <CategorizedResults />
     </div>
   );
 };
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 41a156e..ae09dde 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -1,30 +1,62 @@
 "use client";
 import React, { useState, useContext, useEffect, Suspense } from "react";
+
+import BackBar from "@/components/app/results/BackBar";
 import CategoryBar from "@/components/app/results/CategoryBar";
 import ListResults from "@/components/app/results/ListResults";
+import MobileSearch from "@/components/layout/MobileSearch";
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Category } from "@/types/types";
 
 const CategorizedResultsComponent = () => {
   const [activeCategory, setActiveCategory] = useState<Category>();
   const [resultsLoading, setResultsLoading] = useState(false);
-  const { relatedActiveCategory } = useContext(CategoryContext);
+  const { inDetailsView, primaryActiveCategory, relatedActiveCategory } =
+    useContext(CategoryContext);
 
   useEffect(() => {
-    if (relatedActiveCategory !== null) {
+    if (inDetailsView && relatedActiveCategory !== null) {
       setActiveCategory(relatedActiveCategory);
     }
-  }, [relatedActiveCategory]);
+  }, [inDetailsView, relatedActiveCategory]);
+
+  useEffect(() => {
+    if (!inDetailsView && primaryActiveCategory !== null) {
+      setActiveCategory(primaryActiveCategory);
+    }
+  }, [inDetailsView, primaryActiveCategory]);
 
   return (
     <>
-      <CategoryBar activeCategory={activeCategory} />
-      {activeCategory && (
-        <ListResults
-          activeCategory={activeCategory}
-          resultsLoading={resultsLoading}
-          setResultsLoading={setResultsLoading}
-        />
+      {inDetailsView ? (
+        <>
+          <CategoryBar activeCategory={activeCategory} />
+          {activeCategory && (
+            <ListResults
+              activeCategory={activeCategory}
+              resultsLoading={resultsLoading}
+              setResultsLoading={setResultsLoading}
+            />
+          )}
+        </>
+      ) : (
+        <>
+          <CategoryBar activeCategory={activeCategory} />
+          <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
+            <>
+              <BackBar />
+              <MobileSearch />
+            </>
+
+            {activeCategory && (
+              <ListResults
+                activeCategory={activeCategory}
+                resultsLoading={resultsLoading}
+                setResultsLoading={setResultsLoading}
+              />
+            )}
+          </div>
+        </>
       )}
     </>
   );
diff --git a/src/components/app/results/Details/RelatedResults.tsx b/src/components/app/results/Details/RelatedResults.tsx
index a2badcb..586d148 100644
--- a/src/components/app/results/Details/RelatedResults.tsx
+++ b/src/components/app/results/Details/RelatedResults.tsx
@@ -1,4 +1,5 @@
 import { useTranslations } from "next-intl";
+
 import CategorizedResults from "@/components/app/results/CategorizedResults";
 
 const RelatedResults = () => {
-- 
GitLab


From 359a30e77c2701354130288eeed6c27ae307484e Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 26 Feb 2025 17:01:35 +0100
Subject: [PATCH 22/48] loading and error state based handling of category
 context for landing page implemented

---
 src/components/app/Categories.tsx | 19 ++++++++++---------
 src/contexts/CategoryContext.tsx  | 12 ++++++++++++
 src/types/types.ts                |  2 ++
 3 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index f8ebc8c..d0bd0c9 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -2,7 +2,7 @@
 
 import Image from "next/image";
 import { useTranslations } from "next-intl";
-import { useState, useContext, useEffect } from "react";
+import { useContext, useEffect } from "react";
 
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
@@ -11,27 +11,28 @@ import { Link } from "@/i18n/routing";
 
 const Categories = () => {
   const t = useTranslations("Categories");
-  const { visibleCategories, setPrimaryActiveCategory, setRelatedActiveCategory } =
-    useContext(CategoryContext);
+  const {
+    categoryIsLoading,
+    categoryHasError,
+    visibleCategories,
+    setPrimaryActiveCategory,
+    setRelatedActiveCategory,
+  } = useContext(CategoryContext);
 
   useEffect(() => {
     setRelatedActiveCategory(null);
     setPrimaryActiveCategory(null);
   }, [setPrimaryActiveCategory, setRelatedActiveCategory]);
 
-  // TODO: handle loading and error states
-  const [isLoading, setIsLoading] = useState<boolean>(false);
-  const [hasError, setHasError] = useState<boolean>(false);
-
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
   /* traditional JS module that makes the badge numbers appear as strings representing exponents
       check - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options */
 
   return (
     <div className="grow flex flex-col">
-      {isLoading ? (
+      {categoryIsLoading ? (
         <Spinner />
-      ) : hasError ? (
+      ) : categoryHasError ? (
         <Error />
       ) : (
         <div className="grow container mx-auto flex flex-col justify-center pb-16 md:p-14 md:pt-2 lg:p-20 lg:pt-10 xl:pt-10 xl:pb-20 2xl:pb-16 3xl:py-0">
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 6c7dfcf..5f7b5c7 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -17,6 +17,10 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
   const [inDetailsView, setInDetailsView] = useState<boolean>(false);
 
+  // Flags for loading and error states
+  const [categoryIsLoading, setCategoryIsLoading] = useState<boolean>(false);
+  const [categoryHasError, setCategoryHasError] = useState<boolean>(false);
+
   // State for processing
   const resultParams = useSearchParams();
   const searchText = resultParams.get("searchText") ?? undefined;
@@ -48,13 +52,19 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   useEffect(() => {
     const setLandingPageCategories = async () => {
       try {
+        setCategoryIsLoading(true);
+        setCategoryHasError(false);
         const allCategories = await fetchCategoriesAndSetImages();
         setVisibleCategories(allCategories);
         setTotalNoOfCategories(allCategories.length);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
+        setCategoryHasError(true);
+      } finally {
+        setCategoryIsLoading(false);
       }
     };
+
     setLandingPageCategories();
   }, []);
 
@@ -134,6 +144,8 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   return (
     <CategoryContext.Provider
       value={{
+        categoryIsLoading,
+        categoryHasError,
         inDetailsView,
         visibleCategories,
         primaryActiveCategory,
diff --git a/src/types/types.ts b/src/types/types.ts
index c4065e0..e438b8f 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -184,6 +184,8 @@ export type Category = {
 };
 
 export interface CategoryContextType {
+  categoryIsLoading: boolean;
+  categoryHasError: boolean;
   inDetailsView?: boolean;
   visibleCategories: Category[];
   primaryActiveCategory: Category | null;
-- 
GitLab


From e5e5ab1d9ecb82a287593ea6cc2855e0363c8b1a Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 26 Feb 2025 18:55:52 +0100
Subject: [PATCH 23/48] first iteration of state and error handling for
 categories in different components implemented

---
 src/components/app/Categories.tsx             |  8 +--
 .../app/results/CategorizedResults.tsx        | 65 ++++++++++++-------
 src/components/layout/Error.tsx               |  2 +-
 src/components/layout/Spinner.tsx             |  2 +-
 src/contexts/CategoryContext.tsx              | 59 +++++++++++++----
 src/types/types.ts                            |  6 +-
 6 files changed, 96 insertions(+), 46 deletions(-)

diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index d0bd0c9..4f10216 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -12,8 +12,8 @@ import { Link } from "@/i18n/routing";
 const Categories = () => {
   const t = useTranslations("Categories");
   const {
-    categoryIsLoading,
-    categoryHasError,
+    landingPageCategoriesError,
+    landingPageCategoriesLoading,
     visibleCategories,
     setPrimaryActiveCategory,
     setRelatedActiveCategory,
@@ -30,9 +30,9 @@ const Categories = () => {
 
   return (
     <div className="grow flex flex-col">
-      {categoryIsLoading ? (
+      {landingPageCategoriesLoading ? (
         <Spinner />
-      ) : categoryHasError ? (
+      ) : landingPageCategoriesError ? (
         <Error />
       ) : (
         <div className="grow container mx-auto flex flex-col justify-center pb-16 md:p-14 md:pt-2 lg:p-20 lg:pt-10 xl:pt-10 xl:pb-20 2xl:pb-16 3xl:py-0">
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index ae09dde..4b17c7e 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -4,15 +4,22 @@ import React, { useState, useContext, useEffect, Suspense } from "react";
 import BackBar from "@/components/app/results/BackBar";
 import CategoryBar from "@/components/app/results/CategoryBar";
 import ListResults from "@/components/app/results/ListResults";
+import Error from "@/components/layout/Error";
 import MobileSearch from "@/components/layout/MobileSearch";
+import Spinner from "@/components/layout/Spinner";
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Category } from "@/types/types";
 
 const CategorizedResultsComponent = () => {
   const [activeCategory, setActiveCategory] = useState<Category>();
   const [resultsLoading, setResultsLoading] = useState(false);
-  const { inDetailsView, primaryActiveCategory, relatedActiveCategory } =
-    useContext(CategoryContext);
+  const {
+    categoryBarLoading,
+    categoryBarHasError,
+    inDetailsView,
+    primaryActiveCategory,
+    relatedActiveCategory,
+  } = useContext(CategoryContext);
 
   useEffect(() => {
     if (inDetailsView && relatedActiveCategory !== null) {
@@ -28,34 +35,42 @@ const CategorizedResultsComponent = () => {
 
   return (
     <>
-      {inDetailsView ? (
-        <>
-          <CategoryBar activeCategory={activeCategory} />
-          {activeCategory && (
-            <ListResults
-              activeCategory={activeCategory}
-              resultsLoading={resultsLoading}
-              setResultsLoading={setResultsLoading}
-            />
-          )}
-        </>
+      {categoryBarLoading ? (
+        <Spinner />
+      ) : categoryBarHasError ? (
+        <Error />
       ) : (
         <>
-          <CategoryBar activeCategory={activeCategory} />
-          <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
+          {inDetailsView ? (
             <>
-              <BackBar />
-              <MobileSearch />
+              <CategoryBar activeCategory={activeCategory} />
+              {activeCategory && (
+                <ListResults
+                  activeCategory={activeCategory}
+                  resultsLoading={resultsLoading}
+                  setResultsLoading={setResultsLoading}
+                />
+              )}
             </>
+          ) : (
+            <>
+              <CategoryBar activeCategory={activeCategory} />
+              <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
+                <>
+                  <BackBar />
+                  <MobileSearch />
+                </>
 
-            {activeCategory && (
-              <ListResults
-                activeCategory={activeCategory}
-                resultsLoading={resultsLoading}
-                setResultsLoading={setResultsLoading}
-              />
-            )}
-          </div>
+                {activeCategory && (
+                  <ListResults
+                    activeCategory={activeCategory}
+                    resultsLoading={resultsLoading}
+                    setResultsLoading={setResultsLoading}
+                  />
+                )}
+              </div>
+            </>
+          )}
         </>
       )}
     </>
diff --git a/src/components/layout/Error.tsx b/src/components/layout/Error.tsx
index 0b940c1..690ac5b 100644
--- a/src/components/layout/Error.tsx
+++ b/src/components/layout/Error.tsx
@@ -4,7 +4,7 @@ import React from "react";
 import ErrorWarning from "@/resources/images/misc/error-warning.png";
 const Error = () => {
   return (
-    <div className="grow flex flex-col justify-center items-center p-10 bg-whitesmoke h-full">
+    <div className="grow flex flex-col justify-center items-center p-10 h-full">
       {/* TODO: change the font on the icon */}
       <Image
         src={ErrorWarning || ""}
diff --git a/src/components/layout/Spinner.tsx b/src/components/layout/Spinner.tsx
index b78807f..fff90e0 100644
--- a/src/components/layout/Spinner.tsx
+++ b/src/components/layout/Spinner.tsx
@@ -1,5 +1,5 @@
 const Spinner = () => (
-  <div className="grow flex flex-col p-10 pt-5 bg-whitesmoke h-full">
+  <div className="grow flex flex-col p-10 pt-5 h-full">
     <svg
       className="m-auto align-middle max-h-20 max-w-20 animate-spin text-secondary-helmholtz-mint"
       xmlns="http://www.w3.org/2000/svg"
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 5f7b5c7..4e44030 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -16,18 +16,39 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
   const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
   const [inDetailsView, setInDetailsView] = useState<boolean>(false);
-
-  // Flags for loading and error states
-  const [categoryIsLoading, setCategoryIsLoading] = useState<boolean>(false);
-  const [categoryHasError, setCategoryHasError] = useState<boolean>(false);
+  // Main flags for loading and error states
+  const [landingPageCategoriesLoading, setLandingPageCategoriesLoading] = useState(false);
+  const [landingPageCategoriesError, setLandingPageCategoriesError] = useState(false);
+  const [categoryBarLoading, setCategoryBarLoading] = useState<boolean>(false);
+  const [categoryBarHasError, setCategoryBarHasError] = useState<boolean>(false);
 
   // State for processing
   const resultParams = useSearchParams();
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
-
-  const [baseCategories, setBaseCategories] = useState<Category[]>([]);
   const [totalNoOfCategories, setTotalNoOfCategories] = useState<number>(0);
+  const [baseCategories, setBaseCategories] = useState<Category[]>([]);
+
+  // Loading and error states for different API calls
+  const [categoryBarCategoriesLoading, setCategoryBarCategoriesLoading] = useState(false);
+  const [categoryBarCategoriesError, setCategoryBarCategoriesError] = useState(false);
+  const [categoryBarCountLoading, setCategoryBarCountLoading] = useState(false);
+  const [categoryBarCountError, setCategoryBarCountError] = useState(false);
+
+  useEffect(() => {
+    if (!categoryBarCategoriesLoading && !categoryBarCountLoading) {
+      setCategoryBarLoading(false);
+      setCategoryBarHasError(categoryBarCategoriesError || categoryBarCountError);
+    } else {
+      setCategoryBarLoading(true);
+      setCategoryBarHasError(false);
+    }
+  }, [
+    categoryBarCategoriesLoading,
+    categoryBarCategoriesError,
+    categoryBarCountLoading,
+    categoryBarCountError,
+  ]);
 
   // Function to set the category images
   const fetchCategoriesAndSetImages = async () => {
@@ -52,16 +73,16 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   useEffect(() => {
     const setLandingPageCategories = async () => {
       try {
-        setCategoryIsLoading(true);
-        setCategoryHasError(false);
+        setLandingPageCategoriesLoading(true);
+        setLandingPageCategoriesError(false);
         const allCategories = await fetchCategoriesAndSetImages();
         setVisibleCategories(allCategories);
         setTotalNoOfCategories(allCategories.length);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
-        setCategoryHasError(true);
+        setLandingPageCategoriesError(true);
       } finally {
-        setCategoryIsLoading(false);
+        setLandingPageCategoriesLoading(false);
       }
     };
 
@@ -106,6 +127,8 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   useEffect(() => {
     const setCategoryBarCategories = async () => {
       try {
+        setCategoryBarCategoriesLoading(true);
+        setCategoryBarCategoriesError(false);
         const allCategories = await fetchCategoriesAndSetImages();
         if (inDetailsView && primaryActiveCategory) {
           setBaseCategories(allCategories.filter((cat) => cat.id !== primaryActiveCategory.id));
@@ -114,15 +137,20 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         }
       } catch (error) {
         console.error("Error fetching categories for category bar", error);
+        setCategoryBarCategoriesError(true);
+      } finally {
+        setCategoryBarCategoriesLoading(false);
       }
     };
 
     setCategoryBarCategories();
-  }, [inDetailsView, primaryActiveCategory]);
+  }, [primaryActiveCategory, inDetailsView]);
 
   useEffect(() => {
     const fetchAndUpdateCategoryBarCount = async () => {
       try {
+        setCategoryBarCountLoading(true);
+        setCategoryBarCountError(false);
         const updatedSearchText =
           searchText || detailsText
             ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
@@ -135,6 +163,9 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         setVisibleCategories(categories);
       } catch (error) {
         console.error("Failed to fetch category counts for categorybar", error);
+        setCategoryBarCountError(true);
+      } finally {
+        setCategoryBarCountLoading(false);
       }
     };
 
@@ -144,14 +175,16 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   return (
     <CategoryContext.Provider
       value={{
-        categoryIsLoading,
-        categoryHasError,
         inDetailsView,
         visibleCategories,
         primaryActiveCategory,
         setPrimaryActiveCategory,
         relatedActiveCategory,
         setRelatedActiveCategory,
+        landingPageCategoriesLoading,
+        landingPageCategoriesError,
+        categoryBarLoading,
+        categoryBarHasError,
       }}
     >
       {children}
diff --git a/src/types/types.ts b/src/types/types.ts
index e438b8f..739d99d 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -184,14 +184,16 @@ export type Category = {
 };
 
 export interface CategoryContextType {
-  categoryIsLoading: boolean;
-  categoryHasError: boolean;
   inDetailsView?: boolean;
   visibleCategories: Category[];
   primaryActiveCategory: Category | null;
   setPrimaryActiveCategory: (category: Category | null) => void;
   relatedActiveCategory: Category | null;
   setRelatedActiveCategory: (category: Category | null) => void;
+  landingPageCategoriesLoading: boolean;
+  landingPageCategoriesError: boolean;
+  categoryBarLoading: boolean;
+  categoryBarHasError: boolean;
 }
 
 // -------- SEARCH API ---------
-- 
GitLab


From bab7b642d2069233b7a0af0da15e879515bed6d9 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 27 Feb 2025 11:33:07 +0100
Subject: [PATCH 24/48] narrowed effect running conditions to manage context
 state and make it more streamlined and specific

---
 src/contexts/CategoryContext.tsx | 144 +++++++++++++++++++------------
 1 file changed, 89 insertions(+), 55 deletions(-)

diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 4e44030..9dc7465 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -1,5 +1,5 @@
 "use client";
-import { useSearchParams } from "next/navigation";
+import { useSearchParams, usePathname } from "next/navigation";
 import { createContext, useState, useEffect } from "react";
 
 import { DEFAULT_CATEGORY_CONTEXT } from "@/app/api/config/config";
@@ -16,6 +16,8 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
   const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
   const [inDetailsView, setInDetailsView] = useState<boolean>(false);
+  const [inResultsListView, setInResultsListView] = useState<boolean>(false);
+
   // Main flags for loading and error states
   const [landingPageCategoriesLoading, setLandingPageCategoriesLoading] = useState(false);
   const [landingPageCategoriesError, setLandingPageCategoriesError] = useState(false);
@@ -23,6 +25,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const [categoryBarHasError, setCategoryBarHasError] = useState<boolean>(false);
 
   // State for processing
+  const pathName = usePathname();
   const resultParams = useSearchParams();
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
@@ -35,21 +38,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const [categoryBarCountLoading, setCategoryBarCountLoading] = useState(false);
   const [categoryBarCountError, setCategoryBarCountError] = useState(false);
 
-  useEffect(() => {
-    if (!categoryBarCategoriesLoading && !categoryBarCountLoading) {
-      setCategoryBarLoading(false);
-      setCategoryBarHasError(categoryBarCategoriesError || categoryBarCountError);
-    } else {
-      setCategoryBarLoading(true);
-      setCategoryBarHasError(false);
-    }
-  }, [
-    categoryBarCategoriesLoading,
-    categoryBarCategoriesError,
-    categoryBarCountLoading,
-    categoryBarCountError,
-  ]);
-
   // Function to set the category images
   const fetchCategoriesAndSetImages = async () => {
     const allCategories = await getAllCategories();
@@ -68,7 +56,42 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     return categoriesClone;
   };
 
-  // Effects affecting the Landing page actions related to categories
+  // Effect to identify which page requires the context
+
+  useEffect(() => {
+    const pathParams = pathName.split(/(\/)+/g);
+    const hasResults = pathParams.includes("results");
+    const hasDetails = pathParams.includes("details");
+    if (hasResults && hasDetails) {
+      setInResultsListView(false);
+      setInDetailsView(true);
+    } else if (hasResults && !hasDetails) {
+      setInResultsListView(true);
+      setInDetailsView(false);
+    } else {
+      setInResultsListView(false);
+      setInDetailsView(false);
+    }
+  }, [pathName]);
+
+  // Effect to calculate error and loading state of the context
+
+  useEffect(() => {
+    if (!categoryBarCategoriesLoading && !categoryBarCountLoading) {
+      setCategoryBarLoading(false);
+      setCategoryBarHasError(categoryBarCategoriesError || categoryBarCountError);
+    } else {
+      setCategoryBarLoading(true);
+      setCategoryBarHasError(false);
+    }
+  }, [
+    categoryBarCategoriesLoading,
+    categoryBarCategoriesError,
+    categoryBarCountLoading,
+    categoryBarCountError,
+  ]);
+
+  // Effects affecting the Categories component in the Landing page
 
   useEffect(() => {
     const setLandingPageCategories = async () => {
@@ -86,35 +109,65 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
       }
     };
 
-    setLandingPageCategories();
-  }, []);
+    if (!inResultsListView && !inDetailsView) {
+      setLandingPageCategories();
+    }
+  }, [inResultsListView, inDetailsView]);
 
   // Effects affecting the CategoryBar component
 
   useEffect(() => {
-    if (detailsText) {
-      setInDetailsView(true);
-    } else {
-      setInDetailsView(false);
+    if (inResultsListView && !primaryActiveCategory) {
+      setPrimaryActiveCategory(visibleCategories[0]);
     }
-  }, [detailsText]);
+  }, [inDetailsView, inResultsListView, primaryActiveCategory, visibleCategories]);
 
   useEffect(() => {
-    if (!primaryActiveCategory && !inDetailsView) {
-      setPrimaryActiveCategory(visibleCategories[0]);
+    const setCategoryBarCategories = async () => {
+      setCategoryBarCategoriesLoading(true);
+      setCategoryBarCategoriesError(false);
+      try {
+        const allCategories = await fetchCategoriesAndSetImages();
+        if (inDetailsView) {
+          setBaseCategories(allCategories.filter((cat) => cat.id !== primaryActiveCategory?.id));
+        } else {
+          setBaseCategories(allCategories);
+        }
+      } catch (error) {
+        console.error("Error fetching categories for category bar", error);
+        setCategoryBarCategoriesError(true);
+      } finally {
+        setCategoryBarCategoriesLoading(false);
+      }
+    };
+    if (
+      (inResultsListView && baseCategories.length === 0) || // when going to the results page for the first time
+      inDetailsView || //every time details page is updated
+      (inResultsListView && visibleCategories.length < totalNoOfCategories) //every time we navigate from details page to results view
+    ) {
+      setCategoryBarCategories();
     }
-  }, [inDetailsView, primaryActiveCategory, visibleCategories]);
+  }, [
+    primaryActiveCategory,
+    inDetailsView,
+    inResultsListView,
+    baseCategories.length,
+    visibleCategories.length,
+    totalNoOfCategories,
+  ]);
 
   // Effect runs when navigating into details view or in details view
+
   useEffect(() => {
     if (inDetailsView) {
       setRelatedActiveCategory(visibleCategories[0]);
     }
   }, [visibleCategories, inDetailsView]);
 
-  // Effect runs when navigating out from details view
+  // Clean up Effect runs when navigating out from details view -
+
   useEffect(() => {
-    if (!inDetailsView && visibleCategories.length < totalNoOfCategories) {
+    if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
       // You are no longer in details view and have a search text but you are navigating from details to results view and visibile category is not yet updated
       // this state exists because inDetailsView is searchParam based that updates and maintains the most relevant state during navigation
       // but we are using the small gap during navigation wherein visibleCategories is being updated via the above effect,
@@ -122,34 +175,12 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
       setPrimaryActiveCategory(null);
       setRelatedActiveCategory(null);
     }
-  }, [inDetailsView, visibleCategories, totalNoOfCategories]);
-
-  useEffect(() => {
-    const setCategoryBarCategories = async () => {
-      try {
-        setCategoryBarCategoriesLoading(true);
-        setCategoryBarCategoriesError(false);
-        const allCategories = await fetchCategoriesAndSetImages();
-        if (inDetailsView && primaryActiveCategory) {
-          setBaseCategories(allCategories.filter((cat) => cat.id !== primaryActiveCategory.id));
-        } else {
-          setBaseCategories(allCategories);
-        }
-      } catch (error) {
-        console.error("Error fetching categories for category bar", error);
-        setCategoryBarCategoriesError(true);
-      } finally {
-        setCategoryBarCategoriesLoading(false);
-      }
-    };
-
-    setCategoryBarCategories();
-  }, [primaryActiveCategory, inDetailsView]);
+  }, [inResultsListView, visibleCategories, totalNoOfCategories]);
 
   useEffect(() => {
     const fetchAndUpdateCategoryBarCount = async () => {
       try {
-        setCategoryBarCountLoading(true);
+        setCategoryBarCountLoading(false);
         setCategoryBarCountError(false);
         const updatedSearchText =
           searchText || detailsText
@@ -162,6 +193,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
         setVisibleCategories(categories);
       } catch (error) {
+        //  TODO: How to handle error in case of API failure in this effect?
         console.error("Failed to fetch category counts for categorybar", error);
         setCategoryBarCountError(true);
       } finally {
@@ -169,8 +201,10 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
       }
     };
 
-    fetchAndUpdateCategoryBarCount();
-  }, [searchText, detailsText, baseCategories]);
+    if (inResultsListView || inDetailsView) {
+      fetchAndUpdateCategoryBarCount();
+    }
+  }, [searchText, detailsText, baseCategories, inResultsListView, inDetailsView]);
 
   return (
     <CategoryContext.Provider
-- 
GitLab


From 9b9c69ed2fe8463c38bef66ce89420104daa931e Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 27 Feb 2025 11:53:59 +0100
Subject: [PATCH 25/48] further simplified handling of loading and error states

---
 src/components/app/Categories.tsx             |  8 +--
 .../app/results/CategorizedResults.tsx        |  8 +--
 src/contexts/CategoryContext.tsx              | 62 +++++--------------
 src/types/types.ts                            |  6 +-
 4 files changed, 26 insertions(+), 58 deletions(-)

diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index 4f10216..d0bd0c9 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -12,8 +12,8 @@ import { Link } from "@/i18n/routing";
 const Categories = () => {
   const t = useTranslations("Categories");
   const {
-    landingPageCategoriesError,
-    landingPageCategoriesLoading,
+    categoryIsLoading,
+    categoryHasError,
     visibleCategories,
     setPrimaryActiveCategory,
     setRelatedActiveCategory,
@@ -30,9 +30,9 @@ const Categories = () => {
 
   return (
     <div className="grow flex flex-col">
-      {landingPageCategoriesLoading ? (
+      {categoryIsLoading ? (
         <Spinner />
-      ) : landingPageCategoriesError ? (
+      ) : categoryHasError ? (
         <Error />
       ) : (
         <div className="grow container mx-auto flex flex-col justify-center pb-16 md:p-14 md:pt-2 lg:p-20 lg:pt-10 xl:pt-10 xl:pb-20 2xl:pb-16 3xl:py-0">
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 4b17c7e..860a742 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -14,8 +14,8 @@ const CategorizedResultsComponent = () => {
   const [activeCategory, setActiveCategory] = useState<Category>();
   const [resultsLoading, setResultsLoading] = useState(false);
   const {
-    categoryBarLoading,
-    categoryBarHasError,
+    categoryIsLoading,
+    categoryHasError,
     inDetailsView,
     primaryActiveCategory,
     relatedActiveCategory,
@@ -35,9 +35,9 @@ const CategorizedResultsComponent = () => {
 
   return (
     <>
-      {categoryBarLoading ? (
+      {categoryIsLoading ? (
         <Spinner />
-      ) : categoryBarHasError ? (
+      ) : categoryHasError ? (
         <Error />
       ) : (
         <>
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 9dc7465..33874ad 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -16,27 +16,19 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
   const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
   const [inDetailsView, setInDetailsView] = useState<boolean>(false);
-  const [inResultsListView, setInResultsListView] = useState<boolean>(false);
 
   // Main flags for loading and error states
-  const [landingPageCategoriesLoading, setLandingPageCategoriesLoading] = useState(false);
-  const [landingPageCategoriesError, setLandingPageCategoriesError] = useState(false);
-  const [categoryBarLoading, setCategoryBarLoading] = useState<boolean>(false);
-  const [categoryBarHasError, setCategoryBarHasError] = useState<boolean>(false);
+  const [categoryIsLoading, setCategoryIsLoading] = useState(false);
+  const [categoryHasError, setCategoryHasError] = useState(false);
 
-  // State for processing
+  // Unshared state for local processing of shared state above
   const pathName = usePathname();
   const resultParams = useSearchParams();
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
   const [totalNoOfCategories, setTotalNoOfCategories] = useState<number>(0);
   const [baseCategories, setBaseCategories] = useState<Category[]>([]);
-
-  // Loading and error states for different API calls
-  const [categoryBarCategoriesLoading, setCategoryBarCategoriesLoading] = useState(false);
-  const [categoryBarCategoriesError, setCategoryBarCategoriesError] = useState(false);
-  const [categoryBarCountLoading, setCategoryBarCountLoading] = useState(false);
-  const [categoryBarCountError, setCategoryBarCountError] = useState(false);
+  const [inResultsListView, setInResultsListView] = useState<boolean>(false);
 
   // Function to set the category images
   const fetchCategoriesAndSetImages = async () => {
@@ -74,38 +66,23 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [pathName]);
 
-  // Effect to calculate error and loading state of the context
-
-  useEffect(() => {
-    if (!categoryBarCategoriesLoading && !categoryBarCountLoading) {
-      setCategoryBarLoading(false);
-      setCategoryBarHasError(categoryBarCategoriesError || categoryBarCountError);
-    } else {
-      setCategoryBarLoading(true);
-      setCategoryBarHasError(false);
-    }
-  }, [
-    categoryBarCategoriesLoading,
-    categoryBarCategoriesError,
-    categoryBarCountLoading,
-    categoryBarCountError,
-  ]);
-
   // Effects affecting the Categories component in the Landing page
 
   useEffect(() => {
     const setLandingPageCategories = async () => {
       try {
-        setLandingPageCategoriesLoading(true);
-        setLandingPageCategoriesError(false);
+        setCategoryIsLoading(true);
+        setCategoryHasError(false);
         const allCategories = await fetchCategoriesAndSetImages();
         setVisibleCategories(allCategories);
         setTotalNoOfCategories(allCategories.length);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
-        setLandingPageCategoriesError(true);
+        setCategoryHasError(true);
       } finally {
-        setLandingPageCategoriesLoading(false);
+        setPrimaryActiveCategory(null);
+        setRelatedActiveCategory(null);
+        setCategoryIsLoading(false);
       }
     };
 
@@ -124,8 +101,8 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
 
   useEffect(() => {
     const setCategoryBarCategories = async () => {
-      setCategoryBarCategoriesLoading(true);
-      setCategoryBarCategoriesError(false);
+      setCategoryIsLoading(true);
+      setCategoryHasError(false);
       try {
         const allCategories = await fetchCategoriesAndSetImages();
         if (inDetailsView) {
@@ -135,9 +112,9 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         }
       } catch (error) {
         console.error("Error fetching categories for category bar", error);
-        setCategoryBarCategoriesError(true);
+        setCategoryHasError(true);
       } finally {
-        setCategoryBarCategoriesLoading(false);
+        setCategoryIsLoading(false);
       }
     };
     if (
@@ -180,8 +157,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   useEffect(() => {
     const fetchAndUpdateCategoryBarCount = async () => {
       try {
-        setCategoryBarCountLoading(false);
-        setCategoryBarCountError(false);
         const updatedSearchText =
           searchText || detailsText
             ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
@@ -195,9 +170,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
       } catch (error) {
         //  TODO: How to handle error in case of API failure in this effect?
         console.error("Failed to fetch category counts for categorybar", error);
-        setCategoryBarCountError(true);
-      } finally {
-        setCategoryBarCountLoading(false);
       }
     };
 
@@ -215,10 +187,8 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         setPrimaryActiveCategory,
         relatedActiveCategory,
         setRelatedActiveCategory,
-        landingPageCategoriesLoading,
-        landingPageCategoriesError,
-        categoryBarLoading,
-        categoryBarHasError,
+        categoryIsLoading,
+        categoryHasError,
       }}
     >
       {children}
diff --git a/src/types/types.ts b/src/types/types.ts
index 739d99d..94184b8 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -190,10 +190,8 @@ export interface CategoryContextType {
   setPrimaryActiveCategory: (category: Category | null) => void;
   relatedActiveCategory: Category | null;
   setRelatedActiveCategory: (category: Category | null) => void;
-  landingPageCategoriesLoading: boolean;
-  landingPageCategoriesError: boolean;
-  categoryBarLoading: boolean;
-  categoryBarHasError: boolean;
+  categoryIsLoading: boolean;
+  categoryHasError: boolean;
 }
 
 // -------- SEARCH API ---------
-- 
GitLab


From a6da14afbc8be124f1588850d36e6311532c41f8 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 27 Feb 2025 13:36:41 +0100
Subject: [PATCH 26/48] more simplification of state setting and selected
 running of effects under the right conditions

---
 src/components/app/Categories.tsx | 16 ++-------
 src/contexts/CategoryContext.tsx  | 54 +++++++++++++++----------------
 2 files changed, 30 insertions(+), 40 deletions(-)

diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index d0bd0c9..faede8c 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -2,7 +2,7 @@
 
 import Image from "next/image";
 import { useTranslations } from "next-intl";
-import { useContext, useEffect } from "react";
+import { useContext } from "react";
 
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
@@ -11,18 +11,8 @@ import { Link } from "@/i18n/routing";
 
 const Categories = () => {
   const t = useTranslations("Categories");
-  const {
-    categoryIsLoading,
-    categoryHasError,
-    visibleCategories,
-    setPrimaryActiveCategory,
-    setRelatedActiveCategory,
-  } = useContext(CategoryContext);
-
-  useEffect(() => {
-    setRelatedActiveCategory(null);
-    setPrimaryActiveCategory(null);
-  }, [setPrimaryActiveCategory, setRelatedActiveCategory]);
+  const { categoryIsLoading, categoryHasError, visibleCategories, setPrimaryActiveCategory } =
+    useContext(CategoryContext);
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
   /* traditional JS module that makes the badge numbers appear as strings representing exponents
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 33874ad..2906572 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -97,7 +97,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     if (inResultsListView && !primaryActiveCategory) {
       setPrimaryActiveCategory(visibleCategories[0]);
     }
-  }, [inDetailsView, inResultsListView, primaryActiveCategory, visibleCategories]);
+  }, [inResultsListView, primaryActiveCategory, visibleCategories]);
 
   useEffect(() => {
     const setCategoryBarCategories = async () => {
@@ -133,8 +133,33 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     totalNoOfCategories,
   ]);
 
-  // Effect runs when navigating into details view or in details view
+  useEffect(() => {
+    const fetchAndUpdateCategoryBarCount = async () => {
+      if (baseCategories.length !== 0) {
+        try {
+          const updatedSearchText =
+            searchText || detailsText
+              ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
+              : undefined;
+
+          const categoryCounts = await getCategoryCounts(updatedSearchText);
+          const categories = structuredClone(baseCategories);
+
+          categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
+          setVisibleCategories(categories);
+        } catch (error) {
+          //  TODO: How to handle error in case of API failure in this effect?
+          console.error("Failed to fetch category counts for categorybar", error);
+        }
+      }
+    };
+
+    if (inResultsListView || inDetailsView) {
+      fetchAndUpdateCategoryBarCount();
+    }
+  }, [searchText, detailsText, baseCategories, inResultsListView, inDetailsView]);
 
+  // Effect runs when navigating into details view or in details view
   useEffect(() => {
     if (inDetailsView) {
       setRelatedActiveCategory(visibleCategories[0]);
@@ -142,7 +167,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   }, [visibleCategories, inDetailsView]);
 
   // Clean up Effect runs when navigating out from details view -
-
   useEffect(() => {
     if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
       // You are no longer in details view and have a search text but you are navigating from details to results view and visibile category is not yet updated
@@ -154,30 +178,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [inResultsListView, visibleCategories, totalNoOfCategories]);
 
-  useEffect(() => {
-    const fetchAndUpdateCategoryBarCount = async () => {
-      try {
-        const updatedSearchText =
-          searchText || detailsText
-            ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
-            : undefined;
-
-        const categoryCounts = await getCategoryCounts(updatedSearchText);
-        const categories = structuredClone(baseCategories);
-
-        categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
-        setVisibleCategories(categories);
-      } catch (error) {
-        //  TODO: How to handle error in case of API failure in this effect?
-        console.error("Failed to fetch category counts for categorybar", error);
-      }
-    };
-
-    if (inResultsListView || inDetailsView) {
-      fetchAndUpdateCategoryBarCount();
-    }
-  }, [searchText, detailsText, baseCategories, inResultsListView, inDetailsView]);
-
   return (
     <CategoryContext.Provider
       value={{
-- 
GitLab


From ea235ff62f067bcdd7aa7a63cef6d7a9e1c77fbf Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 27 Feb 2025 13:42:59 +0100
Subject: [PATCH 27/48] cleaned, properly lined and generally prettified
 context file

---
 src/contexts/CategoryContext.tsx | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 2906572..4dba1c4 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -93,12 +93,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
 
   // Effects affecting the CategoryBar component
 
-  useEffect(() => {
-    if (inResultsListView && !primaryActiveCategory) {
-      setPrimaryActiveCategory(visibleCategories[0]);
-    }
-  }, [inResultsListView, primaryActiveCategory, visibleCategories]);
-
   useEffect(() => {
     const setCategoryBarCategories = async () => {
       setCategoryIsLoading(true);
@@ -120,7 +114,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     if (
       (inResultsListView && baseCategories.length === 0) || // when going to the results page for the first time
       inDetailsView || //every time details page is updated
-      (inResultsListView && visibleCategories.length < totalNoOfCategories) //every time we navigate from details page to results view
+      (inResultsListView && visibleCategories.length < totalNoOfCategories) //every time we navigate away from details page to results view
     ) {
       setCategoryBarCategories();
     }
@@ -159,14 +153,20 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [searchText, detailsText, baseCategories, inResultsListView, inDetailsView]);
 
-  // Effect runs when navigating into details view or in details view
+  // Effects for setting default values
+  useEffect(() => {
+    if (inResultsListView && !primaryActiveCategory) {
+      setPrimaryActiveCategory(visibleCategories[0]);
+    }
+  }, [inResultsListView, primaryActiveCategory, visibleCategories]);
+
   useEffect(() => {
     if (inDetailsView) {
       setRelatedActiveCategory(visibleCategories[0]);
     }
   }, [visibleCategories, inDetailsView]);
 
-  // Clean up Effect runs when navigating out from details view -
+  // Clean up Effect runs when navigating away from details view
   useEffect(() => {
     if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
       // You are no longer in details view and have a search text but you are navigating from details to results view and visibile category is not yet updated
-- 
GitLab


From ff3615afb884f99d2ce0072f6a2e25953e63e8df Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 27 Feb 2025 14:28:35 +0100
Subject: [PATCH 28/48] removed loading state in list results to simplify state
 handling in UI

---
 .../app/results/CategorizedResults.tsx        | 19 +++------------
 src/components/app/results/ListResults.tsx    | 24 ++++++-------------
 src/utils/apiCall.ts                          |  2 --
 3 files changed, 10 insertions(+), 35 deletions(-)

diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 860a742..ccfcf84 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -12,7 +12,7 @@ import { Category } from "@/types/types";
 
 const CategorizedResultsComponent = () => {
   const [activeCategory, setActiveCategory] = useState<Category>();
-  const [resultsLoading, setResultsLoading] = useState(false);
+
   const {
     categoryIsLoading,
     categoryHasError,
@@ -44,13 +44,7 @@ const CategorizedResultsComponent = () => {
           {inDetailsView ? (
             <>
               <CategoryBar activeCategory={activeCategory} />
-              {activeCategory && (
-                <ListResults
-                  activeCategory={activeCategory}
-                  resultsLoading={resultsLoading}
-                  setResultsLoading={setResultsLoading}
-                />
-              )}
+              {activeCategory && <ListResults activeCategory={activeCategory} />}
             </>
           ) : (
             <>
@@ -60,14 +54,7 @@ const CategorizedResultsComponent = () => {
                   <BackBar />
                   <MobileSearch />
                 </>
-
-                {activeCategory && (
-                  <ListResults
-                    activeCategory={activeCategory}
-                    resultsLoading={resultsLoading}
-                    setResultsLoading={setResultsLoading}
-                  />
-                )}
+                {activeCategory && <ListResults activeCategory={activeCategory} />}
               </div>
             </>
           )}
diff --git a/src/components/app/results/ListResults.tsx b/src/components/app/results/ListResults.tsx
index 59100f3..960e449 100644
--- a/src/components/app/results/ListResults.tsx
+++ b/src/components/app/results/ListResults.tsx
@@ -2,7 +2,7 @@
 import { useSetAtom } from "jotai";
 import { useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
-import React, { Suspense, useEffect, useState, Dispatch, SetStateAction } from "react";
+import React, { Suspense, useEffect, useState } from "react";
 
 import Filters from "@/components/app/results/ListResults/Filters";
 import NoResults from "@/components/app/results/ListResults/NoResults";
@@ -16,23 +16,22 @@ import { detailsItemAtom } from "@/utils/atoms";
 
 type Props = {
   activeCategory: Category;
-  resultsLoading: boolean;
-  setResultsLoading: Dispatch<SetStateAction<boolean>>;
 };
 
-const ListResultsComponent = ({ activeCategory, resultsLoading, setResultsLoading }: Props) => {
+const ListResultsComponent = ({ activeCategory }: Props) => {
   const t = useTranslations("ListResults");
   const s = useTranslations("Categories");
 
   const setDetailsItem = useSetAtom(detailsItemAtom);
 
-  const [hasError, setHasError] = useState(false);
   const [start, setStart] = useState(0);
   const [results, setResults] = useState<ResultItemType[]>([]);
   const [searchCount, setSearchCount] = useState(0);
   const [availableFilters, setAvailableFilters] = useState<FilterEntity[]>([]);
   const [selectedFilters, setSelectedFilters] = useState<FilterEntity[]>([]);
 
+  const [hasError, setHasError] = useState(false);
+
   const resultParams = useSearchParams();
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
@@ -95,28 +94,19 @@ const ListResultsComponent = ({ activeCategory, resultsLoading, setResultsLoadin
         }
         setSearchCount(searchResult.totalCount);
         setAvailableFilters(searchResult.filters);
+        setHasError(false);
       } catch (error) {
         console.log("Error on updating result list", error);
         setHasError(true);
-      } finally {
-        setResultsLoading(false);
       }
     };
 
     updateResultList().catch(console.error);
-  }, [
-    activeCategory,
-    searchText,
-    detailsText,
-    start,
-    resultsPerPage,
-    selectedFilters,
-    setResultsLoading,
-  ]);
+  }, [activeCategory, searchText, detailsText, start, resultsPerPage, selectedFilters]);
 
   return (
     <>
-      {resultsLoading ? null : hasError ? (
+      {hasError ? (
         <Error />
       ) : (
         <div
diff --git a/src/utils/apiCall.ts b/src/utils/apiCall.ts
index ed95087..0953e23 100644
--- a/src/utils/apiCall.ts
+++ b/src/utils/apiCall.ts
@@ -47,7 +47,6 @@ export async function getAllCategories(): Promise<Category[]> {
   // You can return Date, Map, Set, etc.
 
   if (!res.ok) {
-    // This will activate the closest `error.js` Error Boundary
     throw new Error("Failed to fetch data");
   }
   return res.json();
@@ -79,7 +78,6 @@ export async function search(
   try {
     return await searchAPI(searchText, documentType, rows, start, filters);
   } catch (error) {
-    // TODO: Error handling
     throw error;
   }
 }
-- 
GitLab


From d6b5685d76645c353a7746bdc83cd8fce9bbc473 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 27 Feb 2025 16:31:56 +0100
Subject: [PATCH 29/48] comment fixes

---
 src/contexts/CategoryContext.tsx | 5 +----
 src/i18n/routing.ts              | 3 ++-
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 4dba1c4..238e588 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -169,10 +169,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   // Clean up Effect runs when navigating away from details view
   useEffect(() => {
     if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
-      // You are no longer in details view and have a search text but you are navigating from details to results view and visibile category is not yet updated
-      // this state exists because inDetailsView is searchParam based that updates and maintains the most relevant state during navigation
-      // but we are using the small gap during navigation wherein visibleCategories is being updated via the above effect,
-      // this intermediate state points to us that we navigated from the details view
+      // Taking advantage of the short period of time until the visiblecategories need to be fetched and updated for the UI
       setPrimaryActiveCategory(null);
       setRelatedActiveCategory(null);
     }
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
index ee5d06d..14e403e 100644
--- a/src/i18n/routing.ts
+++ b/src/i18n/routing.ts
@@ -4,7 +4,8 @@ import { defineRouting, LocalePrefix } from "next-intl/routing";
 export const i18nConfig = {
   locales: ["en", "de"],
   defaultLocale: "en",
-  // the following setting causes surprising side effects of category bar does not show the right language in details view
+  // the following setting causes surprising side effects of category bar not showing  the right language in details view
+  // TODO: Why is this happening? Discuss why locale detection shoube be false
   // localeDetection: false,
   localePrefix: "as-needed" as LocalePrefix,
 };
-- 
GitLab


From 631cac369b0cef43eecba8789e4163f2b53ae840 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Thu, 27 Feb 2025 17:05:50 +0100
Subject: [PATCH 30/48] updated default category context values

---
 src/app/api/config/config.ts | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/app/api/config/config.ts b/src/app/api/config/config.ts
index 109363e..b2562e3 100644
--- a/src/app/api/config/config.ts
+++ b/src/app/api/config/config.ts
@@ -61,13 +61,14 @@ export const INITIAL_CATECORY_COUNT_MAP: Record<FacetFieldKeys, number> = {
 };
 
 export const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
+  inDetailsView: false,
   visibleCategories: [],
-  setVisibleCategories: () => {},
   primaryActiveCategory: null,
   setPrimaryActiveCategory: () => {},
   relatedActiveCategory: null,
   setRelatedActiveCategory: () => {},
-  inDetailsView: false,
+  categoryIsLoading: false,
+  categoryHasError: false,
 };
 
 export const AVAILABLE_FACETS: string[] = [
-- 
GitLab


From 4714018c08b65675718b5fdbdf36178493eceeef Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 4 Mar 2025 15:03:05 +0100
Subject: [PATCH 31/48] turned related active category into a local state that
 is managed as props, while maintaining default categories and counts

---
 src/app/api/config/config.ts                  |   5 +-
 src/components/app/Categories.tsx             |  20 +-
 .../app/results/CategorizedResults.tsx        |  79 +++++-
 src/components/app/results/CategoryBar.tsx    |  25 +-
 src/components/app/results/ListResults.tsx    |  12 +-
 .../app/results/ListResults/ResultItem.tsx    |  11 +-
 src/components/layout/Header.tsx              |   5 +-
 src/contexts/CategoryContext.tsx              | 226 ++++++++----------
 src/types/types.ts                            |  12 +-
 9 files changed, 231 insertions(+), 164 deletions(-)

diff --git a/src/app/api/config/config.ts b/src/app/api/config/config.ts
index b2562e3..0197fc6 100644
--- a/src/app/api/config/config.ts
+++ b/src/app/api/config/config.ts
@@ -62,11 +62,8 @@ export const INITIAL_CATECORY_COUNT_MAP: Record<FacetFieldKeys, number> = {
 
 export const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
   inDetailsView: false,
-  visibleCategories: [],
-  primaryActiveCategory: null,
+  categories: [],
   setPrimaryActiveCategory: () => {},
-  relatedActiveCategory: null,
-  setRelatedActiveCategory: () => {},
   categoryIsLoading: false,
   categoryHasError: false,
 };
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index faede8c..f9a8113 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -2,7 +2,7 @@
 
 import Image from "next/image";
 import { useTranslations } from "next-intl";
-import { useContext } from "react";
+import { useContext, useEffect } from "react";
 
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
@@ -11,8 +11,20 @@ import { Link } from "@/i18n/routing";
 
 const Categories = () => {
   const t = useTranslations("Categories");
-  const { categoryIsLoading, categoryHasError, visibleCategories, setPrimaryActiveCategory } =
-    useContext(CategoryContext);
+  const {
+    categoryIsLoading,
+    categoryHasError,
+    categories,
+    primaryActiveCategory,
+    setPrimaryActiveCategory,
+  } = useContext(CategoryContext);
+
+  // When this component loads the primary active category should be reset to null - for a fresh slate
+  useEffect(() => {
+    if (primaryActiveCategory) {
+      setPrimaryActiveCategory();
+    }
+  }, [primaryActiveCategory, setPrimaryActiveCategory]);
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
   /* traditional JS module that makes the badge numbers appear as strings representing exponents
@@ -31,7 +43,7 @@ const Categories = () => {
           </div>
           {/* grid of categories */}
           <div className="px-10 grid grid-cols-1  md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 gap-y-28 md:gap-x-16 lg:gap-x-32 xl:gap-x-46 content-center justify-center">
-            {visibleCategories.map((o) => {
+            {categories.map((o) => {
               return (
                 <Link
                   key={o.id}
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index ccfcf84..3e7737e 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -1,4 +1,5 @@
 "use client";
+import { useSearchParams, usePathname } from "next/navigation";
 import React, { useState, useContext, useEffect, Suspense } from "react";
 
 import BackBar from "@/components/app/results/BackBar";
@@ -11,28 +12,80 @@ import { CategoryContext } from "@/contexts/CategoryContext";
 import { Category } from "@/types/types";
 
 const CategorizedResultsComponent = () => {
+  const pathName = usePathname();
   const [activeCategory, setActiveCategory] = useState<Category>();
+  const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
+  const [inDetailsView, setInDetailsView] = useState<boolean>(false);
 
   const {
     categoryIsLoading,
     categoryHasError,
-    inDetailsView,
+    categories,
     primaryActiveCategory,
-    relatedActiveCategory,
+    setPrimaryActiveCategory,
   } = useContext(CategoryContext);
 
+  const resultParams = useSearchParams();
+  const searchText = resultParams.get("searchText") ?? undefined;
+  const detailsText = resultParams.get("detailsText") ?? undefined;
+  const paramsCategory = resultParams.get("category") ?? undefined;
+
+  // Figuring out which view is calling this component
   useEffect(() => {
-    if (inDetailsView && relatedActiveCategory !== null) {
-      setActiveCategory(relatedActiveCategory);
+    const pathParams = pathName.split(/(\/)+/g);
+    const hasResults = pathParams.includes("results");
+    const hasDetails = pathParams.includes("details");
+    if (hasResults && hasDetails) {
+      setInDetailsView(true);
+    } else if (hasResults && !hasDetails) {
+      setInDetailsView(false);
+    } else {
+      setInDetailsView(false);
     }
-  }, [inDetailsView, relatedActiveCategory]);
+  }, [pathName]);
 
+  // Setting default values for primaryActiveCategory and relatedActiveCategory when necessary
+
+  // sets default primaryActiveCategory in Results View
   useEffect(() => {
-    if (!inDetailsView && primaryActiveCategory !== null) {
+    if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
+      // in case you navigate to the results view by copy pasting the right url
+      const setCat = categories.filter((o) => o.id === paramsCategory)[0];
+      setPrimaryActiveCategory(setCat);
+    } else if (!inDetailsView && !primaryActiveCategory) {
+      setPrimaryActiveCategory(categories[0]);
+    }
+  }, [inDetailsView, primaryActiveCategory, categories, paramsCategory, setPrimaryActiveCategory]);
+
+  // sets the default relatedActiveCategory in Details View
+  useEffect(() => {
+    // TODO: fix the following
+    const isSameCat = paramsCategory === categories[0].id;
+    if (inDetailsView && !relatedActiveCategory && isSameCat) {
+      const filteredCategories = categories.filter((o) => o.id !== paramsCategory);
+      setRelatedActiveCategory(filteredCategories[0]);
+      setActiveCategory(filteredCategories[0]);
+    } else if (inDetailsView && !relatedActiveCategory) {
+      setRelatedActiveCategory(categories[0]);
+      setActiveCategory(categories[0]);
+    }
+  }, [inDetailsView, relatedActiveCategory, categories, paramsCategory]);
+
+  // Setting active values for view.
+  // Required to handle null and undefined states of primaryActiveCategory and relatedActiveCategory
+
+  useEffect(() => {
+    if (!inDetailsView && primaryActiveCategory) {
       setActiveCategory(primaryActiveCategory);
     }
   }, [inDetailsView, primaryActiveCategory]);
 
+  useEffect(() => {
+    if (inDetailsView && relatedActiveCategory) {
+      setActiveCategory(relatedActiveCategory);
+    }
+  }, [inDetailsView, relatedActiveCategory]);
+
   return (
     <>
       {categoryIsLoading ? (
@@ -43,8 +96,18 @@ const CategorizedResultsComponent = () => {
         <>
           {inDetailsView ? (
             <>
-              <CategoryBar activeCategory={activeCategory} />
-              {activeCategory && <ListResults activeCategory={activeCategory} />}
+              <CategoryBar
+                activeCategory={activeCategory}
+                inDetailsView={inDetailsView}
+                setRelatedActiveCategory={setRelatedActiveCategory}
+              />
+              {activeCategory && (
+                <ListResults
+                  activeCategory={activeCategory}
+                  inDetailsView={inDetailsView}
+                  setRelatedActiveCategory={setRelatedActiveCategory}
+                />
+              )}
             </>
           ) : (
             <>
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index bf6cbc1..2cfb594 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -2,7 +2,7 @@
 
 import { useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { Suspense, useContext } from "react";
+import { SetStateAction, Suspense, useContext, Dispatch } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
@@ -10,9 +10,15 @@ import type { Category } from "@/types/types";
 
 type Props = {
   activeCategory?: Category;
+  inDetailsView?: boolean;
+  setRelatedActiveCategory?: Dispatch<SetStateAction<Category | null>>;
 };
 
-const CategoryBarComponent = ({ activeCategory }: Props) => {
+const CategoryBarComponent = ({
+  activeCategory,
+  inDetailsView,
+  setRelatedActiveCategory,
+}: Props) => {
   const c = useTranslations("Categories");
 
   const resultParams = useSearchParams();
@@ -23,18 +29,13 @@ const CategoryBarComponent = ({ activeCategory }: Props) => {
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
 
-  const {
-    visibleCategories,
-    primaryActiveCategory,
-    inDetailsView,
-    setPrimaryActiveCategory,
-    setRelatedActiveCategory,
-  } = useContext(CategoryContext);
+  const { categories, primaryActiveCategory, setPrimaryActiveCategory } =
+    useContext(CategoryContext);
 
   return (
     <>
       <div className="bg-primary-helmholtz-dunkelblau text-primary-helmholtz-weiss grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:flex gap-y-2 gap-x-2 xl:gap-x-6 px-4 pt-2 pb-3 md:px-10 lg:px-16 xl:pt-4 xl:min-h-24">
-        {visibleCategories.map((o, idx) => (
+        {categories.map((o, idx) => (
           <Link
             href={
               inDetailsView
@@ -44,7 +45,9 @@ const CategoryBarComponent = ({ activeCategory }: Props) => {
             shallow
             replace={!!(inDetailsView && activeCategory?.id !== primaryActiveCategory?.id)}
             onClick={() =>
-              inDetailsView ? setRelatedActiveCategory(o) : setPrimaryActiveCategory(o)
+              inDetailsView && setRelatedActiveCategory
+                ? setRelatedActiveCategory(o)
+                : setPrimaryActiveCategory(o)
             }
             key={idx}
             className={`${
diff --git a/src/components/app/results/ListResults.tsx b/src/components/app/results/ListResults.tsx
index 960e449..c678063 100644
--- a/src/components/app/results/ListResults.tsx
+++ b/src/components/app/results/ListResults.tsx
@@ -2,7 +2,7 @@
 import { useSetAtom } from "jotai";
 import { useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
-import React, { Suspense, useEffect, useState } from "react";
+import React, { Dispatch, SetStateAction, Suspense, useEffect, useState } from "react";
 
 import Filters from "@/components/app/results/ListResults/Filters";
 import NoResults from "@/components/app/results/ListResults/NoResults";
@@ -16,9 +16,15 @@ import { detailsItemAtom } from "@/utils/atoms";
 
 type Props = {
   activeCategory: Category;
+  inDetailsView?: boolean;
+  setRelatedActiveCategory?: Dispatch<SetStateAction<Category | null>>;
 };
 
-const ListResultsComponent = ({ activeCategory }: Props) => {
+const ListResultsComponent = ({
+  activeCategory,
+  inDetailsView,
+  setRelatedActiveCategory,
+}: Props) => {
   const t = useTranslations("ListResults");
   const s = useTranslations("Categories");
 
@@ -161,6 +167,8 @@ const ListResultsComponent = ({ activeCategory }: Props) => {
                     activeCategory={activeCategory}
                     searchText={searchText}
                     associatedToFilters={availableFilters.length !== 0}
+                    inDetailsView={inDetailsView}
+                    setRelatedActiveCategory={setRelatedActiveCategory}
                   />
                 ))
               ) : (
diff --git a/src/components/app/results/ListResults/ResultItem.tsx b/src/components/app/results/ListResults/ResultItem.tsx
index dbdae8f..aa125d2 100644
--- a/src/components/app/results/ListResults/ResultItem.tsx
+++ b/src/components/app/results/ListResults/ResultItem.tsx
@@ -4,7 +4,7 @@ import { useSetAtom } from "jotai";
 import Image from "next/image";
 import { useRouter } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { useEffect, useRef, useState, useContext } from "react";
+import { useEffect, useRef, useState, useContext, Dispatch, SetStateAction } from "react";
 
 import { INSTITUTE_ROR_LOGOS } from "@/app/api/config/config";
 import ExternalLink from "@/components/ExternalLink";
@@ -22,7 +22,9 @@ type Props = {
   searchText?: string;
   activeCategory?: Category;
   associatedToFilters?: boolean;
+  inDetailsView?: boolean;
   isDetailsCard?: boolean;
+  setRelatedActiveCategory?: Dispatch<SetStateAction<Category | null>>;
 };
 
 const ResultItem = ({
@@ -32,6 +34,8 @@ const ResultItem = ({
   activeCategory,
   associatedToFilters,
   isDetailsCard,
+  inDetailsView,
+  setRelatedActiveCategory,
 }: Props) => {
   const setDetailsItem = useSetAtom(detailsItemAtom);
   const router = useRouter();
@@ -44,8 +48,7 @@ const ResultItem = ({
   const [titleIconHref, setTitleIconHref] = useState<string>("");
   const [logoHref, setLogoHref] = useState<string>("");
 
-  const { inDetailsView, setPrimaryActiveCategory, setRelatedActiveCategory } =
-    useContext(CategoryContext);
+  const { setPrimaryActiveCategory } = useContext(CategoryContext);
 
   useEffect(() => {
     const element = descRef.current;
@@ -131,7 +134,7 @@ const ResultItem = ({
   const handleDetailSearch = () => {
     setDetailsItem((prev) => [...prev, ...[item]]);
 
-    if (inDetailsView && activeCategory) {
+    if (inDetailsView && activeCategory && setRelatedActiveCategory) {
       // setting currently chosen relatedActiveCategory, which in this case is the activeCategory, as the primaryActiveCategory
       setPrimaryActiveCategory(activeCategory);
       // cleaning the slate on relatedActiveCategory
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index 3e5f384..eb5561e 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -13,11 +13,10 @@ const Header = () => {
   const t = useTranslations("Search");
   const s = useTranslations("Intro");
 
-  const { setPrimaryActiveCategory, setRelatedActiveCategory } = useContext(CategoryContext);
+  const { setPrimaryActiveCategory } = useContext(CategoryContext);
 
   const handleReset = () => {
-    setPrimaryActiveCategory(null);
-    setRelatedActiveCategory(null);
+    setPrimaryActiveCategory();
   };
 
   return (
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 238e588..3872998 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -1,10 +1,10 @@
 "use client";
-import { useSearchParams, usePathname } from "next/navigation";
+import { useSearchParams } from "next/navigation";
 import { createContext, useState, useEffect } from "react";
 
-import { DEFAULT_CATEGORY_CONTEXT } from "@/app/api/config/config";
-import { Category, CategoryContextType } from "@/types/types";
-import { getAllCategories, getCategoryCounts } from "@/utils/apiCall";
+import { DEFAULT_CATEGORY_CONTEXT, INITIAL_CATECORY_COUNT_MAP } from "@/app/api/config/config";
+import { Category, CategoryContextType, FacetFieldKeys } from "@/types/types";
+import { getAllCategories } from "@/utils/apiCall";
 
 // Creating a context for the category
 export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGORY_CONTEXT);
@@ -12,23 +12,26 @@ export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGO
 // Creating a provider for the category
 export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
   // The state that will be shared
-  const [visibleCategories, setVisibleCategories] = useState<Category[]>([]);
-  const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
-  const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
-  const [inDetailsView, setInDetailsView] = useState<boolean>(false);
+  const [categories, setCategories] = useState<Category[]>([]);
+  const [categoryDefaultCounts, setCategoryDefaultCounts] = useState<
+    Record<FacetFieldKeys, number>
+  >(INITIAL_CATECORY_COUNT_MAP);
+  const [categoryCountsForSearch, setCategoryCountsForSearch] = useState<
+    Record<FacetFieldKeys, number>
+  >(INITIAL_CATECORY_COUNT_MAP);
+  const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category>();
 
   // Main flags for loading and error states
   const [categoryIsLoading, setCategoryIsLoading] = useState(false);
   const [categoryHasError, setCategoryHasError] = useState(false);
 
   // Unshared state for local processing of shared state above
-  const pathName = usePathname();
   const resultParams = useSearchParams();
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
-  const [totalNoOfCategories, setTotalNoOfCategories] = useState<number>(0);
-  const [baseCategories, setBaseCategories] = useState<Category[]>([]);
-  const [inResultsListView, setInResultsListView] = useState<boolean>(false);
+  const category = resultParams.get("category") ?? undefined;
+
+  const [defaultCategories, setDefaultCategories] = useState<Category[]>([]);
 
   // Function to set the category images
   const fetchCategoriesAndSetImages = async () => {
@@ -48,142 +51,121 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     return categoriesClone;
   };
 
-  // Effect to identify which page requires the context
-
-  useEffect(() => {
-    const pathParams = pathName.split(/(\/)+/g);
-    const hasResults = pathParams.includes("results");
-    const hasDetails = pathParams.includes("details");
-    if (hasResults && hasDetails) {
-      setInResultsListView(false);
-      setInDetailsView(true);
-    } else if (hasResults && !hasDetails) {
-      setInResultsListView(true);
-      setInDetailsView(false);
-    } else {
-      setInResultsListView(false);
-      setInDetailsView(false);
-    }
-  }, [pathName]);
-
   // Effects affecting the Categories component in the Landing page
 
   useEffect(() => {
-    const setLandingPageCategories = async () => {
+    const setDefaultCategoriesAndCounts = async () => {
       try {
         setCategoryIsLoading(true);
         setCategoryHasError(false);
+        // get all categories
         const allCategories = await fetchCategoriesAndSetImages();
-        setVisibleCategories(allCategories);
-        setTotalNoOfCategories(allCategories.length);
+        // collect all default counts
+        const defaultCategoryCounts = structuredClone(categoryDefaultCounts);
+        allCategories.map((o) => (defaultCategoryCounts[o.id] = o.count));
+
+        // set categories and default counts
+        setDefaultCategories(allCategories);
+        setCategoryDefaultCounts(defaultCategoryCounts);
+        setCategories(allCategories);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
         setCategoryHasError(true);
       } finally {
-        setPrimaryActiveCategory(null);
-        setRelatedActiveCategory(null);
         setCategoryIsLoading(false);
       }
     };
 
-    if (!inResultsListView && !inDetailsView) {
-      setLandingPageCategories();
+    if (categories.length === 0) {
+      setDefaultCategoriesAndCounts();
     }
-  }, [inResultsListView, inDetailsView]);
-
-  // Effects affecting the CategoryBar component
-
-  useEffect(() => {
-    const setCategoryBarCategories = async () => {
-      setCategoryIsLoading(true);
-      setCategoryHasError(false);
-      try {
-        const allCategories = await fetchCategoriesAndSetImages();
-        if (inDetailsView) {
-          setBaseCategories(allCategories.filter((cat) => cat.id !== primaryActiveCategory?.id));
-        } else {
-          setBaseCategories(allCategories);
-        }
-      } catch (error) {
-        console.error("Error fetching categories for category bar", error);
-        setCategoryHasError(true);
-      } finally {
-        setCategoryIsLoading(false);
-      }
-    };
-    if (
-      (inResultsListView && baseCategories.length === 0) || // when going to the results page for the first time
-      inDetailsView || //every time details page is updated
-      (inResultsListView && visibleCategories.length < totalNoOfCategories) //every time we navigate away from details page to results view
-    ) {
-      setCategoryBarCategories();
-    }
-  }, [
-    primaryActiveCategory,
-    inDetailsView,
-    inResultsListView,
-    baseCategories.length,
-    visibleCategories.length,
-    totalNoOfCategories,
-  ]);
-
-  useEffect(() => {
-    const fetchAndUpdateCategoryBarCount = async () => {
-      if (baseCategories.length !== 0) {
-        try {
-          const updatedSearchText =
-            searchText || detailsText
-              ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
-              : undefined;
-
-          const categoryCounts = await getCategoryCounts(updatedSearchText);
-          const categories = structuredClone(baseCategories);
-
-          categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
-          setVisibleCategories(categories);
-        } catch (error) {
-          //  TODO: How to handle error in case of API failure in this effect?
-          console.error("Failed to fetch category counts for categorybar", error);
-        }
-      }
-    };
-
-    if (inResultsListView || inDetailsView) {
-      fetchAndUpdateCategoryBarCount();
-    }
-  }, [searchText, detailsText, baseCategories, inResultsListView, inDetailsView]);
+  }, [categories, categoryDefaultCounts]);
 
   // Effects for setting default values
-  useEffect(() => {
-    if (inResultsListView && !primaryActiveCategory) {
-      setPrimaryActiveCategory(visibleCategories[0]);
-    }
-  }, [inResultsListView, primaryActiveCategory, visibleCategories]);
 
-  useEffect(() => {
-    if (inDetailsView) {
-      setRelatedActiveCategory(visibleCategories[0]);
-    }
-  }, [visibleCategories, inDetailsView]);
+  // Effects affecting the CategoryBar component
 
-  // Clean up Effect runs when navigating away from details view
-  useEffect(() => {
-    if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
-      // Taking advantage of the short period of time until the visiblecategories need to be fetched and updated for the UI
-      setPrimaryActiveCategory(null);
-      setRelatedActiveCategory(null);
-    }
-  }, [inResultsListView, visibleCategories, totalNoOfCategories]);
+  // useEffect(() => {
+  //   const setCategoryBarCategories = async () => {
+  //     setCategoryIsLoading(true);
+  //     setCategoryHasError(false);
+  //     try {
+  //       const allCategories = await fetchCategoriesAndSetImages();
+  //       if (inDetailsView) {
+  //         setBaseCategories(allCategories.filter((cat) => cat.id !== primaryActiveCategory?.id));
+  //       } else {
+  //         setBaseCategories(allCategories);
+  //       }
+  //     } catch (error) {
+  //       console.error("Error fetching categories for category bar", error);
+  //       setCategoryHasError(true);
+  //     } finally {
+  //       setCategoryIsLoading(false);
+  //     }
+  //   };
+  //   if (
+  //     (inResultsListView && baseCategories.length === 0) || // when going to the results page for the first time
+  //     inDetailsView || //every time details page is updated
+  //     (inResultsListView && visibleCategories.length < totalNoOfCategories) //every time we navigate away from details page to results view
+  //   ) {
+  //     setCategoryBarCategories();
+  //   }
+  // }, [
+  //   primaryActiveCategory,
+  //   inDetailsView,
+  //   inResultsListView,
+  //   baseCategories.length,
+  //   visibleCategories.length,
+  //   totalNoOfCategories,
+  // ]);
+
+  // useEffect(() => {
+  //   const fetchAndUpdateCategoryBarCount = async () => {
+  //     if (baseCategories.length !== 0) {
+  //       try {
+  //         const updatedSearchText =
+  //           searchText || detailsText
+  //             ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
+  //             : undefined;
+
+  //         const categoryCounts = await getCategoryCounts(updatedSearchText);
+  //         const categories = structuredClone(baseCategories);
+
+  //         categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
+  //         setVisibleCategories(categories);
+  //       } catch (error) {
+  //         //  TODO: How to handle error in case of API failure in this effect?
+  //         console.error("Failed to fetch category counts for categorybar", error);
+  //       }
+  //     }
+  //   };
+
+  //   if (inResultsListView || inDetailsView) {
+  //     fetchAndUpdateCategoryBarCount();
+  //   }
+  // }, [searchText, detailsText, baseCategories, inResultsListView, inDetailsView]);
+
+  // useEffect(() => {
+  //   if (inDetailsView) {
+  //     setRelatedActiveCategory(visibleCategories[0]);
+  //   }
+  // }, [visibleCategories, inDetailsView]);
+
+  // // Clean up Effect runs when navigating away from details view
+  // useEffect(() => {
+  //   if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
+  //     // Taking advantage of the short period of time until the visiblecategories need to be fetched and updated for the UI
+  //     setPrimaryActiveCategory(null);
+  //     setRelatedActiveCategory(null);
+  //   }
+  // }, [inResultsListView, visibleCategories, totalNoOfCategories]);
 
   return (
     <CategoryContext.Provider
       value={{
-        inDetailsView,
-        visibleCategories,
+        categories,
         primaryActiveCategory,
         setPrimaryActiveCategory,
-        relatedActiveCategory,
-        setRelatedActiveCategory,
         categoryIsLoading,
         categoryHasError,
       }}
diff --git a/src/types/types.ts b/src/types/types.ts
index 94184b8..10d0af9 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -177,7 +177,7 @@ export type FacetFieldKeys =
   | "instruments";
 
 export type Category = {
-  id: string;
+  id: FacetFieldKeys;
   text: string;
   icon?: StaticImport;
   count: number;
@@ -185,15 +185,15 @@ export type Category = {
 
 export interface CategoryContextType {
   inDetailsView?: boolean;
-  visibleCategories: Category[];
-  primaryActiveCategory: Category | null;
-  setPrimaryActiveCategory: (category: Category | null) => void;
-  relatedActiveCategory: Category | null;
-  setRelatedActiveCategory: (category: Category | null) => void;
+  categories: Category[];
+  primaryActiveCategory?: Category;
+  setPrimaryActiveCategory: (category?: Category) => void;
   categoryIsLoading: boolean;
   categoryHasError: boolean;
 }
 
+export type CategoryCountsType = Record<FacetFieldKeys, number>;
+
 // -------- SEARCH API ---------
 export const searchSchema = z.object({
   searchText: z.string().optional(),
-- 
GitLab


From 19c7114f5c500e62163ae95e194b40098d4f009c Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 4 Mar 2025 15:43:51 +0100
Subject: [PATCH 32/48] updating category bar counts on searchtext update and
 relying on defaultCategories for the landing page categories component

---
 src/app/api/config/config.ts                  |  4 +-
 src/components/app/Categories.tsx             |  6 +--
 .../app/results/CategorizedResults.tsx        | 25 ++++++-----
 src/components/app/results/CategoryBar.tsx    | 29 +++++++++++-
 src/components/layout/Header.tsx              |  2 +-
 src/contexts/CategoryContext.tsx              | 45 +++++++------------
 src/types/types.ts                            |  8 ++--
 7 files changed, 68 insertions(+), 51 deletions(-)

diff --git a/src/app/api/config/config.ts b/src/app/api/config/config.ts
index 0197fc6..5de47bd 100644
--- a/src/app/api/config/config.ts
+++ b/src/app/api/config/config.ts
@@ -61,8 +61,10 @@ export const INITIAL_CATECORY_COUNT_MAP: Record<FacetFieldKeys, number> = {
 };
 
 export const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
-  inDetailsView: false,
   categories: [],
+  defaultCategories: [],
+  primaryActiveCategory: null,
+  setCategories: () => {},
   setPrimaryActiveCategory: () => {},
   categoryIsLoading: false,
   categoryHasError: false,
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index f9a8113..42eccd0 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -14,7 +14,7 @@ const Categories = () => {
   const {
     categoryIsLoading,
     categoryHasError,
-    categories,
+    defaultCategories,
     primaryActiveCategory,
     setPrimaryActiveCategory,
   } = useContext(CategoryContext);
@@ -22,7 +22,7 @@ const Categories = () => {
   // When this component loads the primary active category should be reset to null - for a fresh slate
   useEffect(() => {
     if (primaryActiveCategory) {
-      setPrimaryActiveCategory();
+      setPrimaryActiveCategory(null);
     }
   }, [primaryActiveCategory, setPrimaryActiveCategory]);
 
@@ -43,7 +43,7 @@ const Categories = () => {
           </div>
           {/* grid of categories */}
           <div className="px-10 grid grid-cols-1  md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 gap-y-28 md:gap-x-16 lg:gap-x-32 xl:gap-x-46 content-center justify-center">
-            {categories.map((o) => {
+            {defaultCategories.map((o) => {
               return (
                 <Link
                   key={o.id}
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 3e7737e..3f956e2 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -48,11 +48,13 @@ const CategorizedResultsComponent = () => {
 
   // sets default primaryActiveCategory in Results View
   useEffect(() => {
-    if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
-      // in case you navigate to the results view by copy pasting the right url
-      const setCat = categories.filter((o) => o.id === paramsCategory)[0];
-      setPrimaryActiveCategory(setCat);
-    } else if (!inDetailsView && !primaryActiveCategory) {
+    // TODO: handle the following case properly
+    // if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
+    //   // in case you navigate to the results view by copy pasting the right url
+    //   const setCat = categories.filter((o) => o.id === paramsCategory)[0];
+    //   setPrimaryActiveCategory(setCat);
+    // } else
+    if (!inDetailsView && !primaryActiveCategory) {
       setPrimaryActiveCategory(categories[0]);
     }
   }, [inDetailsView, primaryActiveCategory, categories, paramsCategory, setPrimaryActiveCategory]);
@@ -60,12 +62,13 @@ const CategorizedResultsComponent = () => {
   // sets the default relatedActiveCategory in Details View
   useEffect(() => {
     // TODO: fix the following
-    const isSameCat = paramsCategory === categories[0].id;
-    if (inDetailsView && !relatedActiveCategory && isSameCat) {
-      const filteredCategories = categories.filter((o) => o.id !== paramsCategory);
-      setRelatedActiveCategory(filteredCategories[0]);
-      setActiveCategory(filteredCategories[0]);
-    } else if (inDetailsView && !relatedActiveCategory) {
+    // const isSameCat = paramsCategory === categories[0].id;
+    // if (inDetailsView && !relatedActiveCategory && isSameCat) {
+    //   const filteredCategories = categories.filter((o) => o.id !== paramsCategory);
+    //   setRelatedActiveCategory(filteredCategories[0]);
+    //   setActiveCategory(filteredCategories[0]);
+    // } else
+    if (inDetailsView && !relatedActiveCategory) {
       setRelatedActiveCategory(categories[0]);
       setActiveCategory(categories[0]);
     }
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index 2cfb594..e22b9b5 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -2,11 +2,12 @@
 
 import { useSearchParams } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { SetStateAction, Suspense, useContext, Dispatch } from "react";
+import { SetStateAction, Suspense, useContext, Dispatch, useEffect } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import type { Category } from "@/types/types";
+import { getCategoryCounts } from "@/utils/apiCall";
 
 type Props = {
   activeCategory?: Category;
@@ -29,9 +30,33 @@ const CategoryBarComponent = ({
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
 
-  const { categories, primaryActiveCategory, setPrimaryActiveCategory } =
+  const { categories, primaryActiveCategory, setCategories, setPrimaryActiveCategory } =
     useContext(CategoryContext);
 
+  useEffect(() => {
+    const fetchAndUpdateCategoryBarCount = async () => {
+      if (categories.length !== 0) {
+        try {
+          const updatedSearchText =
+            searchText || detailsText
+              ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
+              : undefined;
+
+          const categoryCounts = await getCategoryCounts(updatedSearchText);
+          const updatedCategories = structuredClone(categories);
+
+          updatedCategories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
+          setCategories(updatedCategories);
+        } catch (error) {
+          //  TODO: How to handle error in case of API failure in this effect?
+          console.error("Failed to fetch category counts for categorybar", error);
+        }
+      }
+    };
+
+    fetchAndUpdateCategoryBarCount();
+  }, [searchText, detailsText, categories, setCategories]);
+
   return (
     <>
       <div className="bg-primary-helmholtz-dunkelblau text-primary-helmholtz-weiss grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:flex gap-y-2 gap-x-2 xl:gap-x-6 px-4 pt-2 pb-3 md:px-10 lg:px-16 xl:pt-4 xl:min-h-24">
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index eb5561e..68d93b3 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -16,7 +16,7 @@ const Header = () => {
   const { setPrimaryActiveCategory } = useContext(CategoryContext);
 
   const handleReset = () => {
-    setPrimaryActiveCategory();
+    setPrimaryActiveCategory(null);
   };
 
   return (
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 3872998..c50157a 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -1,9 +1,8 @@
 "use client";
-import { useSearchParams } from "next/navigation";
 import { createContext, useState, useEffect } from "react";
 
-import { DEFAULT_CATEGORY_CONTEXT, INITIAL_CATECORY_COUNT_MAP } from "@/app/api/config/config";
-import { Category, CategoryContextType, FacetFieldKeys } from "@/types/types";
+import { DEFAULT_CATEGORY_CONTEXT } from "@/app/api/config/config";
+import { Category, CategoryContextType } from "@/types/types";
 import { getAllCategories } from "@/utils/apiCall";
 
 // Creating a context for the category
@@ -13,26 +12,14 @@ export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGO
 export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
   // The state that will be shared
   const [categories, setCategories] = useState<Category[]>([]);
-  const [categoryDefaultCounts, setCategoryDefaultCounts] = useState<
-    Record<FacetFieldKeys, number>
-  >(INITIAL_CATECORY_COUNT_MAP);
-  const [categoryCountsForSearch, setCategoryCountsForSearch] = useState<
-    Record<FacetFieldKeys, number>
-  >(INITIAL_CATECORY_COUNT_MAP);
-  const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category>();
+  const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
+
+  const [defaultCategories, setDefaultCategories] = useState<Category[]>([]);
 
   // Main flags for loading and error states
   const [categoryIsLoading, setCategoryIsLoading] = useState(false);
   const [categoryHasError, setCategoryHasError] = useState(false);
 
-  // Unshared state for local processing of shared state above
-  const resultParams = useSearchParams();
-  const searchText = resultParams.get("searchText") ?? undefined;
-  const detailsText = resultParams.get("detailsText") ?? undefined;
-  const category = resultParams.get("category") ?? undefined;
-
-  const [defaultCategories, setDefaultCategories] = useState<Category[]>([]);
-
   // Function to set the category images
   const fetchCategoriesAndSetImages = async () => {
     const allCategories = await getAllCategories();
@@ -60,13 +47,9 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         setCategoryHasError(false);
         // get all categories
         const allCategories = await fetchCategoriesAndSetImages();
-        // collect all default counts
-        const defaultCategoryCounts = structuredClone(categoryDefaultCounts);
-        allCategories.map((o) => (defaultCategoryCounts[o.id] = o.count));
 
         // set categories and default counts
         setDefaultCategories(allCategories);
-        setCategoryDefaultCounts(defaultCategoryCounts);
         setCategories(allCategories);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
@@ -76,10 +59,10 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
       }
     };
 
-    if (categories.length === 0) {
+    if (defaultCategories.length === 0) {
       setDefaultCategoriesAndCounts();
     }
-  }, [categories, categoryDefaultCounts]);
+  }, [defaultCategories]);
 
   // Effects for setting default values
 
@@ -121,7 +104,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
 
   // useEffect(() => {
   //   const fetchAndUpdateCategoryBarCount = async () => {
-  //     if (baseCategories.length !== 0) {
+  //     if (defaultCategories.length !== 0) {
   //       try {
   //         const updatedSearchText =
   //           searchText || detailsText
@@ -129,10 +112,10 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   //             : undefined;
 
   //         const categoryCounts = await getCategoryCounts(updatedSearchText);
-  //         const categories = structuredClone(baseCategories);
+  //         const categories = structuredClone(defaultCategories);
 
   //         categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
-  //         setVisibleCategories(categories);
+  //         setCategories(categories);
   //       } catch (error) {
   //         //  TODO: How to handle error in case of API failure in this effect?
   //         console.error("Failed to fetch category counts for categorybar", error);
@@ -140,10 +123,10 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
   //     }
   //   };
 
-  //   if (inResultsListView || inDetailsView) {
+  //   // if (inResultsListView || inDetailsView) {
   //     fetchAndUpdateCategoryBarCount();
-  //   }
-  // }, [searchText, detailsText, baseCategories, inResultsListView, inDetailsView]);
+  //   // }
+  // }, [searchText, detailsText, defaultCategories]);
 
   // useEffect(() => {
   //   if (inDetailsView) {
@@ -164,7 +147,9 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     <CategoryContext.Provider
       value={{
         categories,
+        defaultCategories,
         primaryActiveCategory,
+        setCategories,
         setPrimaryActiveCategory,
         categoryIsLoading,
         categoryHasError,
diff --git a/src/types/types.ts b/src/types/types.ts
index 10d0af9..30ce732 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -1,4 +1,5 @@
 import { StaticImport } from "next/dist/shared/lib/get-img-props";
+import { Dispatch, SetStateAction } from "react";
 import { z } from "zod";
 
 import {
@@ -184,10 +185,11 @@ export type Category = {
 };
 
 export interface CategoryContextType {
-  inDetailsView?: boolean;
   categories: Category[];
-  primaryActiveCategory?: Category;
-  setPrimaryActiveCategory: (category?: Category) => void;
+  defaultCategories: Category[];
+  primaryActiveCategory: Category | null;
+  setCategories: Dispatch<SetStateAction<Category[]>>;
+  setPrimaryActiveCategory: Dispatch<SetStateAction<Category | null>>;
   categoryIsLoading: boolean;
   categoryHasError: boolean;
 }
-- 
GitLab


From d2c2bb8112c1c6eefd62f0ea3d55791e6f384cb5 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 4 Mar 2025 15:46:10 +0100
Subject: [PATCH 33/48] removed unnecessary reset of primaryActiveCategory via
 Headers component

---
 src/components/layout/Header.tsx | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index 68d93b3..6584f41 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -1,10 +1,7 @@
-"use client";
 import Image from "next/image";
 import { useTranslations } from "next-intl";
-import { useContext } from "react";
 
 import Search from "@/components/layout/Search";
-import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import logoHGF from "@/resources/images/logo/Logo_HGF-KG_text_brightback.png";
 import unhideLogo from "@/resources/images/logo/unhide_header.png";
@@ -13,18 +10,12 @@ const Header = () => {
   const t = useTranslations("Search");
   const s = useTranslations("Intro");
 
-  const { setPrimaryActiveCategory } = useContext(CategoryContext);
-
-  const handleReset = () => {
-    setPrimaryActiveCategory(null);
-  };
-
   return (
     <div className="flex items-center justify-between px-4 md:px-12 lg:px-16">
       <div className=" basis-1/3 md:basis-1/8">
         {/* logo */}
         <div className="">
-          <Link href="/" onClick={handleReset}>
+          <Link href="/">
             <Image
               className="max-w-36 md:max-w-56 lg:max-w-64 xl:max-w-72"
               src={unhideLogo}
-- 
GitLab


From 3922d4f52cf3db14322a3615b55a7125cc62c201 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 4 Mar 2025 16:02:52 +0100
Subject: [PATCH 34/48] some redundant code cleanup

---
 .../app/results/CategorizedResults.tsx        | 24 +------
 src/contexts/CategoryContext.tsx              | 70 -------------------
 src/types/types.ts                            |  2 -
 3 files changed, 3 insertions(+), 93 deletions(-)

diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 3f956e2..1f2c159 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -1,5 +1,5 @@
 "use client";
-import { useSearchParams, usePathname } from "next/navigation";
+import { usePathname } from "next/navigation";
 import React, { useState, useContext, useEffect, Suspense } from "react";
 
 import BackBar from "@/components/app/results/BackBar";
@@ -25,11 +25,6 @@ const CategorizedResultsComponent = () => {
     setPrimaryActiveCategory,
   } = useContext(CategoryContext);
 
-  const resultParams = useSearchParams();
-  const searchText = resultParams.get("searchText") ?? undefined;
-  const detailsText = resultParams.get("detailsText") ?? undefined;
-  const paramsCategory = resultParams.get("category") ?? undefined;
-
   // Figuring out which view is calling this component
   useEffect(() => {
     const pathParams = pathName.split(/(\/)+/g);
@@ -48,31 +43,18 @@ const CategorizedResultsComponent = () => {
 
   // sets default primaryActiveCategory in Results View
   useEffect(() => {
-    // TODO: handle the following case properly
-    // if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
-    //   // in case you navigate to the results view by copy pasting the right url
-    //   const setCat = categories.filter((o) => o.id === paramsCategory)[0];
-    //   setPrimaryActiveCategory(setCat);
-    // } else
     if (!inDetailsView && !primaryActiveCategory) {
       setPrimaryActiveCategory(categories[0]);
     }
-  }, [inDetailsView, primaryActiveCategory, categories, paramsCategory, setPrimaryActiveCategory]);
+  }, [inDetailsView, primaryActiveCategory, categories, setPrimaryActiveCategory]);
 
   // sets the default relatedActiveCategory in Details View
   useEffect(() => {
-    // TODO: fix the following
-    // const isSameCat = paramsCategory === categories[0].id;
-    // if (inDetailsView && !relatedActiveCategory && isSameCat) {
-    //   const filteredCategories = categories.filter((o) => o.id !== paramsCategory);
-    //   setRelatedActiveCategory(filteredCategories[0]);
-    //   setActiveCategory(filteredCategories[0]);
-    // } else
     if (inDetailsView && !relatedActiveCategory) {
       setRelatedActiveCategory(categories[0]);
       setActiveCategory(categories[0]);
     }
-  }, [inDetailsView, relatedActiveCategory, categories, paramsCategory]);
+  }, [inDetailsView, relatedActiveCategory, categories]);
 
   // Setting active values for view.
   // Required to handle null and undefined states of primaryActiveCategory and relatedActiveCategory
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index c50157a..d877c99 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -64,76 +64,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [defaultCategories]);
 
-  // Effects for setting default values
-
-  // Effects affecting the CategoryBar component
-
-  // useEffect(() => {
-  //   const setCategoryBarCategories = async () => {
-  //     setCategoryIsLoading(true);
-  //     setCategoryHasError(false);
-  //     try {
-  //       const allCategories = await fetchCategoriesAndSetImages();
-  //       if (inDetailsView) {
-  //         setBaseCategories(allCategories.filter((cat) => cat.id !== primaryActiveCategory?.id));
-  //       } else {
-  //         setBaseCategories(allCategories);
-  //       }
-  //     } catch (error) {
-  //       console.error("Error fetching categories for category bar", error);
-  //       setCategoryHasError(true);
-  //     } finally {
-  //       setCategoryIsLoading(false);
-  //     }
-  //   };
-  //   if (
-  //     (inResultsListView && baseCategories.length === 0) || // when going to the results page for the first time
-  //     inDetailsView || //every time details page is updated
-  //     (inResultsListView && visibleCategories.length < totalNoOfCategories) //every time we navigate away from details page to results view
-  //   ) {
-  //     setCategoryBarCategories();
-  //   }
-  // }, [
-  //   primaryActiveCategory,
-  //   inDetailsView,
-  //   inResultsListView,
-  //   baseCategories.length,
-  //   visibleCategories.length,
-  //   totalNoOfCategories,
-  // ]);
-
-  // useEffect(() => {
-  //   const fetchAndUpdateCategoryBarCount = async () => {
-  //     if (defaultCategories.length !== 0) {
-  //       try {
-  //         const updatedSearchText =
-  //           searchText || detailsText
-  //             ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
-  //             : undefined;
-
-  //         const categoryCounts = await getCategoryCounts(updatedSearchText);
-  //         const categories = structuredClone(defaultCategories);
-
-  //         categories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
-  //         setCategories(categories);
-  //       } catch (error) {
-  //         //  TODO: How to handle error in case of API failure in this effect?
-  //         console.error("Failed to fetch category counts for categorybar", error);
-  //       }
-  //     }
-  //   };
-
-  //   // if (inResultsListView || inDetailsView) {
-  //     fetchAndUpdateCategoryBarCount();
-  //   // }
-  // }, [searchText, detailsText, defaultCategories]);
-
-  // useEffect(() => {
-  //   if (inDetailsView) {
-  //     setRelatedActiveCategory(visibleCategories[0]);
-  //   }
-  // }, [visibleCategories, inDetailsView]);
-
   // // Clean up Effect runs when navigating away from details view
   // useEffect(() => {
   //   if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
diff --git a/src/types/types.ts b/src/types/types.ts
index 30ce732..cbb5584 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -194,8 +194,6 @@ export interface CategoryContextType {
   categoryHasError: boolean;
 }
 
-export type CategoryCountsType = Record<FacetFieldKeys, number>;
-
 // -------- SEARCH API ---------
 export const searchSchema = z.object({
   searchText: z.string().optional(),
-- 
GitLab


From 796d2a64fd7ef49446cd0957de6549b14189e9e6 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 4 Mar 2025 17:43:51 +0100
Subject: [PATCH 35/48] tried setting category state via search component and
 failed due to race conditions

---
 src/components/app/results/CategoryBar.tsx |  7 +++++++
 src/components/layout/Search.tsx           | 17 ++++++++++++++++-
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index e22b9b5..88c8a30 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -23,6 +23,7 @@ const CategoryBarComponent = ({
   const c = useTranslations("Categories");
 
   const resultParams = useSearchParams();
+  const paramsCategory = resultParams.get("category") ?? undefined;
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
   const details = detailsText ? `&detailsText=${detailsText}` : "";
@@ -57,6 +58,12 @@ const CategoryBarComponent = ({
     fetchAndUpdateCategoryBarCount();
   }, [searchText, detailsText, categories, setCategories]);
 
+  // useEffect(() => {
+  //   if (paramsCategory && primaryActiveCategory && paramsCategory !== primaryActiveCategory.id) {
+  //     console.log("ive done it");
+  //   }
+  // }, [paramsCategory, primaryActiveCategory, setPrimaryActiveCategory]);
+
   return (
     <>
       <div className="bg-primary-helmholtz-dunkelblau text-primary-helmholtz-weiss grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:flex gap-y-2 gap-x-2 xl:gap-x-6 px-4 pt-2 pb-3 md:px-10 lg:px-16 xl:pt-4 xl:min-h-24">
diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index d6e2489..8dbf133 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -1,9 +1,10 @@
 "use client";
 
 import { useSearchParams } from "next/navigation";
-import { Suspense, useEffect, useState } from "react";
+import { Suspense, useEffect, useState, useContext } from "react";
 
 import SearchForm from "@/components/layout/SearchForm";
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
 
@@ -17,6 +18,8 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
   const paramsCategory = resultParams.get("category") ?? "datasets";
   const detailsText = resultParams.get("detailsText") ?? undefined;
 
+  const { categories, setPrimaryActiveCategory } = useContext(CategoryContext);
+
   // not really random but randomly enough for us…
   const [exampleSearch, setExamples] = useState<string[]>([]);
 
@@ -42,6 +45,18 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
     );
   }, []);
 
+  // Cant do this due to race conditions wiping state clean
+  const updatePrimaryActiveCategory = () => {
+    console.log("running");
+    let newCategory;
+    if (detailsText) {
+      newCategory = categories.filter((o) => o.id === "datasets")[0];
+    } else {
+      newCategory = categories.filter((o) => o.id === paramsCategory)[0];
+    }
+    setPrimaryActiveCategory(newCategory);
+  };
+
   return (
     <div>
       <SearchForm />
-- 
GitLab


From 2ceb2ddd74144b2427d70effe8de9280e90ad795 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 4 Mar 2025 17:50:07 +0100
Subject: [PATCH 36/48] some code clean up for the search navigation case
 preparation

---
 src/components/layout/Search.tsx | 17 +----------------
 src/contexts/CategoryContext.tsx |  3 ++-
 2 files changed, 3 insertions(+), 17 deletions(-)

diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index 8dbf133..d6e2489 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -1,10 +1,9 @@
 "use client";
 
 import { useSearchParams } from "next/navigation";
-import { Suspense, useEffect, useState, useContext } from "react";
+import { Suspense, useEffect, useState } from "react";
 
 import SearchForm from "@/components/layout/SearchForm";
-import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
 
@@ -18,8 +17,6 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
   const paramsCategory = resultParams.get("category") ?? "datasets";
   const detailsText = resultParams.get("detailsText") ?? undefined;
 
-  const { categories, setPrimaryActiveCategory } = useContext(CategoryContext);
-
   // not really random but randomly enough for us…
   const [exampleSearch, setExamples] = useState<string[]>([]);
 
@@ -45,18 +42,6 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
     );
   }, []);
 
-  // Cant do this due to race conditions wiping state clean
-  const updatePrimaryActiveCategory = () => {
-    console.log("running");
-    let newCategory;
-    if (detailsText) {
-      newCategory = categories.filter((o) => o.id === "datasets")[0];
-    } else {
-      newCategory = categories.filter((o) => o.id === paramsCategory)[0];
-    }
-    setPrimaryActiveCategory(newCategory);
-  };
-
   return (
     <div>
       <SearchForm />
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index d877c99..ad60204 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -64,7 +64,8 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [defaultCategories]);
 
-  // // Clean up Effect runs when navigating away from details view
+  // TODO: Find a way to handle this case connected to search based navigation
+  // Clean up Effect runs when navigating away from details view
   // useEffect(() => {
   //   if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
   //     // Taking advantage of the short period of time until the visiblecategories need to be fetched and updated for the UI
-- 
GitLab


From 73b2015349da0fdb408d458def7f00bdc310e118 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 5 Mar 2025 11:25:00 +0100
Subject: [PATCH 37/48] handled primaryActiveCategory reset when navigating to
 results from details view via search bar and created a separate component for
 search examples

---
 src/app/[locale]/layout.tsx                   |  9 ++-
 .../app/results/CategorizedResults.tsx        | 27 ++++---
 src/components/app/results/CategoryBar.tsx    |  7 --
 src/components/layout/Search.tsx              | 56 +------------
 src/components/layout/SearchExamples.tsx      | 81 +++++++++++++++++++
 src/components/layout/SearchForm.tsx          | 21 +++--
 src/contexts/CategoryContext.tsx              | 25 ++----
 7 files changed, 128 insertions(+), 98 deletions(-)
 create mode 100644 src/components/layout/SearchExamples.tsx

diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index efb2bd3..c38a7ba 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -60,11 +60,12 @@ export default function RootLayout({
         <noscript>{t("js_needed")}</noscript>
         <main className="flex flex-col">
           <NextIntlClientProvider locale={locale} messages={messages}>
-            <header className="sticky top-0 w-full border-b-2 border-b-secondary-helmholtz-mint shadow-lg bg-primary-helmholtz-weiss py-5 ">
-              {showBanner && <Banner />}
-              <Header />
-            </header>
             <CategoryProvider>
+              <header className="sticky top-0 w-full border-b-2 border-b-secondary-helmholtz-mint shadow-lg bg-primary-helmholtz-weiss py-5 ">
+                {showBanner && <Banner />}
+                <Header />
+              </header>
+
               <div className="grow flex flex-col">{children}</div>
             </CategoryProvider>
             <div className="sticky bottom-0">
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 1f2c159..2276867 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -39,32 +39,37 @@ const CategorizedResultsComponent = () => {
     }
   }, [pathName]);
 
-  // Setting default values for primaryActiveCategory and relatedActiveCategory when necessary
+  // ----- Effects for Results View -----
 
   // sets default primaryActiveCategory in Results View
   useEffect(() => {
     if (!inDetailsView && !primaryActiveCategory) {
       setPrimaryActiveCategory(categories[0]);
+      setActiveCategory(categories[0]);
     }
   }, [inDetailsView, primaryActiveCategory, categories, setPrimaryActiveCategory]);
 
-  // sets the default relatedActiveCategory in Details View
+  // sets active category in Results view
+  // required to handle null and undefined states of primaryActiveCategory and relatedActiveCategory
   useEffect(() => {
-    if (inDetailsView && !relatedActiveCategory) {
-      setRelatedActiveCategory(categories[0]);
-      setActiveCategory(categories[0]);
+    if (!inDetailsView && primaryActiveCategory) {
+      setActiveCategory(primaryActiveCategory);
     }
-  }, [inDetailsView, relatedActiveCategory, categories]);
+  }, [inDetailsView, primaryActiveCategory]);
 
-  // Setting active values for view.
-  // Required to handle null and undefined states of primaryActiveCategory and relatedActiveCategory
+  // -----
 
+  // ----- Effects for Details View -----
+
+  // sets the default relatedActiveCategory
   useEffect(() => {
-    if (!inDetailsView && primaryActiveCategory) {
-      setActiveCategory(primaryActiveCategory);
+    if (inDetailsView && !relatedActiveCategory) {
+      setRelatedActiveCategory(categories[0]);
+      setActiveCategory(categories[0]);
     }
-  }, [inDetailsView, primaryActiveCategory]);
+  }, [inDetailsView, relatedActiveCategory, categories]);
 
+  // sets active category
   useEffect(() => {
     if (inDetailsView && relatedActiveCategory) {
       setActiveCategory(relatedActiveCategory);
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index 88c8a30..e22b9b5 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -23,7 +23,6 @@ const CategoryBarComponent = ({
   const c = useTranslations("Categories");
 
   const resultParams = useSearchParams();
-  const paramsCategory = resultParams.get("category") ?? undefined;
   const searchText = resultParams.get("searchText") ?? undefined;
   const detailsText = resultParams.get("detailsText") ?? undefined;
   const details = detailsText ? `&detailsText=${detailsText}` : "";
@@ -58,12 +57,6 @@ const CategoryBarComponent = ({
     fetchAndUpdateCategoryBarCount();
   }, [searchText, detailsText, categories, setCategories]);
 
-  // useEffect(() => {
-  //   if (paramsCategory && primaryActiveCategory && paramsCategory !== primaryActiveCategory.id) {
-  //     console.log("ive done it");
-  //   }
-  // }, [paramsCategory, primaryActiveCategory, setPrimaryActiveCategory]);
-
   return (
     <>
       <div className="bg-primary-helmholtz-dunkelblau text-primary-helmholtz-weiss grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:flex gap-y-2 gap-x-2 xl:gap-x-6 px-4 pt-2 pb-3 md:px-10 lg:px-16 xl:pt-4 xl:min-h-24">
diff --git a/src/components/layout/Search.tsx b/src/components/layout/Search.tsx
index d6e2489..517177f 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/Search.tsx
@@ -1,69 +1,19 @@
 "use client";
 
-import { useSearchParams } from "next/navigation";
-import { Suspense, useEffect, useState } from "react";
+import { Suspense } from "react";
 
+import SearchExamples from "@/components/layout/SearchExamples";
 import SearchForm from "@/components/layout/SearchForm";
-import { Link } from "@/i18n/routing";
-import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
 
 type Props = {
   exampleTrigger: string;
 };
 
 const SearchComponent = ({ exampleTrigger }: Props) => {
-  const resultParams = useSearchParams();
-
-  const paramsCategory = resultParams.get("category") ?? "datasets";
-  const detailsText = resultParams.get("detailsText") ?? undefined;
-
-  // not really random but randomly enough for us…
-  const [exampleSearch, setExamples] = useState<string[]>([]);
-
-  useEffect(() => {
-    setExamples(
-      [
-        "GEOMAR",
-        "AWI",
-        "Forschungszentrum Jülich",
-        "Jülich Data",
-        "Climate",
-        "DESY",
-        "HZB",
-        "KIT",
-        "HZDR",
-        "Rodare",
-        "Pangaea",
-        "Datacite",
-        "Geofon",
-      ]
-        .sort(() => 0.5 - Math.random())
-        .slice(0, 4)
-    );
-  }, []);
-
   return (
     <div>
       <SearchForm />
-
-      <div className="flex md:basis-11/12 space-x-2 pl-2 pt-3 items-center truncate">
-        <div className="font-bold text-primary-helmholtz-dunkelblau pr-2">{exampleTrigger}</div>
-        <div className="flex flex-wrap gap-x-8 gap-y-2 flex-row md:flex-nowrap md:gap-x-10 md:grow md:justify-between h-5">
-          {exampleSearch.map((query, ix) => (
-            <Link
-              key={ix}
-              href={
-                `/results?` +
-                `category=${detailsText ? "datasets" : paramsCategory}&searchText=${query}`
-              }
-              className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
-            >
-              <div className="basis-1">{query}</div>
-              <RightArrowIcon />
-            </Link>
-          ))}
-        </div>
-      </div>
+      <SearchExamples exampleTrigger={exampleTrigger} />
     </div>
   );
 };
diff --git a/src/components/layout/SearchExamples.tsx b/src/components/layout/SearchExamples.tsx
new file mode 100644
index 0000000..8753615
--- /dev/null
+++ b/src/components/layout/SearchExamples.tsx
@@ -0,0 +1,81 @@
+import { useSearchParams } from "next/navigation";
+import { Suspense, useEffect, useState, useContext } from "react";
+
+import { CategoryContext } from "@/contexts/CategoryContext";
+import { Link } from "@/i18n/routing";
+import RightArrowIcon from "@/resources/images/svg/RightArrowIcon";
+
+type Props = {
+  exampleTrigger: string;
+};
+
+const SearchExamplesComponent = ({ exampleTrigger }: Props) => {
+  const resultParams = useSearchParams();
+  const paramsCategory = resultParams.get("category") ?? "datasets";
+  const detailsText = resultParams.get("detailsText") ?? undefined;
+  const { categories, setPrimaryActiveCategory } = useContext(CategoryContext);
+
+  // not really random but randomly enough for us…
+  const [exampleSearch, setExamples] = useState<string[]>([]);
+
+  useEffect(() => {
+    setExamples(
+      [
+        "GEOMAR",
+        "AWI",
+        "Forschungszentrum Jülich",
+        "Jülich Data",
+        "Climate",
+        "DESY",
+        "HZB",
+        "KIT",
+        "HZDR",
+        "Rodare",
+        "Pangaea",
+        "Datacite",
+        "Geofon",
+      ]
+        .sort(() => 0.5 - Math.random())
+        .slice(0, 4)
+    );
+  }, []);
+
+  const handleExampleClick = () => {
+    if (detailsText) {
+      setPrimaryActiveCategory(categories[0]);
+    }
+  };
+
+  return (
+    <>
+      <div className="flex md:basis-11/12 space-x-2 pl-2 pt-3 items-center truncate">
+        <div className="font-bold text-primary-helmholtz-dunkelblau pr-2">{exampleTrigger}</div>
+        <div className="flex flex-wrap gap-x-8 gap-y-2 flex-row md:flex-nowrap md:gap-x-10 md:grow md:justify-between h-5">
+          {exampleSearch.map((query, ix) => (
+            <Link
+              href={
+                detailsText
+                  ? `/results?category=datasets&searchText=${query}`
+                  : `/results?category=${paramsCategory}&searchText=${query}`
+              }
+              key={ix}
+              onClick={handleExampleClick}
+              className="flex items-center text-info text-primary-helmholtz-dunkelblau hover:scale-110 hover:transition hover:delay-150 hover:translate-x-2 hover:ease-in-out hover:text-primary-helmholtz-hellblau"
+            >
+              <div className="basis-1">{query}</div>
+              <RightArrowIcon />
+            </Link>
+          ))}
+        </div>
+      </div>
+    </>
+  );
+};
+
+export default function SearchExamples(props: Props) {
+  return (
+    <Suspense>
+      <SearchExamplesComponent {...props} />
+    </Suspense>
+  );
+}
diff --git a/src/components/layout/SearchForm.tsx b/src/components/layout/SearchForm.tsx
index b535249..bbe5027 100644
--- a/src/components/layout/SearchForm.tsx
+++ b/src/components/layout/SearchForm.tsx
@@ -2,27 +2,38 @@
 
 import { useSearchParams, useRouter } from "next/navigation";
 import { useTranslations } from "next-intl";
-import { Suspense, useEffect, useState } from "react";
+import { Suspense, useEffect, useState, useContext } from "react";
 
+import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import ClearIcon from "@/resources/images/svg/ClearIcon";
 import SearchIcon from "@/resources/images/svg/SearchIcon";
 
 const SearchFormComponent = () => {
-  const resultParams = useSearchParams();
   const router = useRouter();
   const t = useTranslations("Search");
-
-  const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
+  const resultParams = useSearchParams();
   const paramsCategory = resultParams.get("category") ?? "datasets";
   const detailsText = resultParams.get("detailsText") ?? undefined;
+  const { setPrimaryActiveCategory } = useContext(CategoryContext);
+  const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
 
   useEffect(() => {
     setSearchQuery(resultParams.get("searchText") ?? "");
   }, [resultParams]);
 
+  const handleClear = () => {
+    if (detailsText) {
+      setPrimaryActiveCategory(null);
+    }
+    setSearchQuery("");
+  };
+
   const submitSearch = (event: any) => {
     event.preventDefault();
+    if (detailsText) {
+      setPrimaryActiveCategory(null);
+    }
     router.push(
       `/results?category=${detailsText ? "datasets" : paramsCategory}` +
         (searchQuery ? `&searchText=${searchQuery}` : "")
@@ -48,7 +59,7 @@ const SearchFormComponent = () => {
           {searchQuery !== "" ? (
             <Link
               className="-ml-12 pr-2 md:mt-1 xl:mt-1 flex items-center"
-              onClick={() => setSearchQuery("")}
+              onClick={handleClear}
               href={`/results?category=${detailsText ? "datasets" : paramsCategory}`}
             >
               <ClearIcon />
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index ad60204..98f9992 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -10,17 +10,16 @@ export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGO
 
 // Creating a provider for the category
 export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
-  // The state that will be shared
-  const [categories, setCategories] = useState<Category[]>([]);
+  // The category state that will be shared
+  const [categories, setCategories] = useState<Category[]>([]); // list of categories whose counts will be updated as per search text updates
+  const [defaultCategories, setDefaultCategories] = useState<Category[]>([]); // default set of categories and counts
   const [primaryActiveCategory, setPrimaryActiveCategory] = useState<Category | null>(null);
 
-  const [defaultCategories, setDefaultCategories] = useState<Category[]>([]);
-
   // Main flags for loading and error states
   const [categoryIsLoading, setCategoryIsLoading] = useState(false);
   const [categoryHasError, setCategoryHasError] = useState(false);
 
-  // Function to set the category images
+  // Function to fetch categories via API call and set the category images
   const fetchCategoriesAndSetImages = async () => {
     const allCategories = await getAllCategories();
     const categoriesClone = structuredClone(allCategories);
@@ -38,8 +37,7 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     return categoriesClone;
   };
 
-  // Effects affecting the Categories component in the Landing page
-
+  // Effect for loading categories
   useEffect(() => {
     const setDefaultCategoriesAndCounts = async () => {
       try {
@@ -48,8 +46,9 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
         // get all categories
         const allCategories = await fetchCategoriesAndSetImages();
 
-        // set categories and default counts
+        // set default categories with default counts
         setDefaultCategories(allCategories);
+        // and a copy of the default categories that will updated when the counts update for search
         setCategories(allCategories);
       } catch (error) {
         console.error("Error fetching categories for landing page", error);
@@ -64,16 +63,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [defaultCategories]);
 
-  // TODO: Find a way to handle this case connected to search based navigation
-  // Clean up Effect runs when navigating away from details view
-  // useEffect(() => {
-  //   if (inResultsListView && visibleCategories.length < totalNoOfCategories) {
-  //     // Taking advantage of the short period of time until the visiblecategories need to be fetched and updated for the UI
-  //     setPrimaryActiveCategory(null);
-  //     setRelatedActiveCategory(null);
-  //   }
-  // }, [inResultsListView, visibleCategories, totalNoOfCategories]);
-
   return (
     <CategoryContext.Provider
       value={{
-- 
GitLab


From b3526109e34afb834fecd9e5255beacd8eeab0d1 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 5 Mar 2025 12:49:06 +0100
Subject: [PATCH 38/48] fixed a race condition based primaryActiveCategory
 update failure, fixed a cyclic run of useEffect to fetch category counts

---
 .../app/results/CategorizedResults.tsx        | 15 +++++--
 src/components/app/results/CategoryBar.tsx    | 41 +++++++++++--------
 2 files changed, 35 insertions(+), 21 deletions(-)

diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 2276867..145065d 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -1,5 +1,5 @@
 "use client";
-import { usePathname } from "next/navigation";
+import { usePathname, useSearchParams } from "next/navigation";
 import React, { useState, useContext, useEffect, Suspense } from "react";
 
 import BackBar from "@/components/app/results/BackBar";
@@ -13,6 +13,8 @@ import { Category } from "@/types/types";
 
 const CategorizedResultsComponent = () => {
   const pathName = usePathname();
+  const resultParams = useSearchParams();
+  const paramsCategory = resultParams.get("category") ?? undefined;
   const [activeCategory, setActiveCategory] = useState<Category>();
   const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
   const [inDetailsView, setInDetailsView] = useState<boolean>(false);
@@ -43,11 +45,18 @@ const CategorizedResultsComponent = () => {
 
   // sets default primaryActiveCategory in Results View
   useEffect(() => {
-    if (!inDetailsView && !primaryActiveCategory) {
+    // to handle navigation via Categories.tsx
+    // or when user copy pastes the url to the result page (when wanting to share results related to particular category)
+    if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
+      const chosenCategory = categories.filter((o) => o.id === paramsCategory)[0];
+      setPrimaryActiveCategory(chosenCategory);
+      setActiveCategory(chosenCategory);
+    } else if (!inDetailsView && !primaryActiveCategory) {
+      // for navigation via search bar
       setPrimaryActiveCategory(categories[0]);
       setActiveCategory(categories[0]);
     }
-  }, [inDetailsView, primaryActiveCategory, categories, setPrimaryActiveCategory]);
+  }, [inDetailsView, primaryActiveCategory, categories, paramsCategory, setPrimaryActiveCategory]);
 
   // sets active category in Results view
   // required to handle null and undefined states of primaryActiveCategory and relatedActiveCategory
diff --git a/src/components/app/results/CategoryBar.tsx b/src/components/app/results/CategoryBar.tsx
index e22b9b5..4ee6d1c 100644
--- a/src/components/app/results/CategoryBar.tsx
+++ b/src/components/app/results/CategoryBar.tsx
@@ -30,32 +30,37 @@ const CategoryBarComponent = ({
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
 
-  const { categories, primaryActiveCategory, setCategories, setPrimaryActiveCategory } =
-    useContext(CategoryContext);
+  const {
+    categories,
+    defaultCategories,
+    primaryActiveCategory,
+    setCategories,
+    setPrimaryActiveCategory,
+  } = useContext(CategoryContext);
 
   useEffect(() => {
     const fetchAndUpdateCategoryBarCount = async () => {
-      if (categories.length !== 0) {
-        try {
-          const updatedSearchText =
-            searchText || detailsText
-              ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
-              : undefined;
+      try {
+        const updatedSearchText =
+          searchText || detailsText
+            ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
+            : undefined;
 
-          const categoryCounts = await getCategoryCounts(updatedSearchText);
-          const updatedCategories = structuredClone(categories);
+        const categoryCounts = await getCategoryCounts(updatedSearchText);
+        const updatedCategories = structuredClone(defaultCategories);
 
-          updatedCategories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
-          setCategories(updatedCategories);
-        } catch (error) {
-          //  TODO: How to handle error in case of API failure in this effect?
-          console.error("Failed to fetch category counts for categorybar", error);
-        }
+        updatedCategories.forEach((cat: Category) => (cat.count = categoryCounts[cat.id] ?? 0));
+        setCategories(updatedCategories);
+      } catch (error) {
+        //  TODO: How to handle error in case of API failure in this effect?
+        console.error("Failed to fetch category counts for categorybar", error);
       }
     };
 
-    fetchAndUpdateCategoryBarCount();
-  }, [searchText, detailsText, categories, setCategories]);
+    if (defaultCategories.length !== 0) {
+      fetchAndUpdateCategoryBarCount();
+    }
+  }, [searchText, detailsText, defaultCategories, setCategories]);
 
   return (
     <>
-- 
GitLab


From 46858bf29e9e38883944e979c0701f8078b7679e Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 5 Mar 2025 13:30:59 +0100
Subject: [PATCH 39/48] added name and id attributes to search input and used
 next-intl based formatter for copyright year calculation in footer

---
 src/components/layout/Footer.tsx     | 7 +++++--
 src/components/layout/SearchForm.tsx | 2 ++
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index 5f43cd4..4b7f0a3 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -1,6 +1,6 @@
 import Image from "next/image";
 import Link from "next/link";
-import { useTranslations } from "next-intl";
+import { useTranslations, useFormatter } from "next-intl";
 
 import ExternalLink from "@/components/ExternalLink";
 import HMCLogoInv from "@/resources/images/logo/HMC_Logo_invert_S.png";
@@ -13,7 +13,10 @@ import MastodonLogo from "@/resources/images/svg/MastodonLogo";
 import MattermostLogo from "@/resources/images/svg/MattermostLogo";
 
 export default function Footer() {
+  const format = useFormatter();
+  const dateTime = new Date();
   const t = useTranslations("Footer");
+
   return (
     <>
       <div className="text-primary-helmholtz-weiss w-full flex-wrap divide-y divide-secondary-helmholtz-webblassblau bg-primary-helmholtz-dunkelblau px-20 py-5 drop-shadow">
@@ -89,7 +92,7 @@ export default function Footer() {
 
           {/* copyright info */}
           <div className="pt-4 md:pt-0 md:basis-1/3 flex justify-end cursor-default text-sm">
-            {t("copyright", { year: new Date().getFullYear() })}
+            {t("copyright", { year: format.dateTime(dateTime, { year: "numeric" }) })}
           </div>
         </div>
       </div>
diff --git a/src/components/layout/SearchForm.tsx b/src/components/layout/SearchForm.tsx
index bbe5027..2e238b6 100644
--- a/src/components/layout/SearchForm.tsx
+++ b/src/components/layout/SearchForm.tsx
@@ -48,6 +48,8 @@ const SearchFormComponent = () => {
       >
         <div className="basis-9/12 md:basis-10/12">
           <input
+            id="searchInput"
+            name="searchInput"
             className="pl-2 text-primary-helmholtz-dunkelblau placeholder:italic focus:outline-none bg-transparent"
             placeholder={t("placeholder")}
             value={searchQuery}
-- 
GitLab


From 5b0163f99155559453e76e178b1508455714fa73 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 5 Mar 2025 15:50:15 +0100
Subject: [PATCH 40/48] minor fix to state setting in search form

---
 src/components/layout/SearchForm.tsx | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/components/layout/SearchForm.tsx b/src/components/layout/SearchForm.tsx
index 2e238b6..dd54668 100644
--- a/src/components/layout/SearchForm.tsx
+++ b/src/components/layout/SearchForm.tsx
@@ -8,6 +8,7 @@ import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import ClearIcon from "@/resources/images/svg/ClearIcon";
 import SearchIcon from "@/resources/images/svg/SearchIcon";
+import Categories from "../app/Categories";
 
 const SearchFormComponent = () => {
   const router = useRouter();
@@ -15,7 +16,7 @@ const SearchFormComponent = () => {
   const resultParams = useSearchParams();
   const paramsCategory = resultParams.get("category") ?? "datasets";
   const detailsText = resultParams.get("detailsText") ?? undefined;
-  const { setPrimaryActiveCategory } = useContext(CategoryContext);
+  const { categories, setPrimaryActiveCategory } = useContext(CategoryContext);
   const [searchQuery, setSearchQuery] = useState(resultParams.get("searchText") ?? "");
 
   useEffect(() => {
@@ -32,7 +33,7 @@ const SearchFormComponent = () => {
   const submitSearch = (event: any) => {
     event.preventDefault();
     if (detailsText) {
-      setPrimaryActiveCategory(null);
+      setPrimaryActiveCategory(categories[0]);
     }
     router.push(
       `/results?category=${detailsText ? "datasets" : paramsCategory}` +
-- 
GitLab


From 82ab6e7da0a9a941f7a4ab105c4c20548c9c8293 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Fri, 7 Mar 2025 10:18:09 +0100
Subject: [PATCH 41/48] grouped all search related components together and
 updated imports accordingly

---
 src/app/[locale]/page.tsx                             | 2 +-
 src/app/[locale]/results/details/page.tsx             | 2 +-
 src/components/app/results/CategorizedResults.tsx     | 2 +-
 src/components/layout/Header.tsx                      | 2 +-
 src/components/layout/{ => search}/MobileSearch.tsx   | 4 ++--
 src/components/layout/{ => search}/Search.tsx         | 4 ++--
 src/components/layout/{ => search}/SearchExamples.tsx | 2 +-
 src/components/layout/{ => search}/SearchForm.tsx     | 5 ++---
 8 files changed, 11 insertions(+), 12 deletions(-)
 rename src/components/layout/{ => search}/MobileSearch.tsx (82%)
 rename src/components/layout/{ => search}/Search.tsx (74%)
 rename src/components/layout/{ => search}/SearchExamples.tsx (96%)
 rename src/components/layout/{ => search}/SearchForm.tsx (94%)

diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx
index 746a9fa..7f701ee 100644
--- a/src/app/[locale]/page.tsx
+++ b/src/app/[locale]/page.tsx
@@ -2,7 +2,7 @@
 
 import Categories from "@/components/app/Categories";
 import Intro from "@/components/app/Intro";
-import MobileSearch from "@/components/layout/MobileSearch";
+import MobileSearch from "@/components/layout/search/MobileSearch";
 
 const Home = () => {
   return (
diff --git a/src/app/[locale]/results/details/page.tsx b/src/app/[locale]/results/details/page.tsx
index 7526d50..443d6f7 100644
--- a/src/app/[locale]/results/details/page.tsx
+++ b/src/app/[locale]/results/details/page.tsx
@@ -3,7 +3,7 @@ import React, { Suspense } from "react";
 import BackBar from "@/components/app/results/BackBar";
 import DetailsItemCard from "@/components/app/results/Details/DetailsItemCard";
 import RelatedResults from "@/components/app/results/Details/RelatedResults";
-import MobileSearch from "@/components/layout/MobileSearch";
+import MobileSearch from "@/components/layout/search/MobileSearch";
 
 const DetailsComponent = () => {
   return (
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 145065d..6ff1b42 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -6,8 +6,8 @@ import BackBar from "@/components/app/results/BackBar";
 import CategoryBar from "@/components/app/results/CategoryBar";
 import ListResults from "@/components/app/results/ListResults";
 import Error from "@/components/layout/Error";
-import MobileSearch from "@/components/layout/MobileSearch";
 import Spinner from "@/components/layout/Spinner";
+import MobileSearch from "@/components/layout/search/MobileSearch";
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Category } from "@/types/types";
 
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index 6584f41..a018dc7 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -1,7 +1,7 @@
 import Image from "next/image";
 import { useTranslations } from "next-intl";
 
-import Search from "@/components/layout/Search";
+import Search from "@/components/layout/search/Search";
 import { Link } from "@/i18n/routing";
 import logoHGF from "@/resources/images/logo/Logo_HGF-KG_text_brightback.png";
 import unhideLogo from "@/resources/images/logo/unhide_header.png";
diff --git a/src/components/layout/MobileSearch.tsx b/src/components/layout/search/MobileSearch.tsx
similarity index 82%
rename from src/components/layout/MobileSearch.tsx
rename to src/components/layout/search/MobileSearch.tsx
index aa88958..0a4f663 100644
--- a/src/components/layout/MobileSearch.tsx
+++ b/src/components/layout/search/MobileSearch.tsx
@@ -1,7 +1,7 @@
 import { useTranslations } from "next-intl";
 import React from "react";
 
-import Search from "@/components/layout/Search";
+import Search from "@/components/layout/search/Search";
 import bg from "@/resources/images/misc/unhide_kg_tbg.png";
 
 const MobileSearch = () => {
@@ -17,7 +17,7 @@ const MobileSearch = () => {
       <div className="text-xl md:text-2xl text-primary-helmholtz-dunkelblau">{t("search")}</div>
       <div className="">
         {/* search bar */}
-        <Search exampleTrigger={t("try")} placeholder={t("placeholder")} />
+        <Search exampleTrigger={t("try")} />
       </div>
     </div>
   );
diff --git a/src/components/layout/Search.tsx b/src/components/layout/search/Search.tsx
similarity index 74%
rename from src/components/layout/Search.tsx
rename to src/components/layout/search/Search.tsx
index 517177f..3aec51f 100644
--- a/src/components/layout/Search.tsx
+++ b/src/components/layout/search/Search.tsx
@@ -2,8 +2,8 @@
 
 import { Suspense } from "react";
 
-import SearchExamples from "@/components/layout/SearchExamples";
-import SearchForm from "@/components/layout/SearchForm";
+import SearchExamples from "@/components/layout/search/SearchExamples";
+import SearchForm from "@/components/layout/search/SearchForm";
 
 type Props = {
   exampleTrigger: string;
diff --git a/src/components/layout/SearchExamples.tsx b/src/components/layout/search/SearchExamples.tsx
similarity index 96%
rename from src/components/layout/SearchExamples.tsx
rename to src/components/layout/search/SearchExamples.tsx
index 8753615..6d44ac7 100644
--- a/src/components/layout/SearchExamples.tsx
+++ b/src/components/layout/search/SearchExamples.tsx
@@ -42,7 +42,7 @@ const SearchExamplesComponent = ({ exampleTrigger }: Props) => {
 
   const handleExampleClick = () => {
     if (detailsText) {
-      setPrimaryActiveCategory(categories[0]);
+      setPrimaryActiveCategory(categories.filter((o) => o.id === "datasets")[0]);
     }
   };
 
diff --git a/src/components/layout/SearchForm.tsx b/src/components/layout/search/SearchForm.tsx
similarity index 94%
rename from src/components/layout/SearchForm.tsx
rename to src/components/layout/search/SearchForm.tsx
index dd54668..9fef472 100644
--- a/src/components/layout/SearchForm.tsx
+++ b/src/components/layout/search/SearchForm.tsx
@@ -8,7 +8,6 @@ import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
 import ClearIcon from "@/resources/images/svg/ClearIcon";
 import SearchIcon from "@/resources/images/svg/SearchIcon";
-import Categories from "../app/Categories";
 
 const SearchFormComponent = () => {
   const router = useRouter();
@@ -25,7 +24,7 @@ const SearchFormComponent = () => {
 
   const handleClear = () => {
     if (detailsText) {
-      setPrimaryActiveCategory(null);
+      setPrimaryActiveCategory(categories.filter((o) => o.id === "datasets")[0]);
     }
     setSearchQuery("");
   };
@@ -33,7 +32,7 @@ const SearchFormComponent = () => {
   const submitSearch = (event: any) => {
     event.preventDefault();
     if (detailsText) {
-      setPrimaryActiveCategory(categories[0]);
+      setPrimaryActiveCategory(categories.filter((o) => o.id === "datasets")[0]);
     }
     router.push(
       `/results?category=${detailsText ? "datasets" : paramsCategory}` +
-- 
GitLab


From 3a96960b967f17c038a93c958b965a6a0b251876 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Fri, 7 Mar 2025 15:29:30 +0100
Subject: [PATCH 42/48] moved default values for category context to provider
 definition file

---
 src/app/api/config/config.ts     | 11 -----------
 src/contexts/CategoryContext.tsx | 11 ++++++++++-
 2 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/src/app/api/config/config.ts b/src/app/api/config/config.ts
index 5de47bd..30a16bb 100644
--- a/src/app/api/config/config.ts
+++ b/src/app/api/config/config.ts
@@ -1,6 +1,5 @@
 import type {
   Category,
-  CategoryContextType,
   FacetIntervalFieldKeys,
   FacetFieldKeys,
   SOLRCategories,
@@ -60,16 +59,6 @@ export const INITIAL_CATECORY_COUNT_MAP: Record<FacetFieldKeys, number> = {
   instruments: 0,
 };
 
-export const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
-  categories: [],
-  defaultCategories: [],
-  primaryActiveCategory: null,
-  setCategories: () => {},
-  setPrimaryActiveCategory: () => {},
-  categoryIsLoading: false,
-  categoryHasError: false,
-};
-
 export const AVAILABLE_FACETS: string[] = [
   "txt_knowsAbout",
   "txt_knowsLanguage",
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 98f9992..88d5bb5 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -1,10 +1,19 @@
 "use client";
 import { createContext, useState, useEffect } from "react";
 
-import { DEFAULT_CATEGORY_CONTEXT } from "@/app/api/config/config";
 import { Category, CategoryContextType } from "@/types/types";
 import { getAllCategories } from "@/utils/apiCall";
 
+export const DEFAULT_CATEGORY_CONTEXT: CategoryContextType = {
+  categories: [],
+  defaultCategories: [],
+  primaryActiveCategory: null,
+  setCategories: () => {},
+  setPrimaryActiveCategory: () => {},
+  categoryIsLoading: false,
+  categoryHasError: false,
+};
+
 // Creating a context for the category
 export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGORY_CONTEXT);
 
-- 
GitLab


From 70ea92c9e43971dd48bd701a11d339487b8c1d23 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Fri, 7 Mar 2025 15:40:44 +0100
Subject: [PATCH 43/48] removed unnecessary suspense components in search
 related components

---
 src/components/layout/search/Search.tsx         | 14 ++------------
 src/components/layout/search/SearchExamples.tsx | 13 ++++---------
 2 files changed, 6 insertions(+), 21 deletions(-)

diff --git a/src/components/layout/search/Search.tsx b/src/components/layout/search/Search.tsx
index 3aec51f..6d6c421 100644
--- a/src/components/layout/search/Search.tsx
+++ b/src/components/layout/search/Search.tsx
@@ -1,7 +1,3 @@
-"use client";
-
-import { Suspense } from "react";
-
 import SearchExamples from "@/components/layout/search/SearchExamples";
 import SearchForm from "@/components/layout/search/SearchForm";
 
@@ -9,7 +5,7 @@ type Props = {
   exampleTrigger: string;
 };
 
-const SearchComponent = ({ exampleTrigger }: Props) => {
+const Search = ({ exampleTrigger }: Props) => {
   return (
     <div>
       <SearchForm />
@@ -18,10 +14,4 @@ const SearchComponent = ({ exampleTrigger }: Props) => {
   );
 };
 
-export default function Search(props: Props) {
-  return (
-    <Suspense>
-      <SearchComponent {...props} />
-    </Suspense>
-  );
-}
+export default Search;
diff --git a/src/components/layout/search/SearchExamples.tsx b/src/components/layout/search/SearchExamples.tsx
index 6d44ac7..144146f 100644
--- a/src/components/layout/search/SearchExamples.tsx
+++ b/src/components/layout/search/SearchExamples.tsx
@@ -1,5 +1,6 @@
+"use client";
 import { useSearchParams } from "next/navigation";
-import { Suspense, useEffect, useState, useContext } from "react";
+import { useEffect, useState, useContext } from "react";
 
 import { CategoryContext } from "@/contexts/CategoryContext";
 import { Link } from "@/i18n/routing";
@@ -9,7 +10,7 @@ type Props = {
   exampleTrigger: string;
 };
 
-const SearchExamplesComponent = ({ exampleTrigger }: Props) => {
+const SearchExamples = ({ exampleTrigger }: Props) => {
   const resultParams = useSearchParams();
   const paramsCategory = resultParams.get("category") ?? "datasets";
   const detailsText = resultParams.get("detailsText") ?? undefined;
@@ -72,10 +73,4 @@ const SearchExamplesComponent = ({ exampleTrigger }: Props) => {
   );
 };
 
-export default function SearchExamples(props: Props) {
-  return (
-    <Suspense>
-      <SearchExamplesComponent {...props} />
-    </Suspense>
-  );
-}
+export default SearchExamples;
-- 
GitLab


From 0a860a77386dccdd36a5d9cd7c97a268976864a2 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Fri, 7 Mar 2025 16:11:49 +0100
Subject: [PATCH 44/48] setting paramsCategory as primaryActiveCategory on load
 of Category Context, via context

---
 src/components/app/Categories.tsx             | 12 +++++------
 .../app/results/CategorizedResults.tsx        | 20 ++++++++++---------
 src/contexts/CategoryContext.tsx              | 13 ++++++++++++
 3 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index 42eccd0..1502019 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -2,7 +2,7 @@
 
 import Image from "next/image";
 import { useTranslations } from "next-intl";
-import { useContext, useEffect } from "react";
+import { useContext } from "react";
 
 import Error from "@/components/layout/Error";
 import Spinner from "@/components/layout/Spinner";
@@ -20,11 +20,11 @@ const Categories = () => {
   } = useContext(CategoryContext);
 
   // When this component loads the primary active category should be reset to null - for a fresh slate
-  useEffect(() => {
-    if (primaryActiveCategory) {
-      setPrimaryActiveCategory(null);
-    }
-  }, [primaryActiveCategory, setPrimaryActiveCategory]);
+  //useEffect(() => {
+  //if (primaryActiveCategory) {
+  //setPrimaryActiveCategory(null);
+  // }
+  //}, [primaryActiveCategory, setPrimaryActiveCategory]);
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
   /* traditional JS module that makes the badge numbers appear as strings representing exponents
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 6ff1b42..0d1ef88 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -47,21 +47,23 @@ const CategorizedResultsComponent = () => {
   useEffect(() => {
     // to handle navigation via Categories.tsx
     // or when user copy pastes the url to the result page (when wanting to share results related to particular category)
-    if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
-      const chosenCategory = categories.filter((o) => o.id === paramsCategory)[0];
-      setPrimaryActiveCategory(chosenCategory);
-      setActiveCategory(chosenCategory);
-    } else if (!inDetailsView && !primaryActiveCategory) {
-      // for navigation via search bar
-      setPrimaryActiveCategory(categories[0]);
-      setActiveCategory(categories[0]);
-    }
+    // if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
+    //  const chosenCategory = categories.filter((o) => o.id === paramsCategory)[0];
+    // setPrimaryActiveCategory(chosenCategory);
+    // setActiveCategory(chosenCategory);
+    // } else
+    // if (!inDetailsView && !primaryActiveCategory) {
+    // for navigation via search bar
+    // setPrimaryActiveCategory(categories[0]);
+    //setActiveCategory(categories[0]);
+    //}
   }, [inDetailsView, primaryActiveCategory, categories, paramsCategory, setPrimaryActiveCategory]);
 
   // sets active category in Results view
   // required to handle null and undefined states of primaryActiveCategory and relatedActiveCategory
   useEffect(() => {
     if (!inDetailsView && primaryActiveCategory) {
+      console.log("here");
       setActiveCategory(primaryActiveCategory);
     }
   }, [inDetailsView, primaryActiveCategory]);
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 88d5bb5..8471a02 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -1,4 +1,5 @@
 "use client";
+import { useSearchParams } from "next/navigation";
 import { createContext, useState, useEffect } from "react";
 
 import { Category, CategoryContextType } from "@/types/types";
@@ -19,6 +20,8 @@ export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGO
 
 // Creating a provider for the category
 export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
+  const resultParams = useSearchParams();
+  const paramsCategory = resultParams.get("category") ?? undefined;
   // The category state that will be shared
   const [categories, setCategories] = useState<Category[]>([]); // list of categories whose counts will be updated as per search text updates
   const [defaultCategories, setDefaultCategories] = useState<Category[]>([]); // default set of categories and counts
@@ -72,6 +75,16 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [defaultCategories]);
 
+  if (paramsCategory && !primaryActiveCategory) {
+    console.log(" i am happening");
+    setPrimaryActiveCategory(categories[0]);
+  }
+
+  if (!paramsCategory && primaryActiveCategory) {
+    console.log("i run");
+    setPrimaryActiveCategory(null);
+  }
+
   return (
     <CategoryContext.Provider
       value={{
-- 
GitLab


From 69406544ee3983192f973bb05bd240010f7f9b6e Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 11 Mar 2025 12:29:56 +0100
Subject: [PATCH 45/48] setting and resetting primaryActiveCategory in context
 when in landing page

---
 next.config.ts                                |  2 +-
 src/components/app/Categories.tsx             | 16 ++---------
 .../app/results/CategorizedResults.tsx        | 26 +++++++++---------
 src/contexts/CategoryContext.tsx              | 16 +++++------
 tsconfig.json                                 | 27 +++++--------------
 5 files changed, 28 insertions(+), 59 deletions(-)

diff --git a/next.config.ts b/next.config.ts
index 63360ab..5256858 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,5 +1,5 @@
 import createNextIntlPlugin from "next-intl/plugin";
-import type { NextConfig } from 'next'
+import type { NextConfig } from "next";
 
 const withNextIntl = createNextIntlPlugin();
 
diff --git a/src/components/app/Categories.tsx b/src/components/app/Categories.tsx
index 1502019..75ebe1f 100644
--- a/src/components/app/Categories.tsx
+++ b/src/components/app/Categories.tsx
@@ -11,20 +11,8 @@ import { Link } from "@/i18n/routing";
 
 const Categories = () => {
   const t = useTranslations("Categories");
-  const {
-    categoryIsLoading,
-    categoryHasError,
-    defaultCategories,
-    primaryActiveCategory,
-    setPrimaryActiveCategory,
-  } = useContext(CategoryContext);
-
-  // When this component loads the primary active category should be reset to null - for a fresh slate
-  //useEffect(() => {
-  //if (primaryActiveCategory) {
-  //setPrimaryActiveCategory(null);
-  // }
-  //}, [primaryActiveCategory, setPrimaryActiveCategory]);
+  const { categoryIsLoading, categoryHasError, defaultCategories, setPrimaryActiveCategory } =
+    useContext(CategoryContext);
 
   const formatter = Intl.NumberFormat("en", { notation: "compact" });
   /* traditional JS module that makes the badge numbers appear as strings representing exponents
diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 0d1ef88..01ae855 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -32,6 +32,7 @@ const CategorizedResultsComponent = () => {
     const pathParams = pathName.split(/(\/)+/g);
     const hasResults = pathParams.includes("results");
     const hasDetails = pathParams.includes("details");
+
     if (hasResults && hasDetails) {
       setInDetailsView(true);
     } else if (hasResults && !hasDetails) {
@@ -45,25 +46,22 @@ const CategorizedResultsComponent = () => {
 
   // sets default primaryActiveCategory in Results View
   useEffect(() => {
-    // to handle navigation via Categories.tsx
-    // or when user copy pastes the url to the result page (when wanting to share results related to particular category)
-    // if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
-    //  const chosenCategory = categories.filter((o) => o.id === paramsCategory)[0];
-    // setPrimaryActiveCategory(chosenCategory);
-    // setActiveCategory(chosenCategory);
-    // } else
-    // if (!inDetailsView && !primaryActiveCategory) {
-    // for navigation via search bar
-    // setPrimaryActiveCategory(categories[0]);
-    //setActiveCategory(categories[0]);
-    //}
-  }, [inDetailsView, primaryActiveCategory, categories, paramsCategory, setPrimaryActiveCategory]);
+    // we prioritize handling the case when user copy pastes the url to the result page (when wanting to share results related to particular category)
+    if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
+      const chosenCategory = categories.filter((o) => o.id === paramsCategory)[0];
+      setPrimaryActiveCategory(chosenCategory);
+      setActiveCategory(chosenCategory);
+    } else if (!inDetailsView && !primaryActiveCategory) {
+      // for navigation via search bar
+      setPrimaryActiveCategory(categories[0]);
+      setActiveCategory(categories[0]);
+    }
+  }, [inDetailsView, primaryActiveCategory, paramsCategory, categories, setPrimaryActiveCategory]);
 
   // sets active category in Results view
   // required to handle null and undefined states of primaryActiveCategory and relatedActiveCategory
   useEffect(() => {
     if (!inDetailsView && primaryActiveCategory) {
-      console.log("here");
       setActiveCategory(primaryActiveCategory);
     }
   }, [inDetailsView, primaryActiveCategory]);
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 8471a02..13526ee 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -75,15 +75,13 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [defaultCategories]);
 
-  if (paramsCategory && !primaryActiveCategory) {
-    console.log(" i am happening");
-    setPrimaryActiveCategory(categories[0]);
-  }
-
-  if (!paramsCategory && primaryActiveCategory) {
-    console.log("i run");
-    setPrimaryActiveCategory(null);
-  }
+  useEffect(() => {
+    if (!paramsCategory) {
+      // behaviourally, the user has not requested for information on any particular category!!
+      // everytime I am mount/render the landing page, i clean the slate -
+      setPrimaryActiveCategory(null);
+    }
+  }, [paramsCategory]);
 
   return (
     <CategoryContext.Provider
diff --git a/tsconfig.json b/tsconfig.json
index c04e853..1eb4d30 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,10 +1,6 @@
 {
   "compilerOptions": {
-    "lib": [
-      "dom",
-      "dom.iterable",
-      "esnext",
-    ],
+    "lib": ["dom", "dom.iterable", "esnext"],
     "allowJs": true,
     "skipLibCheck": true,
     "strict": true,
@@ -19,27 +15,16 @@
     "incremental": true,
     "plugins": [
       {
-        "name": "next",
+        "name": "next"
       }
     ],
     "paths": {
-      "@/*": [
-        "./src/*",
-      ],
-      "@api/*": [
-        "./src/app/api/*",
-      ]
+      "@/*": ["./src/*"],
+      "@api/*": ["./src/app/api/*"]
     },
     "downlevelIteration": true,
     "target": "ES2017"
   },
-  "include": [
-    "next-env.d.ts",
-    "**/*.ts",
-    "**/*.tsx",
-    ".next/types/**/*.ts",
-  ],
-  "exclude": [
-    "node_modules",
-  ]
+  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+  "exclude": ["node_modules"]
 }
-- 
GitLab


From 8b55cb9892aa0aa7f024090b9e7b2f4d76a0d59e Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 11 Mar 2025 13:11:55 +0100
Subject: [PATCH 46/48] removed intermediate activeCategory state in
 categorizedresult component

---
 .../app/results/CategorizedResults.tsx        | 43 +++++--------------
 1 file changed, 10 insertions(+), 33 deletions(-)

diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index 01ae855..d441c2b 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -15,7 +15,6 @@ const CategorizedResultsComponent = () => {
   const pathName = usePathname();
   const resultParams = useSearchParams();
   const paramsCategory = resultParams.get("category") ?? undefined;
-  const [activeCategory, setActiveCategory] = useState<Category>();
   const [relatedActiveCategory, setRelatedActiveCategory] = useState<Category | null>(null);
   const [inDetailsView, setInDetailsView] = useState<boolean>(false);
 
@@ -50,73 +49,51 @@ const CategorizedResultsComponent = () => {
     if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
       const chosenCategory = categories.filter((o) => o.id === paramsCategory)[0];
       setPrimaryActiveCategory(chosenCategory);
-      setActiveCategory(chosenCategory);
     } else if (!inDetailsView && !primaryActiveCategory) {
       // for navigation via search bar
       setPrimaryActiveCategory(categories[0]);
-      setActiveCategory(categories[0]);
     }
   }, [inDetailsView, primaryActiveCategory, paramsCategory, categories, setPrimaryActiveCategory]);
 
-  // sets active category in Results view
-  // required to handle null and undefined states of primaryActiveCategory and relatedActiveCategory
-  useEffect(() => {
-    if (!inDetailsView && primaryActiveCategory) {
-      setActiveCategory(primaryActiveCategory);
-    }
-  }, [inDetailsView, primaryActiveCategory]);
-
-  // -----
-
   // ----- Effects for Details View -----
 
   // sets the default relatedActiveCategory
   useEffect(() => {
     if (inDetailsView && !relatedActiveCategory) {
       setRelatedActiveCategory(categories[0]);
-      setActiveCategory(categories[0]);
     }
   }, [inDetailsView, relatedActiveCategory, categories]);
 
-  // sets active category
-  useEffect(() => {
-    if (inDetailsView && relatedActiveCategory) {
-      setActiveCategory(relatedActiveCategory);
-    }
-  }, [inDetailsView, relatedActiveCategory]);
-
   return (
     <>
-      {categoryIsLoading ? (
+      {categoryIsLoading || primaryActiveCategory === null ? (
         <Spinner />
       ) : categoryHasError ? (
         <Error />
       ) : (
         <>
-          {inDetailsView ? (
+          {inDetailsView && relatedActiveCategory !== null ? (
             <>
               <CategoryBar
-                activeCategory={activeCategory}
+                activeCategory={relatedActiveCategory}
+                inDetailsView={inDetailsView}
+                setRelatedActiveCategory={setRelatedActiveCategory}
+              />
+              <ListResults
+                activeCategory={relatedActiveCategory}
                 inDetailsView={inDetailsView}
                 setRelatedActiveCategory={setRelatedActiveCategory}
               />
-              {activeCategory && (
-                <ListResults
-                  activeCategory={activeCategory}
-                  inDetailsView={inDetailsView}
-                  setRelatedActiveCategory={setRelatedActiveCategory}
-                />
-              )}
             </>
           ) : (
             <>
-              <CategoryBar activeCategory={activeCategory} />
+              <CategoryBar activeCategory={primaryActiveCategory} />
               <div className="flex flex-col md:px-10 lg:px-16 py-5 mb-8 bg-whitesmoke h-full">
                 <>
                   <BackBar />
                   <MobileSearch />
                 </>
-                {activeCategory && <ListResults activeCategory={activeCategory} />}
+                <ListResults activeCategory={primaryActiveCategory} />
               </div>
             </>
           )}
-- 
GitLab


From df74ee4981bf5b2384ecfcd01e74f9b3bdf96e6c Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Tue, 11 Mar 2025 14:21:20 +0100
Subject: [PATCH 47/48] added a loader while the results list is fetched and
 cleaned out a unnecessary resetting of primaryActiveCategory

---
 src/components/app/results/CategorizedResults.tsx | 10 +---------
 src/components/app/results/ListResults.tsx        |  9 ++++++++-
 src/contexts/CategoryContext.tsx                  | 12 +-----------
 3 files changed, 10 insertions(+), 21 deletions(-)

diff --git a/src/components/app/results/CategorizedResults.tsx b/src/components/app/results/CategorizedResults.tsx
index d441c2b..06947b0 100644
--- a/src/components/app/results/CategorizedResults.tsx
+++ b/src/components/app/results/CategorizedResults.tsx
@@ -41,23 +41,15 @@ const CategorizedResultsComponent = () => {
     }
   }, [pathName]);
 
-  // ----- Effects for Results View -----
-
-  // sets default primaryActiveCategory in Results View
   useEffect(() => {
-    // we prioritize handling the case when user copy pastes the url to the result page (when wanting to share results related to particular category)
-    if (!inDetailsView && !primaryActiveCategory && paramsCategory) {
+    if (!inDetailsView && paramsCategory) {
       const chosenCategory = categories.filter((o) => o.id === paramsCategory)[0];
       setPrimaryActiveCategory(chosenCategory);
     } else if (!inDetailsView && !primaryActiveCategory) {
-      // for navigation via search bar
       setPrimaryActiveCategory(categories[0]);
     }
   }, [inDetailsView, primaryActiveCategory, paramsCategory, categories, setPrimaryActiveCategory]);
 
-  // ----- Effects for Details View -----
-
-  // sets the default relatedActiveCategory
   useEffect(() => {
     if (inDetailsView && !relatedActiveCategory) {
       setRelatedActiveCategory(categories[0]);
diff --git a/src/components/app/results/ListResults.tsx b/src/components/app/results/ListResults.tsx
index c678063..cf37ff3 100644
--- a/src/components/app/results/ListResults.tsx
+++ b/src/components/app/results/ListResults.tsx
@@ -10,6 +10,7 @@ import Pagination from "@/components/app/results/ListResults/Pagination";
 import ResultItem from "@/components/app/results/ListResults/ResultItem";
 import SelectedFiltersBar from "@/components/app/results/ListResults/SelectedFiltersBar";
 import Error from "@/components/layout/Error";
+import Spinner from "@/components/layout/Spinner";
 import type { Category, FilterEntity, ResultItem as ResultItemType } from "@/types/types";
 import { search, getRorInfo } from "@/utils/apiCall";
 import { detailsItemAtom } from "@/utils/atoms";
@@ -36,6 +37,7 @@ const ListResultsComponent = ({
   const [availableFilters, setAvailableFilters] = useState<FilterEntity[]>([]);
   const [selectedFilters, setSelectedFilters] = useState<FilterEntity[]>([]);
 
+  const [isLoading, setIsLoading] = useState(false);
   const [hasError, setHasError] = useState(false);
 
   const resultParams = useSearchParams();
@@ -77,6 +79,7 @@ const ListResultsComponent = ({
 
   useEffect(() => {
     const updateResultList = async () => {
+      setIsLoading(true);
       const updatedSearchText =
         searchText || detailsText
           ? (searchText ? searchText : "") + " " + (detailsText ? detailsText : "")
@@ -104,6 +107,8 @@ const ListResultsComponent = ({
       } catch (error) {
         console.log("Error on updating result list", error);
         setHasError(true);
+      } finally {
+        setIsLoading(false);
       }
     };
 
@@ -112,7 +117,9 @@ const ListResultsComponent = ({
 
   return (
     <>
-      {hasError ? (
+      {isLoading ? (
+        <Spinner />
+      ) : hasError ? (
         <Error />
       ) : (
         <div
diff --git a/src/contexts/CategoryContext.tsx b/src/contexts/CategoryContext.tsx
index 13526ee..590d7ae 100644
--- a/src/contexts/CategoryContext.tsx
+++ b/src/contexts/CategoryContext.tsx
@@ -1,5 +1,5 @@
 "use client";
-import { useSearchParams } from "next/navigation";
+
 import { createContext, useState, useEffect } from "react";
 
 import { Category, CategoryContextType } from "@/types/types";
@@ -20,8 +20,6 @@ export const CategoryContext = createContext<CategoryContextType>(DEFAULT_CATEGO
 
 // Creating a provider for the category
 export const CategoryProvider = ({ children }: { children: React.ReactNode }) => {
-  const resultParams = useSearchParams();
-  const paramsCategory = resultParams.get("category") ?? undefined;
   // The category state that will be shared
   const [categories, setCategories] = useState<Category[]>([]); // list of categories whose counts will be updated as per search text updates
   const [defaultCategories, setDefaultCategories] = useState<Category[]>([]); // default set of categories and counts
@@ -75,14 +73,6 @@ export const CategoryProvider = ({ children }: { children: React.ReactNode }) =>
     }
   }, [defaultCategories]);
 
-  useEffect(() => {
-    if (!paramsCategory) {
-      // behaviourally, the user has not requested for information on any particular category!!
-      // everytime I am mount/render the landing page, i clean the slate -
-      setPrimaryActiveCategory(null);
-    }
-  }, [paramsCategory]);
-
   return (
     <CategoryContext.Provider
       value={{
-- 
GitLab


From c8d25d32e95ac002f8a1cb3652fc441c604edac3 Mon Sep 17 00:00:00 2001
From: FionaDmello <40391218+FionaDmello@users.noreply.github.com>
Date: Wed, 12 Mar 2025 14:17:00 +0100
Subject: [PATCH 48/48] minor i18n update

---
 src/app/[locale]/layout.tsx | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index e790321..a040912 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -1,7 +1,6 @@
 import type { Metadata, Viewport } from "next";
 import "./globals.css";
 import { notFound } from "next/navigation";
-import { notFound } from "next/navigation";
 import { useTranslations, useMessages, NextIntlClientProvider } from "next-intl";
 import React from "react";
 
-- 
GitLab