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