From 1bab07d5d5015d387522f94bc2e634c147163ed8 Mon Sep 17 00:00:00 2001 From: Majk Shkurti Date: Mon, 2 Oct 2023 11:32:53 +0200 Subject: [PATCH] fix: middleware --- .../app/[lng]/(dashboard)/projects/page.tsx | 2 +- .../(dashboard)/settings/account/layout.tsx | 62 ++++ .../(dashboard)/settings/account/page.tsx | 5 + .../settings/account/preferences/page.tsx | 148 +++++++++ .../Profile.tsx => account/profile/page.tsx} | 0 .../app/[lng]/(dashboard)/settings/layout.tsx | 218 ++++++------- .../app/[lng]/(dashboard)/settings/page.tsx | 2 +- .../settings/settings/PersonalPreferences.tsx | 187 ----------- .../(dashboard)/settings/settings/layout.tsx | 29 -- .../(dashboard)/settings/settings/page.tsx | 3 - apps/web/app/[lng]/server/page.tsx | 18 -- apps/web/components/@mui/ThemeRegistry.tsx | 29 +- apps/web/components/common/PopperMenu.tsx | 3 +- apps/web/components/dashboard/Sidebar.tsx | 15 +- apps/web/lib/api/fetcher.ts | 15 +- apps/web/lib/api/system.ts | 39 +++ apps/web/lib/constants.ts | 3 + apps/web/lib/validations/system.ts | 10 +- apps/web/middleware.ts | 10 +- apps/web/middlewares/withCookies.ts | 74 +++++ .../{withLanguages.ts => withLanguage.ts} | 18 +- apps/web/middlewares/withOrganization.ts | 32 +- apps/web/package.json | 1 + apps/web/types/common/index.ts | 2 +- apps/web/types/common/navigation.d.ts | 6 + packages/eslint-config-p4b/package.json | 4 +- packages/ui/components/Icon.tsx | 6 + pnpm-lock.yaml | 298 ++++++++++-------- 28 files changed, 689 insertions(+), 550 deletions(-) create mode 100644 apps/web/app/[lng]/(dashboard)/settings/account/layout.tsx create mode 100644 apps/web/app/[lng]/(dashboard)/settings/account/page.tsx create mode 100644 apps/web/app/[lng]/(dashboard)/settings/account/preferences/page.tsx rename apps/web/app/[lng]/(dashboard)/settings/{settings/Profile.tsx => account/profile/page.tsx} (100%) delete mode 100644 apps/web/app/[lng]/(dashboard)/settings/settings/PersonalPreferences.tsx delete mode 100644 apps/web/app/[lng]/(dashboard)/settings/settings/layout.tsx delete mode 100644 apps/web/app/[lng]/(dashboard)/settings/settings/page.tsx delete mode 100644 apps/web/app/[lng]/server/page.tsx create mode 100644 apps/web/lib/api/system.ts create mode 100644 apps/web/middlewares/withCookies.ts rename apps/web/middlewares/{withLanguages.ts => withLanguage.ts} (77%) create mode 100644 apps/web/types/common/navigation.d.ts diff --git a/apps/web/app/[lng]/(dashboard)/projects/page.tsx b/apps/web/app/[lng]/(dashboard)/projects/page.tsx index d5dd6459..0f6713c5 100644 --- a/apps/web/app/[lng]/(dashboard)/projects/page.tsx +++ b/apps/web/app/[lng]/(dashboard)/projects/page.tsx @@ -54,7 +54,7 @@ const Projects = () => { /> - + { + const pathname = usePathname(); + + const navigation: NavItem[] = [ + { + link: "/profile", + icon: ICON_NAME.USER, + label: "Profile", + current: pathname?.includes("/profile"), + }, + { + link: "/preferences", + icon: ICON_NAME.SETTINGS, + label: "Preferences", + current: pathname?.includes("/preferences"), + }, + ]; + return ( + <> + item.current)?.link || false} + variant="fullWidth" + scrollButtons + > + {navigation.map((item) => ( + + } + iconPosition="start" + label={item.label} + value={item.link} + sx={{ + ...(item.current && { + color: "primary.main", + fontWeight: "bold", + }), + }} + /> + ))} + + {props.children} + + ); +}; + +export default AccountLayout; diff --git a/apps/web/app/[lng]/(dashboard)/settings/account/page.tsx b/apps/web/app/[lng]/(dashboard)/settings/account/page.tsx new file mode 100644 index 00000000..15061e11 --- /dev/null +++ b/apps/web/app/[lng]/(dashboard)/settings/account/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default async function Organization({}) { + return redirect("/settings/account/profile"); +} diff --git a/apps/web/app/[lng]/(dashboard)/settings/account/preferences/page.tsx b/apps/web/app/[lng]/(dashboard)/settings/account/preferences/page.tsx new file mode 100644 index 00000000..1448ce77 --- /dev/null +++ b/apps/web/app/[lng]/(dashboard)/settings/account/preferences/page.tsx @@ -0,0 +1,148 @@ +"use client"; + +import React, { useContext, useEffect, useState } from "react"; +import { + Typography, + Box, + MenuItem, + useTheme, + Stack, + TextField, + InputAdornment, + PaletteMode, +} from "@mui/material"; +import Cookies from "js-cookie"; +import { THEME_COOKIE_NAME } from "@/lib/constants"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { ICON_NAME, Icon } from "@p4b/ui/components/Icon"; +import DarkModeIcon from "@mui/icons-material/Brightness4"; +import LightModeIcon from "@mui/icons-material/Brightness7"; +import { + systemSettingsSchemaUpdate, + type SystemSettingsUpdate, +} from "@/lib/validations/system"; +import { updateSystemSettings } from "@/lib/api/system"; +import { toast } from "react-toastify"; +import { ColorModeContext } from "@/components/@mui/ThemeRegistry"; +const AccountPreferences = () => { + const [isBusy, setIsBusy] = useState(false); + const theme = useTheme(); + + const { changeColorMode } = useContext(ColorModeContext); + const themeModes = ["dark", "light"] as const; + const units = ["metric", "imperial"] as const; + const languages = ["en", "de"] as const; + + const { + register: registerSystemSettings, + handleSubmit: handleSystemSettingsSubmit, + watch: watchSystemSettings, + } = useForm({ + mode: "onChange", + resolver: zodResolver(systemSettingsSchemaUpdate), + }); + async function onSystemSettingsSubmit(systemSettings: SystemSettingsUpdate) { + try { + setIsBusy(true); + await updateSystemSettings(systemSettings); + changeColorMode(systemSettings.client_theme as PaletteMode); + Cookies.set(THEME_COOKIE_NAME, systemSettings.client_theme); + } catch (error) { + toast.error("Failed to update system settings"); + } finally { + setIsBusy(false); + } + } + + useEffect(() => { + const subscription = watchSystemSettings(() => { + handleSystemSettingsSubmit(onSystemSettingsSubmit)(); + }); + return () => subscription.unsubscribe(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [watchSystemSettings, handleSystemSettingsSubmit]); + + return ( + + + + + + Client + + + + + + ), + }} + > + {languages.map((lng) => ( + + {lng} + + ))} + + + {theme.palette.mode === "dark" ? ( + + ) : ( + + )} + + ), + }} + > + {themeModes.map((theme) => ( + + {theme} + + ))} + + + + + ), + }} + > + {units.map((unit) => ( + + {unit} + + ))} + + + + + ); +}; + +export default AccountPreferences; diff --git a/apps/web/app/[lng]/(dashboard)/settings/settings/Profile.tsx b/apps/web/app/[lng]/(dashboard)/settings/account/profile/page.tsx similarity index 100% rename from apps/web/app/[lng]/(dashboard)/settings/settings/Profile.tsx rename to apps/web/app/[lng]/(dashboard)/settings/account/profile/page.tsx diff --git a/apps/web/app/[lng]/(dashboard)/settings/layout.tsx b/apps/web/app/[lng]/(dashboard)/settings/layout.tsx index 539fbcaf..9381d76a 100644 --- a/apps/web/app/[lng]/(dashboard)/settings/layout.tsx +++ b/apps/web/app/[lng]/(dashboard)/settings/layout.tsx @@ -2,19 +2,23 @@ import React from "react"; import { - Typography, useTheme, Box, - Divider, Container, Grid, - IconButton, - Menu, - MenuItem, + Paper, + List, + ListItemButton, + ListItemIcon, + ListItemText, + Link, + ListItem, + Typography, } from "@mui/material"; -import Link from "next/link"; +import NextLink from "next/link"; import { usePathname } from "next/navigation"; import { Icon, ICON_NAME } from "@p4b/ui/components/Icon"; +import { NavItem } from "@/types/common/navigation"; interface SettingsLayoutProps { children: React.ReactNode; @@ -35,122 +39,106 @@ const SettingsLayout = (props: SettingsLayoutProps) => { setAnchorEl(null); }; - const linkStyle = (page) => - pathname.slice(3) === `/settings/${page}` - ? { - cursor: "pointer", - backgroundColor: theme.palette.primary.light + "14", - padding: `${theme.spacing(1)} ${theme.spacing(4)}`, - } - : { - cursor: "pointer", - "&:hover": { - backgroundColor: theme.palette.secondary.light + "50", - }, - padding: `${theme.spacing(1)} ${theme.spacing(4)}`, - }; + const navigation: NavItem[] = [ + { + link: "/settings/account", + icon: ICON_NAME.USER, + label: "Account", + current: pathname?.includes("/account"), + }, + { + link: "/settings/organization", + icon: ICON_NAME.ORGANIZATION, + label: "Organization", + current: pathname?.includes("/organization"), + }, + { + link: "/settings/subscription", + icon: ICON_NAME.CREDIT_CARD, + label: "Subscriptions", + current: pathname?.includes("/subscription"), + }, + ]; return ( - - + - - - - - - Settings + + + + + - Organization - - - Subscription - - - Settings - - - - - -
- - - Organization - - - - - - Subscription - - - - - - Settings - - - -
-
- -
{children}
-
+ {navigation.map((item) => ( + + + + + + + + + + + ))} + +
+
+ + {children} - - + + ); }; diff --git a/apps/web/app/[lng]/(dashboard)/settings/page.tsx b/apps/web/app/[lng]/(dashboard)/settings/page.tsx index 596ddafa..16028135 100644 --- a/apps/web/app/[lng]/(dashboard)/settings/page.tsx +++ b/apps/web/app/[lng]/(dashboard)/settings/page.tsx @@ -1,5 +1,5 @@ import { redirect } from "next/navigation"; export default async function Organization({}) { - return redirect("/settings/organization"); + return redirect("/settings/account"); } diff --git a/apps/web/app/[lng]/(dashboard)/settings/settings/PersonalPreferences.tsx b/apps/web/app/[lng]/(dashboard)/settings/settings/PersonalPreferences.tsx deleted file mode 100644 index 8878a4f4..00000000 --- a/apps/web/app/[lng]/(dashboard)/settings/settings/PersonalPreferences.tsx +++ /dev/null @@ -1,187 +0,0 @@ -"use client"; - -import React from "react"; -import { v4 } from "uuid"; -import type { Option } from "@p4b/types/atomicComponents"; -import { useTranslation } from "react-i18next"; -import { useRouter, usePathname } from "next/navigation"; -import { - Typography, - Divider, - Switch, - Box, - Card, - Select, - MenuItem, - useTheme, -} from "@mui/material"; -import type { SelectChangeEvent } from "@mui/material"; - -function removeNrOfElementsFromArray(pathname: string, len: number) { - const parts = pathname.split("/"); - if (parts.length >= len) { - parts.splice(0, len); - } - return parts.join("/"); -} - -const PersonalPreferences = () => { - const theme = useTheme(); - - const { i18n } = useTranslation("home"); - const router = useRouter(); - const pathname = usePathname(); - - const options: Option[] = [ - { - label: "Eng", - value: "en", - }, - { - label: "Deu", - value: "de", - }, - ]; - - const changeLanguage = (locale: string) => { - i18n.changeLanguage(locale); - const pathnameWithoutLocale = removeNrOfElementsFromArray(pathname, 2); - router.push(`/${locale}/${pathnameWithoutLocale}`); - }; - - return ( -
- - - - Language - Manage the sites language - - - - Language - - - - - - Notification - - Manage the notification settings & modes - - - - - Email - - - - Phone - - - - - - Theme - - Chose between Dark & Light modes - - - - - Dark - - - - - - Newsletter - - Subscribe to our Newsletter - - - - - Subscribe - - - - -
- ); -}; - -export default PersonalPreferences; diff --git a/apps/web/app/[lng]/(dashboard)/settings/settings/layout.tsx b/apps/web/app/[lng]/(dashboard)/settings/settings/layout.tsx deleted file mode 100644 index e29960ea..00000000 --- a/apps/web/app/[lng]/(dashboard)/settings/settings/layout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react"; -import OrganizationTabs from "@/components/dashboard/settings/OrganizationTabs"; -import { Box } from "@mui/material"; - -const tabsData = [ - { - child: "Profile", - name: "Profile", - }, - { - child: "PersonalPreferences", - name: "Personal Preferences", - }, -]; - -const Settings = () => { - const tabs = tabsData.map(({ child, name }) => { - const Component = React.lazy(() => import(`@/app/[lng]/(dashboard)/settings/settings/${child}`)); - - return { - child: , - name, - }; - }); - - return ; -}; - -export default Settings; \ No newline at end of file diff --git a/apps/web/app/[lng]/(dashboard)/settings/settings/page.tsx b/apps/web/app/[lng]/(dashboard)/settings/settings/page.tsx deleted file mode 100644 index db536f9d..00000000 --- a/apps/web/app/[lng]/(dashboard)/settings/settings/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function PrivacyPreference() { - return
PrivacyPreferences
; -} diff --git a/apps/web/app/[lng]/server/page.tsx b/apps/web/app/[lng]/server/page.tsx deleted file mode 100644 index 9b07a1bd..00000000 --- a/apps/web/app/[lng]/server/page.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import UserCard from "@/app/[lng]/components/UserCard"; -import { options } from "@/app/api/auth/[...nextauth]/options"; -import { getServerSession } from "next-auth/next"; -import { redirect } from "next/navigation"; - -export default async function ServerPage() { - const session = await getServerSession(options); - - if (!session) { - redirect("/api/auth/signin?callbackUrl=/server"); - } - - return ( -
- -
- ); -} diff --git a/apps/web/components/@mui/ThemeRegistry.tsx b/apps/web/components/@mui/ThemeRegistry.tsx index 1d8e6ba0..c9337102 100644 --- a/apps/web/components/@mui/ThemeRegistry.tsx +++ b/apps/web/components/@mui/ThemeRegistry.tsx @@ -1,37 +1,24 @@ "use client"; import React from "react"; +import Cookies from "js-cookie"; import NextAppDirEmotionCacheProvider from "./EmotionCache"; import ThemeProvider from "@p4b/ui/theme/ThemeProvider"; +import type { PaletteMode } from "@mui/material"; +import { THEME_COOKIE_NAME } from "@/lib/constants"; export const ColorModeContext = React.createContext({ - // eslint-disable-next-line @typescript-eslint/no-empty-function - toggleColorMode: () => {}, + changeColorMode: (_mode: PaletteMode) => {}, }); export default function ThemeRegistry({ children, }: { children: React.ReactNode; }) { - let theme = "dark"; - if (typeof window !== "undefined") { - theme = localStorage.getItem("theme") || "dark"; - } - - const [mode, setMode] = React.useState<"light" | "dark">( - theme as "light" | "dark", - ); + const defaultMode = Cookies.get(THEME_COOKIE_NAME) as PaletteMode + const [mode, setMode] = React.useState(defaultMode); const colorMode = React.useMemo( () => ({ - toggleColorMode: () => { - setMode((prevMode) => { - localStorage.setItem( - "theme", - prevMode === "light" ? "dark" : "light", - ); - if (prevMode === "light") { - return "dark"; - } - return "light"; - }); + changeColorMode: (mode: PaletteMode) => { + setMode(mode); }, }), [], diff --git a/apps/web/components/common/PopperMenu.tsx b/apps/web/components/common/PopperMenu.tsx index 2c84a71d..f784a55b 100644 --- a/apps/web/components/common/PopperMenu.tsx +++ b/apps/web/components/common/PopperMenu.tsx @@ -51,9 +51,10 @@ export default function PopperMenu(props: PopperMenuProps) { { + onClick={(event) => { props.onSelect(item); setPopperMenuOpen(false); + event.stopPropagation(); }} sx={{ ...(item.color && { diff --git a/apps/web/components/dashboard/Sidebar.tsx b/apps/web/components/dashboard/Sidebar.tsx index d8ad23c5..1e09f51f 100644 --- a/apps/web/components/dashboard/Sidebar.tsx +++ b/apps/web/components/dashboard/Sidebar.tsx @@ -1,3 +1,4 @@ +import { NavItem } from "@/types/common/navigation"; import type { CSSObject, Theme } from "@mui/material"; import { Link, @@ -14,13 +15,6 @@ import { Icon, ICON_NAME } from "@p4b/ui/components/Icon"; import NextLink from "next/link"; import { usePathname } from "next/navigation"; -export interface NavbarItem { - link: string; - icon: ICON_NAME; - label: string; - current: boolean; -} - const openedMixin = (theme: Theme): CSSObject => ({ transition: theme.transitions.create("width", { easing: theme.transitions.easing.sharp, @@ -63,7 +57,7 @@ const DashboardSidebar = (props: Props) => { onClose: () => null, }; - const navigation: NavbarItem[] = [ + const navigation: NavItem[] = [ { link: "/home", icon: ICON_NAME.HOUSE, @@ -125,7 +119,7 @@ const DashboardSidebar = (props: Props) => { overflowX: "hidden", backgroundColor: theme.palette.background.paper, borderRight: "1px solid rgba(58, 53, 65, 0.12)", - } + }, }} > @@ -148,9 +142,6 @@ const DashboardSidebar = (props: Props) => { sx={{ minHeight: 48, }} - onClick={() => { - console.log("click"); - }} > { let queryParams, url; @@ -22,14 +22,13 @@ export const fetcher = async (params) => { const urlWithParams = queryParams ? `${url}?${new URLSearchParams(queryParams)}` : url; - // console.log("urlWithParams", urlWithParams); const options = {}; - // const session = await getSession(); - // if (session?.access_token) { - // options["headers"] = { - // Authorization: `Bearer ${session.access_token}`, - // }; - // } + const session = await getSession(); + if (session?.access_token) { + options["headers"] = { + Authorization: `Bearer ${session.access_token}`, + }; + } const res = await fetch(urlWithParams, options); // If the status code is not in the range 200-299, diff --git a/apps/web/lib/api/system.ts b/apps/web/lib/api/system.ts new file mode 100644 index 00000000..0d4a9098 --- /dev/null +++ b/apps/web/lib/api/system.ts @@ -0,0 +1,39 @@ +import { fetcher } from "@/lib/api/fetcher"; +import type { + SystemSettings, + SystemSettingsUpdate, +} from "@/lib/validations/system"; +import useSWR from "swr"; + +export const SYSTEM_API_BASE_URL = new URL( + "api/v2/system", + process.env.NEXT_PUBLIC_API_URL, +).href; + +export const useSystemSettings = () => { + const { data, isLoading, error, mutate, isValidating } = + useSWR(`${SYSTEM_API_BASE_URL}/settings`, fetcher); + return { + systemSettings: data, + isLoading: isLoading, + isError: error, + mutate, + isValidating, + }; +}; + +export const updateSystemSettings = async ( + system_settings: SystemSettingsUpdate, +): Promise => { + const response = await fetch(`${SYSTEM_API_BASE_URL}/settings`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(system_settings), + }); + if (!response.ok) { + throw new Error("Failed to update system settings"); + } + return await response.json(); +}; diff --git a/apps/web/lib/constants.ts b/apps/web/lib/constants.ts index 4d3a3c1c..2157c6a4 100644 --- a/apps/web/lib/constants.ts +++ b/apps/web/lib/constants.ts @@ -1,2 +1,5 @@ export const MAPBOX_TOKEN = "pk.eyJ1IjoiZWxpYXNwYWphcmVzIiwiYSI6ImNqOW1scnVyOTRxcWwzMm5yYWhta2N2cXcifQ.aDCgidtC9cjf_O75frn9lA"; + + +export const THEME_COOKIE_NAME = "client_theme"; diff --git a/apps/web/lib/validations/system.ts b/apps/web/lib/validations/system.ts index f0a9a62c..6bb30714 100644 --- a/apps/web/lib/validations/system.ts +++ b/apps/web/lib/validations/system.ts @@ -8,19 +8,17 @@ export const systemSettingsSchemaBase = z.object({ client_theme: clientThemeEnum, preferred_language: preferredLanguageEnum, unit: unitEnum, - user_id: z.string(), }); export const systemSettingsSchema = systemSettingsSchemaBase.extend({ updated_at: z.string(), created_at: z.string(), + user_id: z.string(), id: z.string(), }); export const systemSettingsSchemaUpdate = systemSettingsSchemaBase.partial(); -export type SystemSettingsSchema = z.infer; -export type SystemSettingsSchemaUpdate = z.infer< - typeof systemSettingsSchemaUpdate ->; -export type SystemSettingsSchemaBase = z.infer; +export type SystemSettings = z.infer; +export type SystemSettingsUpdate = z.infer; +export type SystemSettingsBase = z.infer; diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index df29343f..003251d7 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -1,6 +1,12 @@ import { stackMiddlewares } from "@/middlewares/stackMiddlewares"; -import { withLanguages } from "@/middlewares/withLanguages"; import { withAuth } from "@/middlewares/withAuth"; +import { withCookies } from "@/middlewares/withCookies"; +import { withLanguage } from "@/middlewares/withLanguage"; import { withOrganization } from "@/middlewares/withOrganization"; -export default stackMiddlewares([withLanguages, withAuth, withOrganization]); +export default stackMiddlewares([ + withCookies, + withLanguage, + withAuth, + withOrganization, +]); diff --git a/apps/web/middlewares/withCookies.ts b/apps/web/middlewares/withCookies.ts new file mode 100644 index 00000000..04b15759 --- /dev/null +++ b/apps/web/middlewares/withCookies.ts @@ -0,0 +1,74 @@ +import type { + NextFetchEvent, + NextMiddleware, + NextRequest, + NextResponse, +} from "next/server"; + +import type { MiddlewareFactory } from "./types"; +import { cookieName as lngCookieName } from "@/app/i18/settings"; +import { THEME_COOKIE_NAME as themeCookieName } from "@/lib/constants"; +import { getToken } from "next-auth/jwt"; +import { refreshAccessToken } from "@/app/api/auth/[...nextauth]/options"; +import { SYSTEM_API_BASE_URL } from "@/lib/api/system"; + +const excluded = [ + "/api", + "/_next/static", + "/_next/image", + "/assets", + "/favicon.ico", + "/sw.js", +]; +export const withCookies: MiddlewareFactory = (next: NextMiddleware) => { + return async (request: NextRequest, _next: NextFetchEvent) => { + const { pathname } = request.nextUrl; + + if (excluded.some((path) => pathname.startsWith(path))) + return next(request, _next); + + // Check for cookies. If not present, try to load theme from the api (!when logged in) + const nextAuthSecret = process.env.NEXTAUTH_SECRET; + if ( + (!request.cookies.has(themeCookieName) || + !request.cookies.has(lngCookieName)) && + nextAuthSecret + ) { + const token = await getToken({ + req: request, + secret: nextAuthSecret, + }); + const isAuthorized = !!token; + if (isAuthorized) { + const refreshedToken = await refreshAccessToken(token); + try { + // System Preferences cookies + const systemPreferences = await fetch( + `${SYSTEM_API_BASE_URL}/settings`, + { + headers: { + Authorization: `Bearer ${refreshedToken.access_token}`, + }, + }, + ); + if (systemPreferences.ok) { + const preferences = await systemPreferences.json(); + const response = (await next(request, _next)) as NextResponse; + if (preferences?.client_theme) + response.cookies.set(themeCookieName, preferences.client_theme); + if (preferences?.preferred_language) + response.cookies.set( + lngCookieName, + preferences.preferred_language, + ); + return response; + } + } catch (error) { + console.log("Error loading preferences from api", error); + } + } + } + + return next(request, _next); + }; +}; diff --git a/apps/web/middlewares/withLanguages.ts b/apps/web/middlewares/withLanguage.ts similarity index 77% rename from apps/web/middlewares/withLanguages.ts rename to apps/web/middlewares/withLanguage.ts index 983b8fa6..9724dea1 100644 --- a/apps/web/middlewares/withLanguages.ts +++ b/apps/web/middlewares/withLanguage.ts @@ -3,8 +3,11 @@ import type { NextFetchEvent, NextMiddleware, NextRequest } from "next/server"; import type { MiddlewareFactory } from "./types"; import acceptLanguage from "accept-language"; -import { fallbackLng, languages, cookieName } from "@/app/i18/settings"; - +import { + fallbackLng, + languages, + cookieName as lngCookieName, +} from "@/app/i18/settings"; acceptLanguage.languages(languages); const excluded = [ @@ -15,16 +18,19 @@ const excluded = [ "/favicon.ico", "/sw.js", ]; -export const withLanguages: MiddlewareFactory = (next: NextMiddleware) => { +export const withLanguage: MiddlewareFactory = ( + next: NextMiddleware, +) => { return async (request: NextRequest, _next: NextFetchEvent) => { const { pathname } = request.nextUrl; if (excluded.some((path) => pathname.startsWith(path))) return next(request, _next); + // Language redirect let lng; - if (request.cookies.has(cookieName)) - lng = acceptLanguage.get(request.cookies.get(cookieName)?.value); + if (request.cookies.has(lngCookieName)) + lng = acceptLanguage.get(request.cookies.get(lngCookieName)?.value); if (!lng) lng = acceptLanguage.get(request.headers.get("Accept-Language")); if (!lng) lng = fallbackLng; if ( @@ -40,7 +46,7 @@ export const withLanguages: MiddlewareFactory = (next: NextMiddleware) => { ); const response = (await next(request, _next)) as NextResponse; if (response && lngInReferer) - response.cookies.set(cookieName, lngInReferer); + response.cookies.set(lngCookieName, lngInReferer); return response; } diff --git a/apps/web/middlewares/withOrganization.ts b/apps/web/middlewares/withOrganization.ts index 15df91ca..a4d0ba8c 100644 --- a/apps/web/middlewares/withOrganization.ts +++ b/apps/web/middlewares/withOrganization.ts @@ -35,22 +35,26 @@ export const withOrganization: MiddlewareFactory = (next: NextMiddleware) => { secret: process.env.NEXTAUTH_SECRET, }); if (!token) return await next(request, _next); - const refreshedToken = await refreshAccessToken(token); - const checkOrganization = await fetch( - `${USERS_API_BASE_URL}/organization`, - { - headers: { - Authorization: `Bearer ${refreshedToken.access_token}`, + try { + const refreshedToken = await refreshAccessToken(token); + const checkOrganization = await fetch( + `${USERS_API_BASE_URL}/organization`, + { + headers: { + Authorization: `Bearer ${refreshedToken.access_token}`, + }, }, - }, - ); - if (checkOrganization.ok) { - const organization = await checkOrganization.json(); - if (organization?.id) { - const response = (await next(request, _next)) as NextResponse; - response.cookies.set("organization", organization.id); - return response; + ); + if (checkOrganization.ok) { + const organization = await checkOrganization.json(); + if (organization?.id) { + const response = (await next(request, _next)) as NextResponse; + response.cookies.set("organization", organization.id); + return response; + } } + } catch (error) { + console.error("Error while fetching organization", error); } const organizationUrl = new URL(`${basePath}${organizationPage}`, origin); diff --git a/apps/web/package.json b/apps/web/package.json index 2ee503ae..72415752 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -43,6 +43,7 @@ "i18next": "^23.4.4", "i18next-browser-languagedetector": "^7.1.0", "i18next-resources-to-backend": "^1.1.4", + "js-cookie": "^3.0.5", "mapbox-gl": "^2.15.0", "maplibre-gl": "^3.1.0", "next": "v13.4.12", diff --git a/apps/web/types/common/index.ts b/apps/web/types/common/index.ts index 1ec13b41..543a9443 100644 --- a/apps/web/types/common/index.ts +++ b/apps/web/types/common/index.ts @@ -5,4 +5,4 @@ export enum ContentActions { DOWNLOAD = "download", SHARE = "share", DELETE = "delete", - } \ No newline at end of file + } diff --git a/apps/web/types/common/navigation.d.ts b/apps/web/types/common/navigation.d.ts new file mode 100644 index 00000000..7341ab55 --- /dev/null +++ b/apps/web/types/common/navigation.d.ts @@ -0,0 +1,6 @@ +export interface NavItem { + link: string; + icon?: ICON_NAME; + label: string; + current: boolean; +} diff --git a/packages/eslint-config-p4b/package.json b/packages/eslint-config-p4b/package.json index abf91f7d..ea7d892b 100644 --- a/packages/eslint-config-p4b/package.json +++ b/packages/eslint-config-p4b/package.json @@ -16,7 +16,7 @@ "eslint-config-turbo": "1.10.12", "eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-you-dont-need-lodash-underscore": "^6.12.0", - "@typescript-eslint/eslint-plugin": "^5.52.0", - "@typescript-eslint/parser": "^5.52.0" + "@typescript-eslint/eslint-plugin": "^6.7.3", + "@typescript-eslint/parser": "^6.7.3" } } \ No newline at end of file diff --git a/packages/ui/components/Icon.tsx b/packages/ui/components/Icon.tsx index 208b429e..92d825a1 100644 --- a/packages/ui/components/Icon.tsx +++ b/packages/ui/components/Icon.tsx @@ -64,6 +64,8 @@ import { faTable, faBarsProgress, faCircleQuestion, + faCreditCard, + faRulerCombined, } from "@fortawesome/free-solid-svg-icons"; @@ -151,6 +153,8 @@ export enum ICON_NAME { UPLOAD = "upload", TABLE = "table", BARS_PROGRESS = "bar-progress", + CREDIT_CARD = "credit-card", + RULES_COMBINED = "rules-combined", // Brand icons GOOGLE = "google", MICROSOFT = "microsoft", @@ -231,6 +235,8 @@ const nameToIcon: { [k in ICON_NAME]: IconDefinition } = { [ICON_NAME.TABLE]: faTable, [ICON_NAME.BARS_PROGRESS]: faBarsProgress, [ICON_NAME.EXTERNAL_LINK]: faExternalLinkAlt, + [ICON_NAME.CREDIT_CARD]: faCreditCard, + [ICON_NAME.RULES_COMBINED]: faRulerCombined, // Brand icons [ICON_NAME.GOOGLE]: faGoogle, [ICON_NAME.MICROSOFT]: faMicrosoft, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5c04d4a..5e76f072 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -226,6 +226,9 @@ importers: i18next-resources-to-backend: specifier: ^1.1.4 version: 1.1.4 + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 mapbox-gl: specifier: ^2.15.0 version: 2.15.0 @@ -333,11 +336,11 @@ importers: packages/eslint-config-p4b: devDependencies: '@typescript-eslint/eslint-plugin': - specifier: ^5.52.0 - version: 5.52.0(@typescript-eslint/parser@5.52.0)(eslint@8.47.0)(typescript@5.2.2) + specifier: ^6.7.3 + version: 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.47.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: ^5.52.0 - version: 5.52.0(eslint@8.47.0)(typescript@5.2.2) + specifier: ^6.7.3 + version: 6.7.3(eslint@8.47.0)(typescript@5.2.2) eslint: specifier: ^8.47.0 version: 8.47.0 @@ -358,7 +361,7 @@ importers: version: 7.33.2(eslint@8.47.0) eslint-plugin-unused-imports: specifier: ^2.0.0 - version: 2.0.0(@typescript-eslint/eslint-plugin@5.52.0)(eslint@8.47.0) + version: 2.0.0(@typescript-eslint/eslint-plugin@6.7.3)(eslint@8.47.0) eslint-plugin-you-dont-need-lodash-underscore: specifier: ^6.12.0 version: 6.12.0 @@ -3735,7 +3738,7 @@ packages: '@babel/runtime': 7.23.1 '@emotion/is-prop-valid': 1.2.1 '@mui/types': 7.2.4(@types/react@18.2.18) - '@mui/utils': 5.13.7(react@18.2.0) + '@mui/utils': 5.14.11(@types/react@18.2.18)(react@18.2.0) '@popperjs/core': 2.11.8 '@types/react': 18.2.18 clsx: 1.2.1 @@ -7102,8 +7105,8 @@ packages: '@types/yargs-parser': 21.0.1 dev: true - /@typescript-eslint/eslint-plugin@5.52.0(@typescript-eslint/parser@5.52.0)(eslint@8.47.0)(typescript@5.2.2): - resolution: {integrity: sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==} + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.50.0)(typescript@5.2.2): + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -7113,16 +7116,16 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.52.0(eslint@8.47.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 5.52.0 - '@typescript-eslint/type-utils': 5.52.0(eslint@8.47.0)(typescript@5.2.2) - '@typescript-eslint/utils': 5.52.0(eslint@8.47.0)(typescript@5.2.2) + '@eslint-community/regexpp': 4.9.0 + '@typescript-eslint/parser': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) debug: 4.3.4(supports-color@5.5.0) - eslint: 8.47.0 - grapheme-splitter: 1.0.4 + eslint: 8.50.0 + graphemer: 1.4.0 ignore: 5.2.4 natural-compare-lite: 1.4.0 - regexpp: 3.2.0 semver: 7.5.4 tsutils: 3.21.0(typescript@5.2.2) typescript: 5.2.2 @@ -7130,29 +7133,30 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.50.0)(typescript@5.2.2): - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.47.0)(typescript@5.2.2): + resolution: {integrity: sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==} + engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: '@eslint-community/regexpp': 4.9.0 - '@typescript-eslint/parser': 5.62.0(eslint@8.50.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.3(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.7.3 + '@typescript-eslint/type-utils': 6.7.3(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.3(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.3 debug: 4.3.4(supports-color@5.5.0) - eslint: 8.50.0 + eslint: 8.47.0 graphemer: 1.4.0 ignore: 5.2.4 - natural-compare-lite: 1.4.0 + natural-compare: 1.4.0 semver: 7.5.4 - tsutils: 3.21.0(typescript@5.2.2) + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -7171,8 +7175,8 @@ packages: - typescript dev: true - /@typescript-eslint/parser@5.52.0(eslint@8.47.0)(typescript@5.2.2): - resolution: {integrity: sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==} + /@typescript-eslint/parser@5.62.0(eslint@8.47.0)(typescript@5.2.2): + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -7181,9 +7185,9 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.52.0 - '@typescript-eslint/types': 5.52.0 - '@typescript-eslint/typescript-estree': 5.52.0(typescript@5.2.2) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) debug: 4.3.4(supports-color@5.5.0) eslint: 8.47.0 typescript: 5.2.2 @@ -7211,12 +7215,25 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@5.52.0: - resolution: {integrity: sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/parser@6.7.3(eslint@8.47.0)(typescript@5.2.2): + resolution: {integrity: sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true dependencies: - '@typescript-eslint/types': 5.52.0 - '@typescript-eslint/visitor-keys': 5.52.0 + '@typescript-eslint/scope-manager': 6.7.3 + '@typescript-eslint/types': 6.7.3 + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.3 + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.47.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color dev: true /@typescript-eslint/scope-manager@5.62.0: @@ -7227,8 +7244,16 @@ packages: '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/type-utils@5.52.0(eslint@8.47.0)(typescript@5.2.2): - resolution: {integrity: sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==} + /@typescript-eslint/scope-manager@6.7.3: + resolution: {integrity: sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.7.3 + '@typescript-eslint/visitor-keys': 6.7.3 + dev: true + + /@typescript-eslint/type-utils@5.62.0(eslint@8.50.0)(typescript@5.2.2): + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -7237,48 +7262,48 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.52.0(typescript@5.2.2) - '@typescript-eslint/utils': 5.52.0(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) debug: 4.3.4(supports-color@5.5.0) - eslint: 8.47.0 + eslint: 8.50.0 tsutils: 3.21.0(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/type-utils@5.62.0(eslint@8.50.0)(typescript@5.2.2): - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/type-utils@6.7.3(eslint@8.47.0)(typescript@5.2.2): + resolution: {integrity: sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==} + engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - eslint: '*' + eslint: ^7.0.0 || ^8.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.3(eslint@8.47.0)(typescript@5.2.2) debug: 4.3.4(supports-color@5.5.0) - eslint: 8.50.0 - tsutils: 3.21.0(typescript@5.2.2) + eslint: 8.47.0 + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@5.52.0: - resolution: {integrity: sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /@typescript-eslint/types@5.62.0: resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.52.0(typescript@5.2.2): - resolution: {integrity: sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==} + /@typescript-eslint/types@6.7.3: + resolution: {integrity: sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.2.2): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -7286,8 +7311,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.52.0 - '@typescript-eslint/visitor-keys': 5.52.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 @@ -7298,80 +7323,79 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@5.62.0(typescript@5.2.2): - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/typescript-estree@6.7.3(typescript@5.2.2): + resolution: {integrity: sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==} + engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 + '@typescript-eslint/types': 6.7.3 + '@typescript-eslint/visitor-keys': 6.7.3 debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - tsutils: 3.21.0(typescript@5.2.2) + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.52.0(eslint@8.47.0)(typescript@5.2.2): - resolution: {integrity: sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==} + /@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.2.2): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 - '@typescript-eslint/scope-manager': 5.52.0 - '@typescript-eslint/types': 5.52.0 - '@typescript-eslint/typescript-estree': 5.52.0(typescript@5.2.2) - eslint: 8.47.0 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) + eslint: 8.50.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@8.47.0) semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.2.2): - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/utils@6.7.3(eslint@8.47.0)(typescript@5.2.2): + resolution: {integrity: sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==} + engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.47.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) - eslint: 8.50.0 - eslint-scope: 5.1.1 + '@typescript-eslint/scope-manager': 6.7.3 + '@typescript-eslint/types': 6.7.3 + '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) + eslint: 8.47.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@5.52.0: - resolution: {integrity: sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==} + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@5.62.0: - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/visitor-keys@6.7.3: + resolution: {integrity: sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==} + engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/types': 6.7.3 eslint-visitor-keys: 3.4.3 dev: true @@ -10541,11 +10565,11 @@ packages: dependencies: '@next/eslint-plugin-next': 13.4.12 '@rushstack/eslint-patch': 1.5.0 - '@typescript-eslint/parser': 5.52.0(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@5.2.2) eslint: 8.47.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.28.1)(eslint@8.47.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.47.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.28.1)(eslint@8.47.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.47.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.47.0) eslint-plugin-react: 7.33.2(eslint@8.47.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.47.0) @@ -10618,7 +10642,7 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.28.1)(eslint@8.47.0): + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.28.1)(eslint@8.47.0): resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -10628,8 +10652,8 @@ packages: debug: 4.3.4(supports-color@5.5.0) enhanced-resolve: 5.15.0 eslint: 8.47.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.47.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.47.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.47.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.47.0) fast-glob: 3.3.1 get-tsconfig: 4.7.2 is-core-module: 2.13.0 @@ -10641,7 +10665,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.47.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.47.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -10662,11 +10686,11 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.52.0(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@5.2.2) debug: 3.2.7 eslint: 8.47.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.28.1)(eslint@8.47.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.28.1)(eslint@8.47.0) transitivePeerDependencies: - supports-color dev: true @@ -10700,6 +10724,35 @@ packages: - supports-color dev: true + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.47.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 6.7.3(eslint@8.47.0)(typescript@5.2.2) + debug: 3.2.7 + eslint: 8.47.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + /eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.22.5)(@babel/plugin-transform-react-jsx@7.22.15)(eslint@8.50.0): resolution: {integrity: sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==} engines: {node: '>=12.0.0'} @@ -10715,7 +10768,7 @@ packages: string-natural-compare: 3.0.1 dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.47.0): + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.50.0): resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: @@ -10725,16 +10778,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.52.0(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.62.0(eslint@8.50.0)(typescript@5.2.2) 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: 8.47.0 + eslint: 8.50.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.47.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.50.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -10750,7 +10803,7 @@ packages: - supports-color dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.50.0): + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.47.0): resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: @@ -10760,16 +10813,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.3(eslint@8.47.0)(typescript@5.2.2) 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: 8.50.0 + eslint: 8.47.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.50.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.47.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -10975,7 +11028,7 @@ packages: eslint: 8.47.0 dev: true - /eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@5.52.0)(eslint@8.47.0): + /eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@6.7.3)(eslint@8.47.0): resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -10985,7 +11038,7 @@ packages: '@typescript-eslint/eslint-plugin': optional: true dependencies: - '@typescript-eslint/eslint-plugin': 5.52.0(@typescript-eslint/parser@5.52.0)(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.47.0)(typescript@5.2.2) eslint: 8.47.0 eslint-rule-composer: 0.3.0 dev: true @@ -11017,16 +11070,6 @@ packages: esrecurse: 4.3.0 estraverse: 5.3.0 - /eslint-utils@3.0.0(eslint@8.47.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.47.0 - eslint-visitor-keys: 2.1.0 - dev: true - /eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} @@ -13743,6 +13786,11 @@ packages: resolution: {integrity: sha512-EqJPEUlZD0/CSUMubKtMaYUOtWe91tZXTWMJZoKSbLk+KtdhNdcvppH8lA9XwVu2V4Ailvsj0GBZJ2ZwDjfesQ==} dev: false + /js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + dev: false + /js-message@1.0.7: resolution: {integrity: sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==} engines: {node: '>=0.6.0'} @@ -17452,11 +17500,6 @@ packages: set-function-name: 2.0.1 dev: true - /regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - dev: true - /regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} @@ -19169,6 +19212,15 @@ packages: resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} dev: true + /ts-api-utils@1.0.3(typescript@5.2.2): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + /ts-dedent@2.2.0: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'}