From 878e3e04d61fd2186b95aa5512ea0333951db73b Mon Sep 17 00:00:00 2001 From: Victor Ryabkov <45964820+moiskillnadne@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:40:02 +0200 Subject: [PATCH] Refactor: Eslint + Small bugs (#16) * Fix all errors and warnings by eslint * Fix import order * Remove obsolete unused code * Fix layout shift on mobile devices * Added accept language for each request header * Remove useless logs --- .prettierrc | 2 +- eslint.config.mjs | 29 + package.json | 41 +- postcss.config.js | 2 +- public/service-worker.js | 34 +- src/app/App.tsx | 20 +- src/app/main.tsx | 20 +- src/app/system/constant.ts | 2 +- src/app/system/i18n.manager.ts | 18 +- src/entity/user/index.ts | 2 +- src/entity/user/lib/useAuthenticated.ts | 31 +- .../AuthorizePasskeys/AuthorizePasskeys.tsx | 25 - src/feature/AuthorizePasskeys/index.ts | 1 + .../lib/useAuthenticateViaPasskeys.ts | 21 +- src/feature/Logout/index.ts | 4 +- src/feature/Logout/lib/useLogoutMutation.ts | 23 +- src/feature/Logout/ui/LogoutButton.tsx | 25 +- src/feature/ProtectedRoute/index.tsx | 42 +- .../{RegisterPasskeys.tsx => index.tsx} | 45 +- src/feature/RememberMe/index.ts | 2 +- src/feature/RememberMe/lib/useDeviceMeta.ts | 25 +- src/feature/translation/index.ts | 4 +- .../translation/lib/useCustomTranslation.ts | 24 +- .../translation/ui/LanguageSwitcher.tsx | 8 +- src/i18n/languageMap.ts | 6 + src/pages/AccountPage.tsx | 18 +- src/pages/ChallengeBuilderPage.tsx | 16 +- src/pages/ChallengePage.tsx | 25 +- src/pages/HomePage.tsx | 16 +- src/pages/LoginPage.tsx | 10 +- src/pages/index.tsx | 26 +- src/shared/api/account.service.ts | 20 +- src/shared/api/api.ts | 46 +- src/shared/api/auth.service.ts | 45 +- src/shared/api/challenge.service.ts | 68 +- src/shared/api/types.ts | 12 +- src/shared/constants/index.ts | 2 +- src/shared/constants/routes.ts | 2 +- src/shared/hooks/index.ts | 3 +- src/shared/hooks/useLongPress.ts | 57 -- src/shared/hooks/useVisitorId.ts | 15 +- src/shared/lib/BrowserExtractor.ts | 40 +- src/shared/lib/EventEmitter.ts | 22 +- src/shared/lib/FingerprintService.ts | 66 +- src/shared/lib/LocalStorage.ts | 32 +- src/shared/lib/OSExtractor.ts | 36 +- src/shared/lib/arrayBufferToBase64.ts | 7 - src/shared/lib/hashData.ts | 14 +- src/shared/lib/index.ts | 12 +- .../lib/isPublicKeyCredentialSupported.ts | 21 - src/shared/types/extractor.ts | 2 +- src/shared/types/index.ts | 2 +- src/shared/ui/Loader/index.tsx | 4 +- src/shared/ui/Modal/Modal.tsx | 28 +- src/shared/ui/Modal/index.ts | 4 +- src/shared/ui/Modal/useModalState.ts | 14 +- src/shared/ui/Page/Page.tsx | 8 +- src/shared/ui/Page/PageContent.tsx | 8 +- src/shared/ui/Page/index.ts | 4 +- src/shared/ui/PlusIcon/index.tsx | 8 +- src/shared/ui/RightArrow/index.tsx | 4 +- src/shared/ui/index.ts | 8 +- src/vite-env.d.ts | 10 +- src/widget/Account/AccountHeader.tsx | 8 +- src/widget/Account/PasskeysManager.tsx | 114 --- src/widget/Account/UserInfoPanel.tsx | 18 +- src/widget/Account/index.tsx | 18 +- src/widget/Account/lib/mappers.ts | 30 +- src/widget/AppVersion/index.tsx | 6 +- src/widget/Challenge/index.ts | 2 +- src/widget/Challenge/lib/convertDate.ts | 12 +- src/widget/Challenge/lib/useStreakState.ts | 55 +- src/widget/Challenge/ui/Calendar.tsx | 44 +- src/widget/Challenge/ui/CalendarDayItem.tsx | 12 +- .../Challenge/ui/CalendarHeaderItem.tsx | 8 +- src/widget/Challenge/ui/CalendarManager.tsx | 54 +- src/widget/Challenge/ui/MetaText.tsx | 10 +- src/widget/Challenge/ui/Timer.tsx | 54 +- src/widget/Challenge/ui/index.tsx | 21 +- src/widget/ChallengeBuilder/index.tsx | 48 +- .../ChallengeManager/ChallengeGridItem.tsx | 14 +- .../ChallengeManager/ChallengeManager.tsx | 33 +- .../ChallengeManagerHeader.tsx | 8 +- .../CreateChallengeButton.tsx | 13 +- src/widget/ChallengeManager/index.ts | 2 +- src/widget/Header/index.tsx | 31 +- src/widget/Login/index.ts | 2 +- src/widget/Login/ui/LoginButton.tsx | 16 +- src/widget/Login/ui/LoginHeader.tsx | 12 +- src/widget/Login/ui/index.tsx | 122 ++- tailwind.config.js | 2 +- tsconfig.app.json | 5 + tsconfig.app.tsbuildinfo | 1 + tsconfig.node.tsbuildinfo | 1 + vite.config.ts | 17 +- yarn.lock | 757 ++++++++++-------- 96 files changed, 1338 insertions(+), 1403 deletions(-) delete mode 100644 src/feature/AuthorizePasskeys/AuthorizePasskeys.tsx create mode 100644 src/feature/AuthorizePasskeys/index.ts rename src/feature/RegisterPasskeys/{RegisterPasskeys.tsx => index.tsx} (51%) create mode 100644 src/i18n/languageMap.ts delete mode 100644 src/shared/hooks/useLongPress.ts delete mode 100644 src/shared/lib/arrayBufferToBase64.ts delete mode 100644 src/shared/lib/isPublicKeyCredentialSupported.ts delete mode 100644 src/widget/Account/PasskeysManager.tsx create mode 100644 tsconfig.app.tsbuildinfo create mode 100644 tsconfig.node.tsbuildinfo diff --git a/.prettierrc b/.prettierrc index 80a8c69..c191329 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,7 +5,7 @@ "singleQuote": true, "jsxSingleQuote": false, "trailingComma": "all", - "semi": true, + "semi": false, "printWidth": 100, "tabWidth": 2, "useTabs": false, diff --git a/eslint.config.mjs b/eslint.config.mjs index fce761c..63a42e1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,6 +6,7 @@ import tsParser from '@typescript-eslint/parser'; import prettier from 'eslint-plugin-prettier'; import prettierConfig from 'eslint-config-prettier'; import airbnb from 'eslint-config-airbnb'; +import importPlugin from 'eslint-plugin-import'; export default [ { @@ -31,6 +32,7 @@ export default [ 'react-hooks': reactHooks, '@typescript-eslint': ts, prettier, + import: importPlugin, }, rules: { ...ts.configs.recommended.rules, @@ -38,6 +40,33 @@ export default [ ...airbnb.rules, 'prettier/prettier': 'warn', // Prettier as ESLint 'react/react-in-jsx-scope': 'off', + 'react-hooks/exhaustive-deps': 'warn', + 'import/order': [ + 'error', + { + groups: [['external', 'builtin'], 'internal', ['sibling', 'parent'], 'index'], + + pathGroups: [ + { + pattern: '@(react)', + group: 'external', + position: 'before', + }, + { + pattern: '@(~app|~shared|~features|~pages|~entities)/**', + group: 'internal', + }, + ], + + pathGroupsExcludedImportTypes: ['internal', 'react'], + 'newlines-between': 'always', + + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + }, + ], }, settings: { react: { diff --git a/package.json b/package.json index 111a1cf..3d7e75c 100644 --- a/package.json +++ b/package.json @@ -12,40 +12,41 @@ }, "dependencies": { "@simplewebauthn/browser": "^11.0.0", - "@tanstack/react-query": "^5.56.2", + "@tanstack/react-query": "^5.59.16", "autoprefixer": "^10.4.20", "axios": "^1.7.7", "date-fns": "^3.6.0", - "i18next": "^23.14.0", + "i18next": "^23.16.4", "i18next-browser-languagedetector": "^8.0.0", - "postcss": "^8.4.42", + "postcss": "^8.4.47", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-i18next": "^15.0.1", - "react-router-dom": "^6.26.1", - "tailwindcss": "^3.4.10", + "react-i18next": "^15.1.0", + "react-router-dom": "^6.27.0", + "tailwindcss": "^3.4.14", "zod": "^3.23.8" }, "devDependencies": { "@simplewebauthn/types": "^11.0.0", - "@tanstack/eslint-plugin-query": "^5.58.1", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^8.7.0", - "@typescript-eslint/parser": "^8.7.0", - "@vitejs/plugin-react": "^4.3.1", - "eslint": "^9.11.1", + "@tanstack/eslint-plugin-query": "^5.59.7", + "@types/node": "^22.8.1", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@typescript-eslint/eslint-plugin": "^8.11.0", + "@typescript-eslint/parser": "^8.11.0", + "@vitejs/plugin-react": "^4.3.3", + "eslint": "^9.13.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.30.0", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.12", - "globals": "^15.9.0", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.11.0", "prettier": "^3.3.3", - "typescript": "^5.5.3", - "vite": "^5.4.1", + "typescript": "^5.6.3", + "vite": "^5.4.10", "vite-plugin-mkcert": "^1.17.6", "vite-plugin-pwa": "^0.20.5" }, diff --git a/postcss.config.js b/postcss.config.js index 2aa7205..2e7af2b 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -3,4 +3,4 @@ export default { tailwindcss: {}, autoprefixer: {}, }, -}; +} diff --git a/public/service-worker.js b/public/service-worker.js index 3a03967..e965fed 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,23 +1,23 @@ // service-worker.ts async function getAssetsFromManifest() { - const manifest = await fetch('/manifest.json').then((response) => response.json()); - const assets = Object.values(manifest).map((entry) => entry.file); - return assets; + const manifest = await fetch('/manifest.json').then((response) => response.json()) + const assets = Object.values(manifest).map((entry) => entry.file) + return assets } self.addEventListener('install', (event) => { event.waitUntil( (async () => { - const cache = await caches.open('app-cache'); - const assets = await getAssetsFromManifest(); - cache.addAll(['/', '/index.html', ...assets]); + const cache = await caches.open('app-cache') + const assets = await getAssetsFromManifest() + cache.addAll(['/', '/index.html', ...assets]) })(), - ); -}); + ) +}) self.addEventListener('fetch', (event) => { - const url = new URL(event.request.url); + const url = new URL(event.request.url) // Static resources are cached const isStaticResource = @@ -27,9 +27,9 @@ self.addEventListener('fetch', (event) => { url.pathname.endsWith('.png') || url.pathname.endsWith('.jpg') || url.pathname.endsWith('.svg') || - url.pathname.endsWith('.ico'); + url.pathname.endsWith('.ico') - const isApiRequest = url.pathname.startsWith('/api/'); + const isApiRequest = url.pathname.startsWith('/api/') if (isStaticResource && !isApiRequest) { event.respondWith( @@ -38,12 +38,12 @@ self.addEventListener('fetch', (event) => { cachedResponse || fetch(event.request).then((networkResponse) => { return caches.open('static-cache').then((cache) => { - cache.put(event.request, networkResponse.clone()); - return networkResponse; - }); + cache.put(event.request, networkResponse.clone()) + return networkResponse + }) }) - ); + ) }), - ); + ) } -}); +}) diff --git a/src/app/App.tsx b/src/app/App.tsx index 05004cd..45e7754 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -1,20 +1,22 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import ApplicationRouter from '../pages'; -import i18nManager from './system/i18n.manager'; -import { useVisitorId } from '../shared/hooks'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -void i18nManager.initialize(); +import i18nManager from './system/i18n.manager' -export const queryClient = new QueryClient(); +import ApplicationRouter from '~/pages' +import { useVisitorId } from '~/shared/hooks' + +void i18nManager.initialize() + +export const queryClient = new QueryClient() function App() { - useVisitorId(); + useVisitorId() return ( - ); + ) } -export default App; +export default App diff --git a/src/app/main.tsx b/src/app/main.tsx index 44bd341..2739347 100644 --- a/src/app/main.tsx +++ b/src/app/main.tsx @@ -1,19 +1,21 @@ -import { StrictMode, Suspense } from 'react'; -import { createRoot } from 'react-dom/client'; -import App from './App'; -import './index.css'; +import { StrictMode, Suspense } from 'react' + +import { createRoot } from 'react-dom/client' + +import App from './App' +import './index.css' if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then((registration) => { - console.log('Service Worker registered with scope:', registration.scope); + console.info('Service Worker registered with scope:', registration.scope) }) .catch((error) => { - console.error('Service Worker registration failed:', error); - }); - }); + console.error('Service Worker registration failed:', error) + }) + }) } createRoot(document.getElementById('root')!).render( @@ -22,4 +24,4 @@ createRoot(document.getElementById('root')!).render( , -); +) diff --git a/src/app/system/constant.ts b/src/app/system/constant.ts index fd5e05b..bb7978d 100644 --- a/src/app/system/constant.ts +++ b/src/app/system/constant.ts @@ -1,4 +1,4 @@ -export type SupportableLanguageKeys = keyof typeof SupportableLanguage; +export type SupportableLanguageKeys = keyof typeof SupportableLanguage export enum SupportableLanguage { EN = 'en', diff --git a/src/app/system/i18n.manager.ts b/src/app/system/i18n.manager.ts index 2555f3a..0d6de07 100644 --- a/src/app/system/i18n.manager.ts +++ b/src/app/system/i18n.manager.ts @@ -1,9 +1,9 @@ -import i18n from 'i18next'; -import LanguageDetector from 'i18next-browser-languagedetector'; -import { initReactI18next } from 'react-i18next'; +import i18n from 'i18next' +import LanguageDetector from 'i18next-browser-languagedetector' +import { initReactI18next } from 'react-i18next' -import enResources from '../../i18n/en/translation.json'; -import ruResources from '../../i18n/ru/translation.json'; +import enResources from '../../i18n/en/translation.json' +import ruResources from '../../i18n/ru/translation.json' const i18nManager = { async initialize() { @@ -22,12 +22,12 @@ const i18nManager = { }, joinArrays: '\n', debug: import.meta.env.NODE_ENV !== 'production', - }); + }) }, addResources(language: string, namespace: string, resources: Record) { - i18n.addResources(language, namespace, resources); + i18n.addResources(language, namespace, resources) }, -}; +} -export default i18nManager; +export default i18nManager diff --git a/src/entity/user/index.ts b/src/entity/user/index.ts index 39c922c..43d45b0 100644 --- a/src/entity/user/index.ts +++ b/src/entity/user/index.ts @@ -1 +1 @@ -export * from './lib/useAuthenticated'; +export * from './lib/useAuthenticated' diff --git a/src/entity/user/lib/useAuthenticated.ts b/src/entity/user/lib/useAuthenticated.ts index 2a26ffd..dd98106 100644 --- a/src/entity/user/lib/useAuthenticated.ts +++ b/src/entity/user/lib/useAuthenticated.ts @@ -1,38 +1,39 @@ -import { useQuery } from '@tanstack/react-query'; -import { accountService, UserDTO } from '../../../shared/api/account.service'; -import { AxiosError } from 'axios'; +import { useQuery } from '@tanstack/react-query' +import { AxiosError } from 'axios' + +import { accountService, UserDTO } from '~/shared/api/account.service' type Return = { - isLoading: boolean; - isAuthenticated: boolean; + isLoading: boolean + isAuthenticated: boolean - user: UserDTO | null; -}; + user: UserDTO | null +} export const useAuthenticated = (): Return => { const query = useQuery({ queryKey: ['/protected/me'], queryFn: accountService.getAccountInfo, select(data) { - return data.data.details; + return data.data.details }, retry(failureCount, error) { - console.log(error); + console.log(error) - const axiosError = error as AxiosError; + const axiosError = error as AxiosError if (axiosError.response?.status === 401) { - return false; + return false } - return failureCount < 4; + return failureCount < 4 }, - }); + }) return { isLoading: query.isLoading, isAuthenticated: !!query.data?.user.email, user: query.data?.user ?? null, - }; -}; + } +} diff --git a/src/feature/AuthorizePasskeys/AuthorizePasskeys.tsx b/src/feature/AuthorizePasskeys/AuthorizePasskeys.tsx deleted file mode 100644 index ae2761c..0000000 --- a/src/feature/AuthorizePasskeys/AuthorizePasskeys.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { isPublicKeyCredentialSupported } from '../../shared/lib'; -import { useAuthenticateViaPasskeys } from './lib/useAuthenticateViaPasskeys'; - -export const AuthorizePasskeys = () => { - const passkeysMutation = useAuthenticateViaPasskeys(); - - const loginChallenge = async () => { - const isSupported = await isPublicKeyCredentialSupported(); - - if (!isSupported) { - return console.error('WebAuthn is not supported'); - } - - passkeysMutation.mutate('vitya.ryabkov@gmail.com'); - }; - - return ( - - ); -}; diff --git a/src/feature/AuthorizePasskeys/index.ts b/src/feature/AuthorizePasskeys/index.ts new file mode 100644 index 0000000..d985628 --- /dev/null +++ b/src/feature/AuthorizePasskeys/index.ts @@ -0,0 +1 @@ +export * from './lib/useAuthenticateViaPasskeys' diff --git a/src/feature/AuthorizePasskeys/lib/useAuthenticateViaPasskeys.ts b/src/feature/AuthorizePasskeys/lib/useAuthenticateViaPasskeys.ts index b9a8101..cfd4e0a 100644 --- a/src/feature/AuthorizePasskeys/lib/useAuthenticateViaPasskeys.ts +++ b/src/feature/AuthorizePasskeys/lib/useAuthenticateViaPasskeys.ts @@ -1,22 +1,23 @@ -import { useMutation } from '@tanstack/react-query'; -import { authService } from '../../../shared/api/auth.service'; +import { useMutation } from '@tanstack/react-query' + +import { authService } from '~/shared/api/auth.service' type Props = { - onError?: (err: unknown) => void; - onSuccess?: (data: unknown) => void; + onError?: (err: unknown) => void + onSuccess?: (data: unknown) => void - loginIfNoCredentials?: (email: string) => void; -}; + loginIfNoCredentials?: (email: string) => void +} export const useAuthenticateViaPasskeys = (props?: Props) => { return useMutation({ mutationFn: authService.authenticateKeys, onError: (err) => { - console.error(`[GenerateLoginChallenge:onError]: ${JSON.stringify(err)}`); + console.error(`[GenerateLoginChallenge:onError]: ${JSON.stringify(err)}`) if (props?.onError) { - props.onError(err); + props.onError(err) } }, - }); -}; + }) +} diff --git a/src/feature/Logout/index.ts b/src/feature/Logout/index.ts index 25337bb..354fe46 100644 --- a/src/feature/Logout/index.ts +++ b/src/feature/Logout/index.ts @@ -1,3 +1,3 @@ -export * from './lib/useLogoutMutation'; +export * from './lib/useLogoutMutation' -export * from './ui/LogoutButton'; +export * from './ui/LogoutButton' diff --git a/src/feature/Logout/lib/useLogoutMutation.ts b/src/feature/Logout/lib/useLogoutMutation.ts index 147ae72..afd2d10 100644 --- a/src/feature/Logout/lib/useLogoutMutation.ts +++ b/src/feature/Logout/lib/useLogoutMutation.ts @@ -1,27 +1,28 @@ -import { useMutation } from '@tanstack/react-query'; -import { authService } from '../../../shared/api/auth.service'; +import { useMutation } from '@tanstack/react-query' + +import { authService } from '~/shared/api/auth.service' type Params = { - onSuccess?: () => void; - onError?: (error: Error) => void; -}; + onSuccess?: () => void + onError?: (error: Error) => void +} export const useLogoutMutation = (params?: Params) => { return useMutation({ mutationFn: authService.logout, onSuccess: () => { - console.info('[Logout:onSuccess]'); + console.info('[Logout:onSuccess]') if (params?.onSuccess) { - params.onSuccess(); + params.onSuccess() } }, onError: (error: Error) => { - console.error('[Logout:onError]', error); + console.error('[Logout:onError]', error) if (params?.onError) { - params.onError(error); + params.onError(error) } }, - }); -}; + }) +} diff --git a/src/feature/Logout/ui/LogoutButton.tsx b/src/feature/Logout/ui/LogoutButton.tsx index f3318fc..242397e 100644 --- a/src/feature/Logout/ui/LogoutButton.tsx +++ b/src/feature/Logout/ui/LogoutButton.tsx @@ -1,20 +1,23 @@ -import { useCallback } from 'react'; -import { useCustomTranslation } from '../../translation'; -import { useLogoutMutation } from '../lib/useLogoutMutation'; -import { useNavigate } from 'react-router-dom'; +import { useCallback } from 'react' + +import { useNavigate } from 'react-router-dom' + +import { useLogoutMutation } from '../lib/useLogoutMutation' + +import { useCustomTranslation } from '~/feature/translation' export const LogoutButton = () => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() - const navigate = useNavigate(); + const navigate = useNavigate() const mutation = useLogoutMutation({ onSuccess: () => navigate('/login'), - }); + }) const logout = useCallback(() => { - mutation.mutate(); - }, []); + mutation.mutate() + }, [mutation]) return (
@@ -25,5 +28,5 @@ export const LogoutButton = () => { {t('logout')}
- ); -}; + ) +} diff --git a/src/feature/ProtectedRoute/index.tsx b/src/feature/ProtectedRoute/index.tsx index 1f14093..63e425b 100644 --- a/src/feature/ProtectedRoute/index.tsx +++ b/src/feature/ProtectedRoute/index.tsx @@ -1,30 +1,32 @@ -import React, { useEffect } from 'react'; -import { Navigate, useNavigate } from 'react-router-dom'; -import { useAuthenticated } from '../../entity/user'; -import { Page } from '../../shared/ui'; -import { Loader } from '../../shared/ui/Loader'; -import { EventEmitter } from '../../shared/lib/EventEmitter'; +import React, { useEffect } from 'react' + +import { Navigate, useNavigate } from 'react-router-dom' + +import { useAuthenticated } from '~/entity/user' +import { EventEmitter } from '~/shared/lib/EventEmitter' +import { Page } from '~/shared/ui' +import { Loader } from '~/shared/ui/Loader' type Props = { - element: React.ReactNode; -}; + element: React.ReactNode +} function ProtectedRoute({ element }: Props) { - const { isAuthenticated, isLoading } = useAuthenticated(); + const { isAuthenticated, isLoading } = useAuthenticated() - const navigate = useNavigate(); + const navigate = useNavigate() useEffect(() => { const handleRefreshTokenExpired = () => { - return navigate('/login'); - }; + return navigate('/login') + } - EventEmitter.on('refreshTokenExpired', handleRefreshTokenExpired); + EventEmitter.on('refreshTokenExpired', handleRefreshTokenExpired) return () => { - EventEmitter.off('refreshTokenExpired', handleRefreshTokenExpired); - }; - }, []); + EventEmitter.off('refreshTokenExpired', handleRefreshTokenExpired) + } + }, [navigate]) if (isLoading) { return ( @@ -33,14 +35,14 @@ function ProtectedRoute({ element }: Props) { ; - ); + ) } if (!isAuthenticated) { - return ; + return } - return element; + return element } -export default ProtectedRoute; +export default ProtectedRoute diff --git a/src/feature/RegisterPasskeys/RegisterPasskeys.tsx b/src/feature/RegisterPasskeys/index.tsx similarity index 51% rename from src/feature/RegisterPasskeys/RegisterPasskeys.tsx rename to src/feature/RegisterPasskeys/index.tsx index fdad5e0..0f3d9a1 100644 --- a/src/feature/RegisterPasskeys/RegisterPasskeys.tsx +++ b/src/feature/RegisterPasskeys/index.tsx @@ -1,49 +1,46 @@ -import { useMutation } from '@tanstack/react-query'; -import { authService } from '../../shared/api/auth.service'; -import { startRegistration } from '@simplewebauthn/browser'; -import { isPublicKeyCredentialSupported } from '../../shared/lib'; +import { browserSupportsWebAuthn, startRegistration } from '@simplewebauthn/browser' +import { useMutation } from '@tanstack/react-query' + +import { authService } from '~/shared/api/auth.service' export const RegisterPasskeys = () => { const verifyChallenge = useMutation({ mutationFn: authService.verifyRegistration, - onSuccess: (data) => { - console.info('[VerifyChallenge:onSuccess]', data); + onSuccess: () => { + console.info('[VerifyChallenge:onSuccess]') }, onError: (err) => { - console.info(`[VerifyChallenge:onError]: ${JSON.stringify(err)}`); + console.info(`[VerifyChallenge:onError]: ${JSON.stringify(err)}`) }, - }); + }) const generateChallengeMutation = useMutation({ mutationFn: authService.registerKeys, onSuccess: async (result) => { - console.info('[GenerateChallenge:onSuccess]', result); + console.info('[GenerateChallenge:onSuccess]') - const options = structuredClone(result.data); + const options = structuredClone(result.data) try { - console.log(options); - const attResult = await startRegistration({ optionsJSON: options }); + const attResult = await startRegistration({ optionsJSON: options }) - verifyChallenge.mutate(attResult); + verifyChallenge.mutate(attResult) } catch (error: unknown) { - console.error(error); + console.error(error) } }, onError: (err) => { - console.info(`[GenerateChallenge:onError]: ${JSON.stringify(err)}`); + console.info(`[GenerateChallenge:onError]: ${JSON.stringify(err)}`) }, - }); + }) const createChallenge = async () => { - const isSupported = await isPublicKeyCredentialSupported(); - - if (!isSupported) { - return console.error('WebAuthn is not supported'); + if (!browserSupportsWebAuthn()) { + return console.error('WebAuthn is not supported') } - generateChallengeMutation.mutate(); - }; + generateChallengeMutation.mutate() + } return ( - ); -}; + ) +} diff --git a/src/feature/RememberMe/index.ts b/src/feature/RememberMe/index.ts index d041ee2..f3a63f0 100644 --- a/src/feature/RememberMe/index.ts +++ b/src/feature/RememberMe/index.ts @@ -1 +1 @@ -export * from './lib/useDeviceMeta'; +export * from './lib/useDeviceMeta' diff --git a/src/feature/RememberMe/lib/useDeviceMeta.ts b/src/feature/RememberMe/lib/useDeviceMeta.ts index d3e75e9..4c07bdf 100644 --- a/src/feature/RememberMe/lib/useDeviceMeta.ts +++ b/src/feature/RememberMe/lib/useDeviceMeta.ts @@ -1,16 +1,17 @@ -import { useCallback } from 'react'; -import { BrowserExtractor, FingerprintService, OSExtractor } from '../../../shared/lib'; +import { useCallback } from 'react' + +import { BrowserExtractor, FingerprintService, OSExtractor } from '~/shared/lib' export const useDeviceMeta = () => { const process = useCallback(async () => { - const fingerprintService = new FingerprintService(); - const fingerprint = await fingerprintService.getFingerprint(); + const fingerprintService = new FingerprintService() + const fingerprint = await fingerprintService.getFingerprint() - const osExtractorService = new OSExtractor(fingerprint.userAgent); - const os = osExtractorService.extract(); + const osExtractorService = new OSExtractor(fingerprint.userAgent) + const os = osExtractorService.extract() - const browserExtractorService = new BrowserExtractor(fingerprint.userAgent); - const browser = browserExtractorService.extract(); + const browserExtractorService = new BrowserExtractor(fingerprint.userAgent) + const browser = browserExtractorService.extract() return { os, @@ -26,10 +27,10 @@ export const useDeviceMeta = () => { colorDepth: fingerprint.colorDepth, pixelDepth: fingerprint.pixelDepth, }, - }; - }, []); + } + }, []) return { process, - }; -}; + } +} diff --git a/src/feature/translation/index.ts b/src/feature/translation/index.ts index 029a1ba..1d8bc14 100644 --- a/src/feature/translation/index.ts +++ b/src/feature/translation/index.ts @@ -1,3 +1,3 @@ -export * from './ui/LanguageSwitcher'; +export * from './ui/LanguageSwitcher' -export * from './lib/useCustomTranslation'; +export * from './lib/useCustomTranslation' diff --git a/src/feature/translation/lib/useCustomTranslation.ts b/src/feature/translation/lib/useCustomTranslation.ts index 5b5742a..37e6588 100644 --- a/src/feature/translation/lib/useCustomTranslation.ts +++ b/src/feature/translation/lib/useCustomTranslation.ts @@ -1,26 +1,28 @@ -import { useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SupportableLanguage } from '../../../app/system/constant'; +import { useCallback, useState } from 'react' + +import { useTranslation } from 'react-i18next' + +import { SupportableLanguage } from '~/app/system/constant' export const useCustomTranslation = () => { - const { i18n, t } = useTranslation(); + const { i18n, t } = useTranslation() const [language, setLanguage] = useState( (i18n.language as SupportableLanguage) || SupportableLanguage.RU, - ); + ) const switchLanguage = useCallback(async () => { const lng = - language === SupportableLanguage.RU ? SupportableLanguage.EN : SupportableLanguage.RU; + language === SupportableLanguage.RU ? SupportableLanguage.EN : SupportableLanguage.RU - setLanguage(lng); + setLanguage(lng) - await i18n.changeLanguage(lng); - }, [language]); + await i18n.changeLanguage(lng) + }, [i18n, language]) return { t, language, switchLanguage, - }; -}; + } +} diff --git a/src/feature/translation/ui/LanguageSwitcher.tsx b/src/feature/translation/ui/LanguageSwitcher.tsx index 0ef435e..90c5dc9 100644 --- a/src/feature/translation/ui/LanguageSwitcher.tsx +++ b/src/feature/translation/ui/LanguageSwitcher.tsx @@ -1,11 +1,11 @@ -import { useCustomTranslation } from '../lib/useCustomTranslation'; +import { useCustomTranslation } from '../lib/useCustomTranslation' export const LanguageSwitcher = () => { - const { switchLanguage, language } = useCustomTranslation(); + const { switchLanguage, language } = useCustomTranslation() return ( - ); -}; + ) +} diff --git a/src/i18n/languageMap.ts b/src/i18n/languageMap.ts new file mode 100644 index 0000000..5ccd16a --- /dev/null +++ b/src/i18n/languageMap.ts @@ -0,0 +1,6 @@ +export type Languages = 'ru' | 'en' + +export const LanguageMap = { + ru: 'RU-ru', + en: 'EN-us', +} diff --git a/src/pages/AccountPage.tsx b/src/pages/AccountPage.tsx index 9c23107..0194b0a 100644 --- a/src/pages/AccountPage.tsx +++ b/src/pages/AccountPage.tsx @@ -1,12 +1,12 @@ -import { useCustomTranslation } from '../feature/translation'; -import { Routes } from '../shared/constants'; -import { Page, PageContent } from '../shared/ui'; -import { AccountWidget } from '../widget/Account'; -import { AppVersion } from '../widget/AppVersion'; -import { Header } from '../widget/Header'; +import { useCustomTranslation } from '~/feature/translation' +import { Routes } from '~/shared/constants' +import { Page, PageContent } from '~/shared/ui' +import { AccountWidget } from '~/widget/Account' +import { AppVersion } from '~/widget/AppVersion' +import { Header } from '~/widget/Header' export const AccountPage = () => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() return ( @@ -22,5 +22,5 @@ export const AccountPage = () => { - ); -}; + ) +} diff --git a/src/pages/ChallengeBuilderPage.tsx b/src/pages/ChallengeBuilderPage.tsx index 3613265..617e010 100644 --- a/src/pages/ChallengeBuilderPage.tsx +++ b/src/pages/ChallengeBuilderPage.tsx @@ -1,11 +1,11 @@ -import { useCustomTranslation } from '../feature/translation'; -import { Routes } from '../shared/constants'; -import { Page, PageContent } from '../shared/ui'; -import { ChallengeBuilderWidget } from '../widget/ChallengeBuilder'; -import { Header } from '../widget/Header'; +import { useCustomTranslation } from '~/feature/translation' +import { Routes } from '~/shared/constants' +import { Page, PageContent } from '~/shared/ui' +import { ChallengeBuilderWidget } from '~/widget/ChallengeBuilder' +import { Header } from '~/widget/Header' export const ChallengeBuilderPage = () => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() return ( @@ -19,5 +19,5 @@ export const ChallengeBuilderPage = () => { - ); -}; + ) +} diff --git a/src/pages/ChallengePage.tsx b/src/pages/ChallengePage.tsx index 5a42d2a..cb2e067 100644 --- a/src/pages/ChallengePage.tsx +++ b/src/pages/ChallengePage.tsx @@ -1,13 +1,14 @@ -import { useParams } from 'react-router-dom'; -import { Page, PageContent } from '../shared/ui'; -import { ChallengeWidget } from '../widget/Challenge'; -import { Header } from '../widget/Header'; -import { Routes } from '../shared/constants'; -import { useCustomTranslation } from '../feature/translation'; +import { useParams } from 'react-router-dom' + +import { useCustomTranslation } from '~/feature/translation' +import { Routes } from '~/shared/constants' +import { Page, PageContent } from '~/shared/ui' +import { ChallengeWidget } from '~/widget/Challenge' +import { Header } from '~/widget/Header' const ChallengePage = () => { - const { challengeId } = useParams(); - const { t } = useCustomTranslation(); + const { challengeId } = useParams() + const { t } = useCustomTranslation() if (!challengeId) { return ( @@ -20,7 +21,7 @@ const ChallengePage = () => {
Sorry, something wrong with your URL.
- ); + ) } return ( @@ -35,7 +36,7 @@ const ChallengePage = () => { - ); -}; + ) +} -export default ChallengePage; +export default ChallengePage diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index f5012f6..9efcbed 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,11 +1,11 @@ -import { useCustomTranslation } from '../feature/translation'; -import { Routes } from '../shared/constants'; -import { Page, PageContent } from '../shared/ui'; -import { ChallengeManager } from '../widget/ChallengeManager/'; -import { Header } from '../widget/Header'; +import { useCustomTranslation } from '~/feature/translation' +import { Routes } from '~/shared/constants' +import { Page, PageContent } from '~/shared/ui' +import { ChallengeManager } from '~/widget/ChallengeManager/' +import { Header } from '~/widget/Header' export const HomePage = () => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() return ( @@ -19,5 +19,5 @@ export const HomePage = () => { - ); -}; + ) +} diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 758f83d..c0a22be 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -1,6 +1,6 @@ -import { Page, PageContent } from '../shared/ui'; -import { Header } from '../widget/Header'; -import { LoginWidget } from '../widget/Login'; +import { Page, PageContent } from '~/shared/ui' +import { Header } from '~/widget/Header' +import { LoginWidget } from '~/widget/Login' export const LoginPage = () => { return ( @@ -11,5 +11,5 @@ export const LoginPage = () => { - ); -}; + ) +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index d3970f4..3f0aa86 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,11 +1,13 @@ -import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom'; -import ChallengePage from './ChallengePage'; -import { LoginPage } from './LoginPage'; -import { AccountPage } from './AccountPage'; -import ProtectedRoute from '../feature/ProtectedRoute'; -import { ChallengeBuilderPage } from './ChallengeBuilderPage'; -import { Routes } from '../shared/constants'; -import { HomePage } from './HomePage'; +import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom' + +import { AccountPage } from './AccountPage' +import { ChallengeBuilderPage } from './ChallengeBuilderPage' +import ChallengePage from './ChallengePage' +import { HomePage } from './HomePage' +import { LoginPage } from './LoginPage' + +import ProtectedRoute from '~/feature/ProtectedRoute' +import { Routes } from '~/shared/constants' const router = createBrowserRouter([ { @@ -32,10 +34,10 @@ const router = createBrowserRouter([ path: Routes.CREATE_CHALLENGE, element: } />, }, -]); +]) const ApplicationRouter = () => { - return ; -}; + return +} -export default ApplicationRouter; +export default ApplicationRouter diff --git a/src/shared/api/account.service.ts b/src/shared/api/account.service.ts index f3975ba..5e11877 100644 --- a/src/shared/api/account.service.ts +++ b/src/shared/api/account.service.ts @@ -1,19 +1,19 @@ -import { api } from './api'; -import { SuccessResponse } from './types'; +import { api } from './api' +import { SuccessResponse } from './types' export type UserDTO = { - id: string; - createdAt: string; - updatedAt: string; - email: string; -}; + id: string + createdAt: string + updatedAt: string + email: string +} function createAccountService() { return { getAccountInfo: () => { - return api.get>>('/protected/user'); + return api.get>>('/protected/user') }, - }; + } } -export const accountService = createAccountService(); +export const accountService = createAccountService() diff --git a/src/shared/api/api.ts b/src/shared/api/api.ts index 4c0e72a..bee8b1e 100644 --- a/src/shared/api/api.ts +++ b/src/shared/api/api.ts @@ -1,39 +1,51 @@ -import axios, { AxiosError, AxiosRequestConfig } from 'axios'; -import { authService } from './auth.service'; -import { EventEmitter } from '../lib/EventEmitter'; +import axios, { AxiosError, AxiosRequestConfig } from 'axios' +import i18n from 'i18next' + +import { authService } from './auth.service' +import { EventEmitter } from '../lib/EventEmitter' + +import { LanguageMap, Languages } from '~/i18n/languageMap' // eslint-disable-next-line @typescript-eslint/no-explicit-any type RequestConfig = AxiosRequestConfig & { - retry?: boolean; -}; + retry?: boolean +} export const api = axios.create({ baseURL: import.meta.env.VITE_SERVER_URL, -}); +}) + +api.defaults.headers.common['Content-Type'] = 'application/json' +api.defaults.withCredentials = true + +api.interceptors.request.use((config) => { + const currentLanguage = i18n.language as Languages + + config.headers['Accept-Language'] = LanguageMap[currentLanguage] ?? LanguageMap.en -api.defaults.headers.common['Content-Type'] = 'application/json'; -api.defaults.withCredentials = true; + return config +}) api.interceptors.response.use( (response) => response, async (error: AxiosError) => { - const config = error?.config as RequestConfig; + const config = error?.config as RequestConfig if (error.response?.status === 401 && !config?.retry) { - config.retry = true; + config.retry = true try { - await authService.refreshToken(); + await authService.refreshToken() } catch (e) { - console.error('User authentication failed'); - EventEmitter.emit('refreshTokenExpired'); + console.error('User authentication failed') + EventEmitter.emit('refreshTokenExpired') - await Promise.reject(e); + await Promise.reject(e) } - return api(config); + return api(config) } - return Promise.reject(error); + return Promise.reject(error) }, -); +) diff --git a/src/shared/api/auth.service.ts b/src/shared/api/auth.service.ts index 60f6441..bf4c9c3 100644 --- a/src/shared/api/auth.service.ts +++ b/src/shared/api/auth.service.ts @@ -3,61 +3,62 @@ import { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON, -} from '@simplewebauthn/types'; -import { api } from './api'; +} from '@simplewebauthn/types' + +import { api } from './api' type LoginPayload = { - email: string; -}; + email: string +} type ConfirmLogin = { - email: string; - code: string; -}; + email: string + code: string +} export interface VerifyLoginChallenge { - email: string; - challengeResponse: AuthenticationResponseJSON; + email: string + challengeResponse: AuthenticationResponseJSON } export type GenerateLoginChallengeResponse = { - success: boolean; - options: PublicKeyCredentialRequestOptionsJSON; -}; + success: boolean + options: PublicKeyCredentialRequestOptionsJSON +} function createAuthService() { return { login: (payload: LoginPayload) => { - return api.post('/auth/login', payload); + return api.post('/auth/login', payload) }, confirmLogin: (payload: ConfirmLogin) => { - return api.post('/auth/confirm-login', payload); + return api.post('/auth/confirm-login', payload) }, logout: () => { - return api.post('/auth/logout'); + return api.post('/auth/logout') }, refreshToken: () => { - return api.post('/auth/refresh-token'); + return api.post('/auth/refresh-token') }, registerKeys: () => { return api.post( '/protected/passkeys/generate-registration-options', - ); + ) }, verifyRegistration: (payload: RegistrationResponseJSON) => { - return api.post('/protected/passkeys/verify-registration', payload); + return api.post('/protected/passkeys/verify-registration', payload) }, authenticateKeys: (email: string) => { return api.post( '/protected/passkeys/generate-authentication-options', { email }, - ); + ) }, verifyAuthentication: (payload: VerifyLoginChallenge) => { - return api.post('/protected/passkeys/verify-authentication', payload); + return api.post('/protected/passkeys/verify-authentication', payload) }, - }; + } } -export const authService = createAuthService(); +export const authService = createAuthService() diff --git a/src/shared/api/challenge.service.ts b/src/shared/api/challenge.service.ts index c2df1ad..6b04932 100644 --- a/src/shared/api/challenge.service.ts +++ b/src/shared/api/challenge.service.ts @@ -1,63 +1,63 @@ -import { api } from './api'; -import { SuccessResponse } from './types'; +import { api } from './api' +import { SuccessResponse } from './types' export type ChallengeDTO = { - id: string; - goal: string; - startedAtDate: string; // "2024-09-01" - duration: number; // 30 - description: string | null; - userId: string; - createdAt: string; // "2024-10-01T21:16:47.497Z" - updatedAt: string; // "2024-10-01T21:16:47.497Z" - progress: Array; -}; + id: string + goal: string + startedAtDate: string // "2024-09-01" + duration: number // 30 + description: string | null + userId: string + createdAt: string // "2024-10-01T21:16:47.497Z" + updatedAt: string // "2024-10-01T21:16:47.497Z" + progress: Array +} export type ProgressDTO = { - id: string; - checkpointDate: string; // "2024-09-01" - createdAt: string; // "2024-10-01T21:16:47.497Z" - updatedAt: string; // "2024-10-01T21:16:47.497Z" - userChallengeId: string; -}; + id: string + checkpointDate: string // "2024-09-01" + createdAt: string // "2024-10-01T21:16:47.497Z" + updatedAt: string // "2024-10-01T21:16:47.497Z" + userChallengeId: string +} type CreateChallengePayload = { - goal: string; - startedAtDate: string; // "2024-09-01" - duration: number; // 30 - description: string | null; -}; + goal: string + startedAtDate: string // "2024-09-01" + duration: number // 30 + description: string | null +} type CheckinPayload = { - checkpointDate: string; // "2024-09-01" - userChallengeId: string; -}; + checkpointDate: string // "2024-09-01" + userChallengeId: string +} function createChallengeService() { return { getChallengeList() { return api.get>>>( '/protected/challenge/', - ); + ) }, getChallengeById(challengeId: string) { return api.get>>( `/protected/challenge/${challengeId}`, - ); + ) }, createChallenge(payload: CreateChallengePayload) { - return api.post('/protected/challenge/create', payload); + return api.post('/protected/challenge/create', payload) }, deleteChallenge(challengeId: string) { - return api.delete(`/protected/challenge/${challengeId}`); + return api.delete(`/protected/challenge/${challengeId}`) }, checkin(payload: CheckinPayload) { - return api.post('/protected/challenge/check-in', payload); + return api.post('/protected/challenge/check-in', payload) }, removeCheckin(checkinId: string) { - return api.delete(`/protected/challenge/check-in/${checkinId}`); + return api.delete(`/protected/challenge/check-in/${checkinId}`) }, - }; + } } -export const challengeService = createChallengeService(); +export const challengeService = createChallengeService() diff --git a/src/shared/api/types.ts b/src/shared/api/types.ts index 05884cf..63d6d26 100644 --- a/src/shared/api/types.ts +++ b/src/shared/api/types.ts @@ -1,7 +1,7 @@ export type SuccessResponse = { - isSuccess: boolean; - message: string; - statusCode: number; - type: string; - details: T; -}; + isSuccess: boolean + message: string + statusCode: number + type: string + details: T +} diff --git a/src/shared/constants/index.ts b/src/shared/constants/index.ts index a382098..49800c7 100644 --- a/src/shared/constants/index.ts +++ b/src/shared/constants/index.ts @@ -1 +1 @@ -export * from './routes'; +export * from './routes' diff --git a/src/shared/constants/routes.ts b/src/shared/constants/routes.ts index 5b2c364..d5e5f30 100644 --- a/src/shared/constants/routes.ts +++ b/src/shared/constants/routes.ts @@ -4,4 +4,4 @@ export const Routes = { ACCOUNT: '/account', CHALLENGE: '/challenge/:challengeId', CREATE_CHALLENGE: '/create-challenge', -}; +} diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts index 6ccd393..45a071f 100644 --- a/src/shared/hooks/index.ts +++ b/src/shared/hooks/index.ts @@ -1,2 +1 @@ -export * from './useVisitorId'; -export * from './useLongPress'; +export * from './useVisitorId' diff --git a/src/shared/hooks/useLongPress.ts b/src/shared/hooks/useLongPress.ts deleted file mode 100644 index e2071d5..0000000 --- a/src/shared/hooks/useLongPress.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useRef, useState } from 'react'; - -export const useLongPress = () => { - const [action, setAction] = useState(); - - const timerRef = useRef(); - const isLongPress = useRef(); - - function startPressTimer() { - isLongPress.current = false; - timerRef.current = setTimeout(() => { - isLongPress.current = true; - setAction('longpress'); - }, 500); - } - - function handleOnClick() { - console.log('handleOnClick'); - if (isLongPress.current) { - console.log('Is long press - not continuing.'); - return; - } - setAction('click'); - } - - function handleOnMouseDown() { - console.log('handleOnMouseDown'); - startPressTimer(); - } - - function handleOnMouseUp() { - console.log('handleOnMouseUp'); - clearTimeout(timerRef.current); - } - - function handleOnTouchStart() { - console.log('handleOnTouchStart'); - startPressTimer(); - } - - function handleOnTouchEnd() { - if (action === 'longpress') return; - console.log('handleOnTouchEnd'); - clearTimeout(timerRef.current); - } - - return { - action, - handlers: { - onClick: handleOnClick, - onMouseDown: handleOnMouseDown, - onMouseUp: handleOnMouseUp, - onTouchStart: handleOnTouchStart, - onTouchEnd: handleOnTouchEnd, - }, - }; -}; diff --git a/src/shared/hooks/useVisitorId.ts b/src/shared/hooks/useVisitorId.ts index a7dfa41..23e18ea 100644 --- a/src/shared/hooks/useVisitorId.ts +++ b/src/shared/hooks/useVisitorId.ts @@ -1,13 +1,14 @@ -import { useEffect } from 'react'; -import LocalStorageService from '../lib/LocalStorage'; +import { useEffect } from 'react' + +import LocalStorageService from '../lib/LocalStorage' export const useVisitorId = () => { useEffect(() => { - const visitorId = LocalStorageService.getItem('visitorId'); + const visitorId = LocalStorageService.getItem('visitorId') if (!visitorId) { - const newVisitorId = crypto.randomUUID(); - LocalStorageService.setItem('visitorId', newVisitorId); + const newVisitorId = crypto.randomUUID() + LocalStorageService.setItem('visitorId', newVisitorId) } - }, []); -}; + }, []) +} diff --git a/src/shared/lib/BrowserExtractor.ts b/src/shared/lib/BrowserExtractor.ts index 6e7e7a4..cc52536 100644 --- a/src/shared/lib/BrowserExtractor.ts +++ b/src/shared/lib/BrowserExtractor.ts @@ -1,86 +1,86 @@ -import { IExtractor } from '../types'; +import { IExtractor } from '../types' export interface BrowserMeta { - browser: string; - version: string; + browser: string + version: string } export class BrowserExtractor implements IExtractor { constructor(private userAgent: string) {} public extract(): BrowserMeta { - return this.getBrowserFromUserAgent(this.userAgent); + return this.getBrowserFromUserAgent(this.userAgent) } private getBrowserFromUserAgent(userAgent: string): BrowserMeta { if (userAgent.includes('Chrome') && !userAgent.includes('Chromium')) { - return this.extractChromeVersion(userAgent); + return this.extractChromeVersion(userAgent) } if (userAgent.includes('Firefox')) { - return this.extractFirefoxVersion(userAgent); + return this.extractFirefoxVersion(userAgent) } if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) { - return this.extractSafariVersion(userAgent); + return this.extractSafariVersion(userAgent) } if (userAgent.includes('Edg')) { - return this.extractEdgeVersion(userAgent); + return this.extractEdgeVersion(userAgent) } if (userAgent.includes('OPR') || userAgent.includes('Opera')) { - return this.extractOperaVersion(userAgent); + return this.extractOperaVersion(userAgent) } return { browser: 'Unknown Browser', version: 'N/A', - }; + } } private extractChromeVersion(userAgent: string): BrowserMeta { - const chromeVersion = userAgent.match(/Chrome\/(\d+\.\d+\.\d+\.\d+)/); + const chromeVersion = userAgent.match(/Chrome\/(\d+\.\d+\.\d+\.\d+)/) return { browser: 'Chrome', version: chromeVersion ? chromeVersion[1] : 'N/A', - }; + } } private extractFirefoxVersion(userAgent: string): BrowserMeta { - const firefoxVersion = userAgent.match(/Firefox\/(\d+\.\d+)/); + const firefoxVersion = userAgent.match(/Firefox\/(\d+\.\d+)/) return { browser: 'Firefox', version: firefoxVersion ? firefoxVersion[1] : 'N/A', - }; + } } private extractSafariVersion(userAgent: string): BrowserMeta { - const safariVersion = userAgent.match(/Version\/(\d+\.\d+)/); + const safariVersion = userAgent.match(/Version\/(\d+\.\d+)/) return { browser: 'Safari', version: safariVersion ? safariVersion[1] : 'N/A', - }; + } } private extractEdgeVersion(userAgent: string): BrowserMeta { - const edgeVersion = userAgent.match(/Edg\/(\d+\.\d+)/); + const edgeVersion = userAgent.match(/Edg\/(\d+\.\d+)/) return { browser: 'Edge', version: edgeVersion ? edgeVersion[1] : 'N/A', - }; + } } private extractOperaVersion(userAgent: string): BrowserMeta { - const operaVersion = userAgent.match(/OPR\/(\d+\.\d+)/); + const operaVersion = userAgent.match(/OPR\/(\d+\.\d+)/) return { browser: 'Opera', version: operaVersion ? operaVersion[1] : 'N/A', - }; + } } } diff --git a/src/shared/lib/EventEmitter.ts b/src/shared/lib/EventEmitter.ts index 237d11d..70ae2c1 100644 --- a/src/shared/lib/EventEmitter.ts +++ b/src/shared/lib/EventEmitter.ts @@ -1,38 +1,38 @@ export const EventsMap = { refreshTokenExpired: 'refreshTokenExpired', -} as const; +} as const -type Events = keyof typeof EventsMap; +type Events = keyof typeof EventsMap // eslint-disable-next-line @typescript-eslint/no-explicit-any -type FunctionArgs = any[]; +type FunctionArgs = any[] -type Function = (...args: FunctionArgs) => void; +type Function = (...args: FunctionArgs) => void class EventEmitterService { private events: Record = { refreshTokenExpired: [], - }; + } on(event: Events, listener: Function) { if (!this.events[event]) { - this.events[event] = []; + this.events[event] = [] } - this.events[event].push(listener); + this.events[event].push(listener) } off(event: Events, listener: Function) { if (!this.events[event]) { - return; + return } - this.events[event] = this.events[event].filter((l) => l !== listener); + this.events[event] = this.events[event].filter((l) => l !== listener) } emit(event: Events, ...args: FunctionArgs) { if (this.events[event]) { - this.events[event].forEach((listener) => listener(...args)); + this.events[event].forEach((listener) => listener(...args)) } } } -export const EventEmitter = new EventEmitterService(); +export const EventEmitter = new EventEmitterService() diff --git a/src/shared/lib/FingerprintService.ts b/src/shared/lib/FingerprintService.ts index 5a372b8..dde0414 100644 --- a/src/shared/lib/FingerprintService.ts +++ b/src/shared/lib/FingerprintService.ts @@ -1,13 +1,13 @@ interface Fingerprint { - userAgent: string; - maxTouchPoints: string; - pixelRatio: string; - width: string; - height: string; - colorDepth: string; - pixelDepth: string; - canvasFingerprint: string; - webGLFingerprint: string; + userAgent: string + maxTouchPoints: string + pixelRatio: string + width: string + height: string + colorDepth: string + pixelDepth: string + canvasFingerprint: string + webGLFingerprint: string } export class FingerprintService { @@ -24,60 +24,60 @@ export class FingerprintService { pixelDepth: this.getPixelDepth(), canvasFingerprint: await this.getCanvasFingerprint(), webGLFingerprint: await this.getWebGLFingerprint(), - }; + } } private getUserAgent(): string { - return navigator.userAgent; + return navigator.userAgent } private getMaxTouchPoints(): string { - return navigator.maxTouchPoints.toString(); + return navigator.maxTouchPoints.toString() } private getPixelRatio(): string { - return window.devicePixelRatio.toString(); + return window.devicePixelRatio.toString() } private getScreenHeight(): string { - return window.screen.height.toString(); + return window.screen.height.toString() } private getScreenWidth(): string { - return window.screen.width.toString(); + return window.screen.width.toString() } private getColorDepth(): string { - return window.screen.colorDepth.toString(); + return window.screen.colorDepth.toString() } private getPixelDepth(): string { - return window.screen.pixelDepth.toString(); + return window.screen.pixelDepth.toString() } private async getCanvasFingerprint(): Promise { - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') if (ctx) { - ctx.textBaseline = 'top'; - ctx.font = '14px Arial'; - ctx.fillStyle = '#f60'; - ctx.fillRect(125, 1, 62, 20); - ctx.fillStyle = '#069'; - ctx.fillText('fingerprint', 2, 15); + ctx.textBaseline = 'top' + ctx.font = '14px Arial' + ctx.fillStyle = '#f60' + ctx.fillRect(125, 1, 62, 20) + ctx.fillStyle = '#069' + ctx.fillText('fingerprint', 2, 15) } - return canvas.toDataURL(); + return canvas.toDataURL() } private async getWebGLFingerprint(): Promise { - const canvas = document.createElement('canvas'); - const gl = canvas.getContext('webgl'); - if (!gl) return ''; + const canvas = document.createElement('canvas') + const gl = canvas.getContext('webgl') + if (!gl) return '' - const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); - const renderer = gl.getParameter(debugInfo?.UNMASKED_RENDERER_WEBGL || 0); - const vendor = gl.getParameter(debugInfo?.UNMASKED_VENDOR_WEBGL || 0); + const debugInfo = gl.getExtension('WEBGL_debug_renderer_info') + const renderer = gl.getParameter(debugInfo?.UNMASKED_RENDERER_WEBGL || 0) + const vendor = gl.getParameter(debugInfo?.UNMASKED_VENDOR_WEBGL || 0) - return `${renderer}-${vendor}`; + return `${renderer}-${vendor}` } } diff --git a/src/shared/lib/LocalStorage.ts b/src/shared/lib/LocalStorage.ts index 786613f..4af41ca 100644 --- a/src/shared/lib/LocalStorage.ts +++ b/src/shared/lib/LocalStorage.ts @@ -1,44 +1,44 @@ class LocalStorageService { setItem(storageKey: string, value: T): void { try { - const serializedValue = JSON.stringify(value); - localStorage.setItem(storageKey, serializedValue); - console.info(`${storageKey} set in localStorage`); + const serializedValue = JSON.stringify(value) + localStorage.setItem(storageKey, serializedValue) + console.info(`${storageKey} set in localStorage`) } catch (error) { - console.error(`Error setting ${storageKey} in localStorage`, error); + console.error(`Error setting ${storageKey} in localStorage`, error) } } getItem(storageKey: string): T | null { try { - const serializedValue = localStorage.getItem(storageKey); + const serializedValue = localStorage.getItem(storageKey) if (serializedValue === null) { - return null; + return null } - console.info(`${storageKey} loaded from localStorage`); - return JSON.parse(serializedValue) as T; + console.info(`${storageKey} loaded from localStorage`) + return JSON.parse(serializedValue) as T } catch (error) { - console.error(`Error getting ${storageKey} from localStorage`, error); - return null; + console.error(`Error getting ${storageKey} from localStorage`, error) + return null } } removeItem(storageKey: string): void { try { - localStorage.removeItem(storageKey); - console.info(`${storageKey} removed from localStorage`); + localStorage.removeItem(storageKey) + console.info(`${storageKey} removed from localStorage`) } catch (error) { - console.error(`Error removing ${storageKey} from localStorage`, error); + console.error(`Error removing ${storageKey} from localStorage`, error) } } clear(): void { try { - localStorage.clear(); + localStorage.clear() } catch (error) { - console.error('Error clearing localStorage', error); + console.error('Error clearing localStorage', error) } } } -export default new LocalStorageService(); +export default new LocalStorageService() diff --git a/src/shared/lib/OSExtractor.ts b/src/shared/lib/OSExtractor.ts index a4687ac..c51cbf4 100644 --- a/src/shared/lib/OSExtractor.ts +++ b/src/shared/lib/OSExtractor.ts @@ -1,80 +1,80 @@ -import { IExtractor } from '../types'; +import { IExtractor } from '../types' export interface OSMeta { - platform: string; - version: string; + platform: string + version: string } export class OSExtractor implements IExtractor { constructor(private userAgent: string) {} public extract(): OSMeta { - return this.getOSFromUserAgent(this.userAgent); + return this.getOSFromUserAgent(this.userAgent) } private getOSFromUserAgent(userAgent: string): OSMeta { if (userAgent.includes('Mac OS X')) { - return this.extractMacosMeta(userAgent); + return this.extractMacosMeta(userAgent) } if (userAgent.includes('Windows NT')) { - return this.extractWindowsVersion(userAgent); + return this.extractWindowsVersion(userAgent) } if (userAgent.includes('Linux')) { return { platform: 'Linux', version: 'N/A', - }; + } } if (userAgent.includes('iPhone') || userAgent.includes('iPad')) { - return this.extractIphoneVersion(userAgent); + return this.extractIphoneVersion(userAgent) } if (userAgent.includes('Android')) { - return this.extractAndroidVersion(userAgent); + return this.extractAndroidVersion(userAgent) } return { platform: 'Unknown OS', version: 'N/A', - }; + } } private extractMacosMeta(userAgent: string): OSMeta { - const macVersion = userAgent.match(/Mac OS X (\d+[_\.]\d+[_\.]?\d*)/); + const macVersion = userAgent.match(/Mac OS X (\d+[_\.]\d+[_\.]?\d*)/) return { platform: 'Mac OS X', version: macVersion ? macVersion[1].replace(/_/g, '.') : 'N/A', - }; + } } private extractWindowsVersion(userAgent: string): OSMeta { - const windowsVersion = userAgent.match(/Windows NT (\d+\.\d+)/); + const windowsVersion = userAgent.match(/Windows NT (\d+\.\d+)/) return { platform: 'Windows', version: windowsVersion ? windowsVersion[1] : 'N/A', - }; + } } private extractIphoneVersion(userAgent: string): OSMeta { - const iosVersion = userAgent.match(/OS (\d+[_\.]\d+[_\.]?\d*) like Mac OS X/); + const iosVersion = userAgent.match(/OS (\d+[_\.]\d+[_\.]?\d*) like Mac OS X/) return { platform: 'iOS', version: iosVersion ? iosVersion[1].replace(/_/g, '.') : 'N/A', - }; + } } private extractAndroidVersion(userAgent: string): OSMeta { - const androidVersion = userAgent.match(/Android (\d+[_\.]?\d*)/); + const androidVersion = userAgent.match(/Android (\d+[_\.]?\d*)/) return { platform: 'Android', version: androidVersion ? androidVersion[1] : 'N/A', - }; + } } } diff --git a/src/shared/lib/arrayBufferToBase64.ts b/src/shared/lib/arrayBufferToBase64.ts deleted file mode 100644 index 9514cc8..0000000 --- a/src/shared/lib/arrayBufferToBase64.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function arrayBufferToBase64(buffer: number[]): string { - // Создаём массив из буфера - const binary = String.fromCharCode(...new Uint8Array(buffer)); - - // Преобразуем бинарные данные в строку Base64 - return btoa(binary); -} diff --git a/src/shared/lib/hashData.ts b/src/shared/lib/hashData.ts index 2f50519..66cbbbd 100644 --- a/src/shared/lib/hashData.ts +++ b/src/shared/lib/hashData.ts @@ -1,10 +1,10 @@ export const hashData = async (data: string): Promise => { - const encoder = new TextEncoder(); - const dataBuffer = encoder.encode(data); + const encoder = new TextEncoder() + const dataBuffer = encoder.encode(data) - const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer); + const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer) - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join(''); - return hashHex; -}; + const hashArray = Array.from(new Uint8Array(hashBuffer)) + const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('') + return hashHex +} diff --git a/src/shared/lib/index.ts b/src/shared/lib/index.ts index 8be4adc..4f6d044 100644 --- a/src/shared/lib/index.ts +++ b/src/shared/lib/index.ts @@ -1,7 +1,5 @@ -export * from './LocalStorage'; -export * from './FingerprintService'; -export * from './hashData'; -export * from './OSExtractor'; -export * from './BrowserExtractor'; -export * from './isPublicKeyCredentialSupported'; -export * from './arrayBufferToBase64'; +export * from './LocalStorage' +export * from './FingerprintService' +export * from './hashData' +export * from './OSExtractor' +export * from './BrowserExtractor' diff --git a/src/shared/lib/isPublicKeyCredentialSupported.ts b/src/shared/lib/isPublicKeyCredentialSupported.ts deleted file mode 100644 index 57dfef4..0000000 --- a/src/shared/lib/isPublicKeyCredentialSupported.ts +++ /dev/null @@ -1,21 +0,0 @@ -export const isPublicKeyCredentialSupported = async (): Promise => { - try { - if ( - window.PublicKeyCredential && - PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable && - PublicKeyCredential.isConditionalMediationAvailable - ) { - const result = await Promise.all([ - PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(), - PublicKeyCredential.isConditionalMediationAvailable(), - ]); - - return result.every((r) => r === true); - } - - return false; - } catch (error) { - console.error(error); - return false; - } -}; diff --git a/src/shared/types/extractor.ts b/src/shared/types/extractor.ts index af11bd1..973ca15 100644 --- a/src/shared/types/extractor.ts +++ b/src/shared/types/extractor.ts @@ -1,3 +1,3 @@ export interface IExtractor { - extract(): Return; + extract(): Return } diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts index 63e1439..fd5621f 100644 --- a/src/shared/types/index.ts +++ b/src/shared/types/index.ts @@ -1 +1 @@ -export * from './extractor'; +export * from './extractor' diff --git a/src/shared/ui/Loader/index.tsx b/src/shared/ui/Loader/index.tsx index d73cd5c..c6e6f5d 100644 --- a/src/shared/ui/Loader/index.tsx +++ b/src/shared/ui/Loader/index.tsx @@ -1,5 +1,5 @@ export const Loader = () => { return (
- ); -}; + ) +} diff --git a/src/shared/ui/Modal/Modal.tsx b/src/shared/ui/Modal/Modal.tsx index fd1e758..14a0a58 100644 --- a/src/shared/ui/Modal/Modal.tsx +++ b/src/shared/ui/Modal/Modal.tsx @@ -1,21 +1,21 @@ type ModalButtonProps = { - label: string; - onClick: () => void; - color: string; -}; + label: string + onClick: () => void + color: string +} type Props = { - isOpen: boolean; - onClose: () => void; - title: string; - children: React.ReactNode; + isOpen: boolean + onClose: () => void + title: string + children: React.ReactNode - leftButton?: ModalButtonProps; - rightButton?: ModalButtonProps; -}; + leftButton?: ModalButtonProps + rightButton?: ModalButtonProps +} export const Modal = ({ isOpen, onClose, title, children, leftButton, rightButton }: Props) => { - if (!isOpen) return null; + if (!isOpen) return null return (
@@ -51,5 +51,5 @@ export const Modal = ({ isOpen, onClose, title, children, leftButton, rightButto
- ); -}; + ) +} diff --git a/src/shared/ui/Modal/index.ts b/src/shared/ui/Modal/index.ts index bffb54e..2550c5b 100644 --- a/src/shared/ui/Modal/index.ts +++ b/src/shared/ui/Modal/index.ts @@ -1,2 +1,2 @@ -export * from './Modal'; -export * from './useModalState'; +export * from './Modal' +export * from './useModalState' diff --git a/src/shared/ui/Modal/useModalState.ts b/src/shared/ui/Modal/useModalState.ts index 89f0b63..6c01231 100644 --- a/src/shared/ui/Modal/useModalState.ts +++ b/src/shared/ui/Modal/useModalState.ts @@ -1,18 +1,18 @@ -import { useCallback, useState } from 'react'; +import { useCallback, useState } from 'react' export const useModalState = () => { - const [isOpen, setIsOpen] = useState(false); + const [isOpen, setIsOpen] = useState(false) - const open = useCallback(() => setIsOpen(true), []); + const open = useCallback(() => setIsOpen(true), []) - const close = useCallback(() => setIsOpen(false), []); + const close = useCallback(() => setIsOpen(false), []) - const toggle = useCallback(() => setIsOpen((prev) => !prev), []); + const toggle = useCallback(() => setIsOpen((prev) => !prev), []) return { isOpen, open, close, toggle, - }; -}; + } +} diff --git a/src/shared/ui/Page/Page.tsx b/src/shared/ui/Page/Page.tsx index 86d6cc0..b784583 100644 --- a/src/shared/ui/Page/Page.tsx +++ b/src/shared/ui/Page/Page.tsx @@ -1,11 +1,11 @@ -import { PropsWithChildren } from 'react'; +import { PropsWithChildren } from 'react' -type Props = PropsWithChildren; +type Props = PropsWithChildren export const Page = ({ children }: Props) => { return (
{children}
- ); -}; + ) +} diff --git a/src/shared/ui/Page/PageContent.tsx b/src/shared/ui/Page/PageContent.tsx index c846fef..77aef1d 100644 --- a/src/shared/ui/Page/PageContent.tsx +++ b/src/shared/ui/Page/PageContent.tsx @@ -1,7 +1,7 @@ -import { PropsWithChildren } from 'react'; +import { PropsWithChildren } from 'react' -type Props = PropsWithChildren; +type Props = PropsWithChildren export const PageContent = ({ children }: Props) => { - return
{children}
; -}; + return
{children}
+} diff --git a/src/shared/ui/Page/index.ts b/src/shared/ui/Page/index.ts index 6ecd92a..ca65928 100644 --- a/src/shared/ui/Page/index.ts +++ b/src/shared/ui/Page/index.ts @@ -1,2 +1,2 @@ -export * from './Page'; -export * from './PageContent'; +export * from './Page' +export * from './PageContent' diff --git a/src/shared/ui/PlusIcon/index.tsx b/src/shared/ui/PlusIcon/index.tsx index 459478f..795fc7a 100644 --- a/src/shared/ui/PlusIcon/index.tsx +++ b/src/shared/ui/PlusIcon/index.tsx @@ -1,6 +1,6 @@ type Props = { - color?: string; -}; + color?: string +} export const PlusIcon = ({ color }: Props) => { return ( @@ -13,5 +13,5 @@ export const PlusIcon = ({ color }: Props) => { - ); -}; + ) +} diff --git a/src/shared/ui/RightArrow/index.tsx b/src/shared/ui/RightArrow/index.tsx index 6154b0a..a179f0c 100644 --- a/src/shared/ui/RightArrow/index.tsx +++ b/src/shared/ui/RightArrow/index.tsx @@ -9,5 +9,5 @@ export const RightArrow = () => { > - ); -}; + ) +} diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts index 7c2883a..dccaa25 100644 --- a/src/shared/ui/index.ts +++ b/src/shared/ui/index.ts @@ -1,4 +1,4 @@ -export * from './Page'; -export * from './RightArrow'; -export * from './PlusIcon'; -export * from './Modal'; +export * from './Page' +export * from './RightArrow' +export * from './PlusIcon' +export * from './Modal' diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 8118adc..9f20b12 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,12 +1,12 @@ /// interface ImportMetaEnv { - readonly VITE_SERVER_URL: string; - readonly VITE_SECURITY_HEADER_KEY: string; - readonly VITE_SECURITY_HEADER_VALUE: string; - readonly VITE_APP_VERSION: string; + readonly VITE_SERVER_URL: string + readonly VITE_SECURITY_HEADER_KEY: string + readonly VITE_SECURITY_HEADER_VALUE: string + readonly VITE_APP_VERSION: string } interface ImportMeta { - readonly env: ImportMetaEnv; + readonly env: ImportMetaEnv } diff --git a/src/widget/Account/AccountHeader.tsx b/src/widget/Account/AccountHeader.tsx index 727c242..6d839eb 100644 --- a/src/widget/Account/AccountHeader.tsx +++ b/src/widget/Account/AccountHeader.tsx @@ -1,7 +1,7 @@ -import { useCustomTranslation } from '../../feature/translation'; +import { useCustomTranslation } from '~/feature/translation' export const AccountHeader = () => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() return (
@@ -12,5 +12,5 @@ export const AccountHeader = () => { {t('yourAccount')}
- ); -}; + ) +} diff --git a/src/widget/Account/PasskeysManager.tsx b/src/widget/Account/PasskeysManager.tsx deleted file mode 100644 index 3ae4037..0000000 --- a/src/widget/Account/PasskeysManager.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { useMutation } from '@tanstack/react-query'; -import { authService } from '../../shared/api/auth.service'; -import { isPublicKeyCredentialSupported } from '../../shared/lib'; -import { startAuthentication, startRegistration } from '@simplewebauthn/browser'; - -export const PasskeysManager = () => { - const verifyChallenge = useMutation({ - mutationFn: authService.verifyRegistration, - onSuccess: (data) => { - console.info('[VerifyChallenge:onSuccess]', data); - }, - onError: (err) => { - console.info(`[VerifyChallenge:onError]: ${JSON.stringify(err)}`); - }, - }); - - const generateChallengeMutation = useMutation({ - mutationFn: authService.registerKeys, - onSuccess: async (result) => { - console.info('[GenerateChallenge:onSuccess]', result); - - const options = structuredClone(result.data); - - try { - console.log(options); - const attResult = await startRegistration({ optionsJSON: options }); - - verifyChallenge.mutate(attResult); - } catch (error: unknown) { - console.error(error); - } - }, - onError: (err) => { - console.info(`[GenerateChallenge:onError]: ${JSON.stringify(err)}`); - }, - }); - - const verifyLoginChallenge = useMutation({ - mutationFn: authService.verifyAuthentication, - onSuccess: (data) => { - console.info('[VerifyLoginChallenge:onSuccess]', data); - }, - onError: (err) => { - console.info(`[VerifyLoginChallenge:onError]: ${JSON.stringify(err)}`); - }, - }); - - const generateLoginChallenge = useMutation({ - mutationFn: authService.authenticateKeys, - onSuccess: async (resp, variables) => { - console.info('[GenerateLoginChallenge:onSuccess]', resp); - - const options = resp.data.options; - - try { - console.log('Passkey options', options); - const result = await startAuthentication({ optionsJSON: options }); - - console.log(result); - verifyLoginChallenge.mutate({ - email: variables, - challengeResponse: result, - }); - } catch (error: unknown) { - console.error(error); - } - }, - onError: (err) => { - console.info(`[GenerateLoginChallenge:onError]: ${JSON.stringify(err)}`); - }, - }); - - const createChallenge = async () => { - const isSupported = await isPublicKeyCredentialSupported(); - - if (!isSupported) { - return console.error('WebAuthn is not supported'); - } - - generateChallengeMutation.mutate(); - }; - - const loginChallenge = async () => { - const isSupported = await isPublicKeyCredentialSupported(); - - if (!isSupported) { - return console.error('WebAuthn is not supported'); - } - - generateLoginChallenge.mutate('vitya.ryabkov@gmail.com'); - }; - - return ( -
-

Passkeys Manager

- -
- - - -
-
- ); -}; diff --git a/src/widget/Account/UserInfoPanel.tsx b/src/widget/Account/UserInfoPanel.tsx index 52f393a..e2e63c9 100644 --- a/src/widget/Account/UserInfoPanel.tsx +++ b/src/widget/Account/UserInfoPanel.tsx @@ -1,18 +1,18 @@ -import { LogoutButton } from '../../feature/Logout'; -import { RegisterPasskeys } from '../../feature/RegisterPasskeys/RegisterPasskeys'; -import { UserDTO } from '../../shared/api/account.service'; +import { LogoutButton } from '~/feature/Logout' +import { RegisterPasskeys } from '~/feature/RegisterPasskeys' +import { UserDTO } from '~/shared/api/account.service' type Props = { - isLoading: boolean; + isLoading: boolean - user?: UserDTO; -}; + user?: UserDTO +} export const UserInfoPanel = (props: Props) => { if (props.isLoading) { return (
- ); + ) } return ( @@ -25,5 +25,5 @@ export const UserInfoPanel = (props: Props) => {
- ); -}; + ) +} diff --git a/src/widget/Account/index.tsx b/src/widget/Account/index.tsx index cf8020e..ebd5000 100644 --- a/src/widget/Account/index.tsx +++ b/src/widget/Account/index.tsx @@ -1,21 +1,23 @@ -import { useQuery } from '@tanstack/react-query'; -import { accountService } from '../../shared/api/account.service'; -import { UserInfoPanel } from './UserInfoPanel'; -import { AccountHeader } from './AccountHeader'; +import { useQuery } from '@tanstack/react-query' + +import { AccountHeader } from './AccountHeader' +import { UserInfoPanel } from './UserInfoPanel' + +import { accountService } from '~/shared/api/account.service' export const AccountWidget = () => { const query = useQuery({ queryKey: ['/protected/me'], queryFn: accountService.getAccountInfo, select(data) { - return data.data.details; + return data.data.details }, - }); + }) return (
- ); -}; + ) +} diff --git a/src/widget/Account/lib/mappers.ts b/src/widget/Account/lib/mappers.ts index 62f16de..cc17c13 100644 --- a/src/widget/Account/lib/mappers.ts +++ b/src/widget/Account/lib/mappers.ts @@ -1,28 +1,28 @@ -import { ChallengeDTO } from '../../../shared/api/challenge.service'; +import { ChallengeDTO } from '~/shared/api/challenge.service' type ChallengeItem = { - id: string; - goal: string; - daysLeft: number; - isActive: boolean; -}; + id: string + goal: string + daysLeft: number + isActive: boolean +} export const mapChallengeToItem = (challenge: ChallengeDTO): ChallengeItem => { - const startedAt = new Date(challenge.startedAtDate); + const startedAt = new Date(challenge.startedAtDate) - const endAt = new Date(startedAt); - endAt.setDate(startedAt.getDate() + challenge.duration); + const endAt = new Date(startedAt) + endAt.setDate(startedAt.getDate() + challenge.duration) - const today = new Date(); - const timeDiff = endAt.getTime() - today.getTime(); - const daysLeft = Math.ceil(timeDiff / (1000 * 3600 * 24)); + const today = new Date() + const timeDiff = endAt.getTime() - today.getTime() + const daysLeft = Math.ceil(timeDiff / (1000 * 3600 * 24)) - const isActive = daysLeft > 0; + const isActive = daysLeft > 0 return { id: challenge.id, goal: challenge.goal, daysLeft: daysLeft < 0 ? 0 : daysLeft, isActive: isActive, - }; -}; + } +} diff --git a/src/widget/AppVersion/index.tsx b/src/widget/AppVersion/index.tsx index 3dca09d..b4e48d5 100644 --- a/src/widget/AppVersion/index.tsx +++ b/src/widget/AppVersion/index.tsx @@ -1,9 +1,9 @@ export const AppVersion = () => { - const appVersion = import.meta.env.VITE_APP_VERSION ?? 'v0.0.0'; + const appVersion = import.meta.env.VITE_APP_VERSION ?? 'v0.0.0' return (
{appVersion}
- ); -}; + ) +} diff --git a/src/widget/Challenge/index.ts b/src/widget/Challenge/index.ts index 5ecdd1f..ed58495 100644 --- a/src/widget/Challenge/index.ts +++ b/src/widget/Challenge/index.ts @@ -1 +1 @@ -export * from './ui'; +export * from './ui' diff --git a/src/widget/Challenge/lib/convertDate.ts b/src/widget/Challenge/lib/convertDate.ts index b50d2ef..9143084 100644 --- a/src/widget/Challenge/lib/convertDate.ts +++ b/src/widget/Challenge/lib/convertDate.ts @@ -1,10 +1,10 @@ export function convertDate(day: number, exactMonth?: number) { - const today = new Date(); - const year = today.getFullYear(); + const today = new Date() + const year = today.getFullYear() - const rawMonth = exactMonth ? exactMonth : today.getMonth(); - const month = String(rawMonth + 1).padStart(2, '0'); - const dayString = String(day).padStart(2, '0'); + const rawMonth = exactMonth ? exactMonth : today.getMonth() + const month = String(rawMonth + 1).padStart(2, '0') + const dayString = String(day).padStart(2, '0') - return `${year}-${month}-${dayString}`; + return `${year}-${month}-${dayString}` } diff --git a/src/widget/Challenge/lib/useStreakState.ts b/src/widget/Challenge/lib/useStreakState.ts index 262336a..490d196 100644 --- a/src/widget/Challenge/lib/useStreakState.ts +++ b/src/widget/Challenge/lib/useStreakState.ts @@ -1,49 +1,58 @@ -import { useMutation, useQuery } from '@tanstack/react-query'; -import { challengeService } from '../../../shared/api/challenge.service'; -import { queryClient } from '../../../app/App'; -import { useCallback } from 'react'; -import { convertDate } from './convertDate'; +import { useCallback } from 'react' + +import { useMutation, useQuery } from '@tanstack/react-query' + +import { convertDate } from './convertDate' + +import { queryClient } from '~/app/App' +import { challengeService } from '~/shared/api/challenge.service' type Props = { - challengeId: string; -}; + challengeId: string +} export const useStreakState = ({ challengeId }: Props) => { const challengeQuery = useQuery({ queryKey: ['/challenge/', challengeId], queryFn: () => challengeService.getChallengeById(challengeId), select(data) { - return data.data.details; + return data.data.details }, - }); + }) const progressMutation = useMutation({ mutationFn: challengeService.checkin, onSettled: async () => { - return queryClient.invalidateQueries({ queryKey: ['/challenge/progress', challengeId] }); + return queryClient.invalidateQueries({ queryKey: ['/challenge/progress', challengeId] }) }, async onSuccess() { - challengeQuery.refetch(); + challengeQuery.refetch() }, - }); + }) const removeProgressMutation = useMutation({ mutationFn: challengeService.removeCheckin, onSettled: async () => { - return queryClient.invalidateQueries({ queryKey: ['/challenge/progress', challengeId] }); + return queryClient.invalidateQueries({ queryKey: ['/challenge/progress', challengeId] }) }, async onSuccess() { - challengeQuery.refetch(); + challengeQuery.refetch() }, - }); + }) - const addDayInStreak = useCallback((day: number) => { - progressMutation.mutate({ userChallengeId: challengeId, checkpointDate: convertDate(day) }); - }, []); + const addDayInStreak = useCallback( + (day: number) => { + progressMutation.mutate({ userChallengeId: challengeId, checkpointDate: convertDate(day) }) + }, + [challengeId, progressMutation], + ) - const removeDayFromStreak = useCallback((checkinId: string) => { - removeProgressMutation.mutate(checkinId); - }, []); + const removeDayFromStreak = useCallback( + (checkinId: string) => { + removeProgressMutation.mutate(checkinId) + }, + [removeProgressMutation], + ) return { isLoading: challengeQuery.isLoading, @@ -52,5 +61,5 @@ export const useStreakState = ({ challengeId }: Props) => { addDayInStreak, removeDayFromStreak, - }; -}; + } +} diff --git a/src/widget/Challenge/ui/Calendar.tsx b/src/widget/Challenge/ui/Calendar.tsx index 4abb0c9..e9e966f 100644 --- a/src/widget/Challenge/ui/Calendar.tsx +++ b/src/widget/Challenge/ui/Calendar.tsx @@ -1,28 +1,26 @@ -import { getDay, getDaysInMonth, getMonth, startOfMonth } from 'date-fns'; +import { getDay, getDaysInMonth, getMonth, startOfMonth } from 'date-fns' +import { useTranslation } from 'react-i18next' -import { CalendarDayItem } from './CalendarDayItem'; -import { CalendarHeaderItem } from './CalendarHeaderItem'; -import { useTranslation } from 'react-i18next'; -import { convertDate } from '../lib/convertDate'; +import { CalendarDayItem } from './CalendarDayItem' +import { CalendarHeaderItem } from './CalendarHeaderItem' +import { convertDate } from '../lib/convertDate' type Props = { - streak: string[]; - isCompleted: boolean; - onDayClick: (day: number) => void; -}; + streak: string[] + isCompleted: boolean + onDayClick: (day: number) => void +} export const Calendar = ({ streak, onDayClick, isCompleted }: Props) => { - const { t } = useTranslation(); + const { t } = useTranslation() - const now = new Date(); + const now = new Date() - const month = getMonth(isCompleted ? new Date(streak[0]) : now); + const month = getMonth(isCompleted ? new Date(streak[0]) : now) - console.log(month); + const daysInMonth = getDaysInMonth(isCompleted ? new Date(streak[0]) : now) - const daysInMonth = getDaysInMonth(isCompleted ? new Date(streak[0]) : now); - - const firstDayOfMonth = getDay(startOfMonth(now)); + const firstDayOfMonth = getDay(startOfMonth(now)) const daysOffset: Record = { 0: 6, @@ -32,12 +30,10 @@ export const Calendar = ({ streak, onDayClick, isCompleted }: Props) => { 4: 3, 5: 4, 6: 5, - }; - - console.log(streak); + } - const offset = Array.from({ length: daysOffset[firstDayOfMonth] }, (_, index) => index + 1); - const daysInMonthArray = Array.from({ length: daysInMonth }, (_, index) => index + 1); + const offset = Array.from({ length: daysOffset[firstDayOfMonth] }, (_, index) => index + 1) + const daysInMonthArray = Array.from({ length: daysInMonth }, (_, index) => index + 1) return (
@@ -60,11 +56,11 @@ export const Calendar = ({ streak, onDayClick, isCompleted }: Props) => { label={index.toString()} isChecked={streak.includes(convertDate(index, month))} onClick={() => { - onDayClick(index); + onDayClick(index) }} /> ))}
- ); -}; + ) +} diff --git a/src/widget/Challenge/ui/CalendarDayItem.tsx b/src/widget/Challenge/ui/CalendarDayItem.tsx index 5d102e5..706f500 100644 --- a/src/widget/Challenge/ui/CalendarDayItem.tsx +++ b/src/widget/Challenge/ui/CalendarDayItem.tsx @@ -1,8 +1,8 @@ type Props = { - label: string; - isChecked: boolean; - onClick: () => void; -}; + label: string + isChecked: boolean + onClick: () => void +} export const CalendarDayItem = ({ label, isChecked, onClick }: Props) => { return ( @@ -35,5 +35,5 @@ export const CalendarDayItem = ({ label, isChecked, onClick }: Props) => { /> - ); -}; + ) +} diff --git a/src/widget/Challenge/ui/CalendarHeaderItem.tsx b/src/widget/Challenge/ui/CalendarHeaderItem.tsx index e861d68..0415cd2 100644 --- a/src/widget/Challenge/ui/CalendarHeaderItem.tsx +++ b/src/widget/Challenge/ui/CalendarHeaderItem.tsx @@ -1,6 +1,6 @@ type Props = { - label: string; -}; + label: string +} export const CalendarHeaderItem = ({ label }: Props) => { return ( @@ -10,5 +10,5 @@ export const CalendarHeaderItem = ({ label }: Props) => {
- ); -}; + ) +} diff --git a/src/widget/Challenge/ui/CalendarManager.tsx b/src/widget/Challenge/ui/CalendarManager.tsx index a4af17c..94de828 100644 --- a/src/widget/Challenge/ui/CalendarManager.tsx +++ b/src/widget/Challenge/ui/CalendarManager.tsx @@ -1,19 +1,21 @@ -import { useCallback } from 'react'; -import { useCustomTranslation } from '../../../feature/translation'; -import { ChallengeDTO, ProgressDTO } from '../../../shared/api/challenge.service'; -import { MetaText } from './MetaText'; -import { convertDate } from '../lib/convertDate'; -import { Calendar } from './Calendar'; -import { mapChallengeToItem } from '../../Account/lib/mappers'; -import { Timer } from './Timer'; +import { useCallback } from 'react' + +import { Calendar } from './Calendar' +import { MetaText } from './MetaText' +import { Timer } from './Timer' +import { convertDate } from '../lib/convertDate' + +import { useCustomTranslation } from '~/feature/translation' +import { ChallengeDTO, ProgressDTO } from '~/shared/api/challenge.service' +import { mapChallengeToItem } from '~/widget/Account/lib/mappers' type Props = { - challenge: ChallengeDTO; - progress: Array; + challenge: ChallengeDTO + progress: Array - addDayInStreak: (day: number) => void; - removeDayFromStreak: (checkinId: string) => void; -}; + addDayInStreak: (day: number) => void + removeDayFromStreak: (checkinId: string) => void +} export const CalendarManager = ({ challenge, @@ -21,30 +23,28 @@ export const CalendarManager = ({ addDayInStreak, removeDayFromStreak, }: Props) => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() - const challengeBaseInfo = mapChallengeToItem(challenge); + const challengeBaseInfo = mapChallengeToItem(challenge) const onDayClick = useCallback( (day: number) => { - const isDayAlreadyChecked = progress - .map((el) => el.checkpointDate) - .includes(convertDate(day)); + const isDayAlreadyChecked = progress.map((el) => el.checkpointDate).includes(convertDate(day)) if (!isDayAlreadyChecked) { - addDayInStreak(day); - return; + addDayInStreak(day) + return } - const checkinId = progress.find((el) => el.checkpointDate === convertDate(day))?.id; + const checkinId = progress.find((el) => el.checkpointDate === convertDate(day))?.id if (checkinId) { - removeDayFromStreak(checkinId); - return; + removeDayFromStreak(checkinId) + return } }, - [addDayInStreak, progress], - ); + [addDayInStreak, progress, removeDayFromStreak], + ) return (
@@ -66,5 +66,5 @@ export const CalendarManager = ({ el.checkpointDate) ?? []} /> )}
- ); -}; + ) +} diff --git a/src/widget/Challenge/ui/MetaText.tsx b/src/widget/Challenge/ui/MetaText.tsx index 9fffe53..63c1dd3 100644 --- a/src/widget/Challenge/ui/MetaText.tsx +++ b/src/widget/Challenge/ui/MetaText.tsx @@ -1,7 +1,7 @@ type Props = { - leftLabel: string; - rightLabel: string; -}; + leftLabel: string + rightLabel: string +} export const MetaText = ({ leftLabel, rightLabel }: Props) => { return ( @@ -9,5 +9,5 @@ export const MetaText = ({ leftLabel, rightLabel }: Props) => { {leftLabel} {rightLabel} - ); -}; + ) +} diff --git a/src/widget/Challenge/ui/Timer.tsx b/src/widget/Challenge/ui/Timer.tsx index d486824..d2ab47a 100644 --- a/src/widget/Challenge/ui/Timer.tsx +++ b/src/widget/Challenge/ui/Timer.tsx @@ -1,50 +1,52 @@ -import { differenceInSeconds, endOfDay, getDate } from 'date-fns'; -import { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { convertDate } from '../lib/convertDate'; +import { useEffect, useState } from 'react' + +import { differenceInSeconds, endOfDay, getDate } from 'date-fns' +import { useTranslation } from 'react-i18next' + +import { convertDate } from '../lib/convertDate' type Props = { - streak: string[]; -}; + streak: string[] +} export const Timer = ({ streak }: Props) => { - const { t } = useTranslation(); + const { t } = useTranslation() - const [timeLeft, setTimeLeft] = useState(0); + const [timeLeft, setTimeLeft] = useState(0) const displayMap = { 0: 'block', 1: 'hidden', - }; + } // Format time left as HH:MM:SS const formatTimeLeft = (seconds: number) => { - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - const secs = seconds % 60; + const hours = Math.floor(seconds / 3600) + const minutes = Math.floor((seconds % 3600) / 60) + const secs = seconds % 60 - return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; - }; + return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}` + } // Calculate seconds remaining until the end of the day const calculateTimeLeft = () => { - const now = new Date(); - const endOfDayTime = endOfDay(now); - return differenceInSeconds(endOfDayTime, now); - }; + const now = new Date() + const endOfDayTime = endOfDay(now) + return differenceInSeconds(endOfDayTime, now) + } - const isTodayCompleted = streak.includes(convertDate(getDate(new Date()))); + const isTodayCompleted = streak.includes(convertDate(getDate(new Date()))) useEffect(() => { - setTimeLeft(calculateTimeLeft()); + setTimeLeft(calculateTimeLeft()) const interval = setInterval(() => { - setTimeLeft(calculateTimeLeft()); - }, 1000); + setTimeLeft(calculateTimeLeft()) + }, 1000) // Clear interval on component unmount - return () => clearInterval(interval); - }, []); + return () => clearInterval(interval) + }, []) return (
@@ -63,5 +65,5 @@ export const Timer = ({ streak }: Props) => {

- ); -}; + ) +} diff --git a/src/widget/Challenge/ui/index.tsx b/src/widget/Challenge/ui/index.tsx index 9f41f05..2fe4091 100644 --- a/src/widget/Challenge/ui/index.tsx +++ b/src/widget/Challenge/ui/index.tsx @@ -1,23 +1,24 @@ -import Logo from '../../../assets/logo1.png'; -import { useStreakState } from '../lib/useStreakState'; -import { Loader } from '../../../shared/ui/Loader'; -import { CalendarManager } from './CalendarManager'; +import { CalendarManager } from './CalendarManager' +import { useStreakState } from '../lib/useStreakState' + +import Logo from '~/assets/logo1.png' +import { Loader } from '~/shared/ui/Loader' type Props = { - challengeId: string; -}; + challengeId: string +} export const ChallengeWidget = ({ challengeId }: Props) => { const { challenge, isLoading, addDayInStreak, removeDayFromStreak } = useStreakState({ challengeId, - }); + }) if (isLoading) { return (
;
- ); + ) } return ( @@ -37,5 +38,5 @@ export const ChallengeWidget = ({ challengeId }: Props) => { )} - ); -}; + ) +} diff --git a/src/widget/ChallengeBuilder/index.tsx b/src/widget/ChallengeBuilder/index.tsx index fedc3c5..c76ad3c 100644 --- a/src/widget/ChallengeBuilder/index.tsx +++ b/src/widget/ChallengeBuilder/index.tsx @@ -1,49 +1,51 @@ -import { useState } from 'react'; -import { RightArrow } from '../../shared/ui'; -import { QueryClient, useMutation } from '@tanstack/react-query'; -import { challengeService } from '../../shared/api/challenge.service'; -import { convertDate } from '../Challenge/lib/convertDate'; -import { getDaysInMonth } from 'date-fns'; -import { useNavigate } from 'react-router-dom'; -import { Routes } from '../../shared/constants'; +import { useState } from 'react' + +import { QueryClient, useMutation } from '@tanstack/react-query' +import { getDaysInMonth } from 'date-fns' +import { useNavigate } from 'react-router-dom' + +import { challengeService } from '~/shared/api/challenge.service' +import { Routes } from '~/shared/constants' +import { RightArrow } from '~/shared/ui' +import { convertDate } from '~/widget/Challenge/lib/convertDate' export const ChallengeBuilderWidget = () => { - const [goal, setGoal] = useState(''); - const [description, setDescription] = useState(''); + const [goal, setGoal] = useState('') + const [description, setDescription] = useState('') - const navigate = useNavigate(); + const navigate = useNavigate() const mutation = useMutation({ mutationFn: challengeService.createChallenge, onSuccess() { - const queryClient = new QueryClient(); + const queryClient = new QueryClient() queryClient.invalidateQueries({ queryKey: ['/challenge'], - }); + }) - return navigate(Routes.HOME); + return navigate(Routes.HOME) }, - }); + }) const onCreateClick = () => { if (goal.length < 1) { - throw new Error(`[ChallengeBuilderWidget:onCreate] Goal length should be more than 1`); + throw new Error(`[ChallengeBuilderWidget:onCreate] Goal length should be more than 1`) } - const now = new Date(); + const now = new Date() - const daysInMonth = getDaysInMonth(now); + const daysInMonth = getDaysInMonth(now) - const startDate = convertDate(1); + const startDate = convertDate(1) mutation.mutate({ goal, description, startedAtDate: startDate, duration: daysInMonth, - }); - }; + }) + } return (
@@ -86,5 +88,5 @@ export const ChallengeBuilderWidget = () => {
- ); -}; + ) +} diff --git a/src/widget/ChallengeManager/ChallengeGridItem.tsx b/src/widget/ChallengeManager/ChallengeGridItem.tsx index 12870ac..0a27993 100644 --- a/src/widget/ChallengeManager/ChallengeGridItem.tsx +++ b/src/widget/ChallengeManager/ChallengeGridItem.tsx @@ -1,9 +1,9 @@ type Props = { - goal: string; - isActive: boolean; - daysLeft: number; - onClick: () => void; -}; + goal: string + isActive: boolean + daysLeft: number + onClick: () => void +} export const ChallengeGridItem = ({ goal, isActive, daysLeft, onClick }: Props) => { return ( @@ -19,5 +19,5 @@ export const ChallengeGridItem = ({ goal, isActive, daysLeft, onClick }: Props) Days left: {daysLeft} - ); -}; + ) +} diff --git a/src/widget/ChallengeManager/ChallengeManager.tsx b/src/widget/ChallengeManager/ChallengeManager.tsx index 6d1899b..e89f02f 100644 --- a/src/widget/ChallengeManager/ChallengeManager.tsx +++ b/src/widget/ChallengeManager/ChallengeManager.tsx @@ -1,29 +1,32 @@ -import { useQuery } from '@tanstack/react-query'; -import { challengeService } from '../../shared/api/challenge.service'; -import { ChallengeGridItem } from './ChallengeGridItem'; -import { mapChallengeToItem } from '../Account/lib/mappers'; -import { Loader } from '../../shared/ui/Loader'; -import { CreateChallengeButton } from './CreateChallengeButton'; -import { useNavigate } from 'react-router-dom'; -import { ChallengeManagerHeader } from './ChallengeManagerHeader'; +import { useQuery } from '@tanstack/react-query' +import { useNavigate } from 'react-router-dom' + +import { ChallengeGridItem } from './ChallengeGridItem' +import { ChallengeManagerHeader } from './ChallengeManagerHeader' +import { CreateChallengeButton } from './CreateChallengeButton' + + +import { challengeService } from '~/shared/api/challenge.service' +import { Loader } from '~/shared/ui/Loader' +import { mapChallengeToItem } from '~/widget/Account/lib/mappers' export const ChallengeManager = () => { - const navigate = useNavigate(); + const navigate = useNavigate() const query = useQuery({ queryKey: ['/protected/challenge/'], queryFn: challengeService.getChallengeList, select: (data) => data.data.details, - }); + }) - const challenges = query.data?.challenges; + const challenges = query.data?.challenges if (query.isPending) { return (
;
- ); + ) } return ( @@ -41,7 +44,7 @@ export const ChallengeManager = () => { isActive={item.isActive} daysLeft={item.daysLeft} onClick={() => { - return navigate(`/challenge/${item.id}`); + return navigate(`/challenge/${item.id}`) }} /> ))} @@ -49,5 +52,5 @@ export const ChallengeManager = () => { - ); -}; + ) +} diff --git a/src/widget/ChallengeManager/ChallengeManagerHeader.tsx b/src/widget/ChallengeManager/ChallengeManagerHeader.tsx index d4c96e3..d44e814 100644 --- a/src/widget/ChallengeManager/ChallengeManagerHeader.tsx +++ b/src/widget/ChallengeManager/ChallengeManagerHeader.tsx @@ -1,7 +1,7 @@ -import { useCustomTranslation } from '../../feature/translation'; +import { useCustomTranslation } from '~/feature/translation' export const ChallengeManagerHeader = () => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() return (
@@ -12,5 +12,5 @@ export const ChallengeManagerHeader = () => { {t('yourChallenges')}
- ); -}; + ) +} diff --git a/src/widget/ChallengeManager/CreateChallengeButton.tsx b/src/widget/ChallengeManager/CreateChallengeButton.tsx index 9e0d975..9dc8d5c 100644 --- a/src/widget/ChallengeManager/CreateChallengeButton.tsx +++ b/src/widget/ChallengeManager/CreateChallengeButton.tsx @@ -1,9 +1,10 @@ -import { useNavigate } from 'react-router-dom'; -import { PlusIcon } from '../../shared/ui'; -import { Routes } from '../../shared/constants'; +import { useNavigate } from 'react-router-dom' + +import { Routes } from '~/shared/constants' +import { PlusIcon } from '~/shared/ui' export const CreateChallengeButton = () => { - const navigate = useNavigate(); + const navigate = useNavigate() return (
{
- ); -}; + ) +} diff --git a/src/widget/ChallengeManager/index.ts b/src/widget/ChallengeManager/index.ts index e57985e..9249282 100644 --- a/src/widget/ChallengeManager/index.ts +++ b/src/widget/ChallengeManager/index.ts @@ -1 +1 @@ -export * from './ChallengeManager'; +export * from './ChallengeManager' diff --git a/src/widget/Header/index.tsx b/src/widget/Header/index.tsx index b07e7a2..1dfd4d0 100644 --- a/src/widget/Header/index.tsx +++ b/src/widget/Header/index.tsx @@ -1,28 +1,29 @@ -import { useNavigate } from 'react-router-dom'; -import { LanguageSwitcher } from '../../feature/translation'; +import { useNavigate } from 'react-router-dom' + +import { LanguageSwitcher } from '~/feature/translation' type PropsWithNavidation = { - navigationButtonShown: true; - navigateTo: string; - labelNavigationButton: string; -}; + navigationButtonShown: true + navigateTo: string + labelNavigationButton: string +} type PropsWithoutNavigation = { - navigationButtonShown: false; -}; + navigationButtonShown: false +} -type Props = PropsWithNavidation | PropsWithoutNavigation; +type Props = PropsWithNavidation | PropsWithoutNavigation export const Header = (props: Props) => { - const navigator = useNavigate(); + const navigator = useNavigate() const onNavigationClick = () => { if (!props.navigationButtonShown) { - return; + return } - return navigator(props.navigateTo); - }; + return navigator(props.navigateTo) + } return (
@@ -38,5 +39,5 @@ export const Header = (props: Props) => {
- ); -}; + ) +} diff --git a/src/widget/Login/index.ts b/src/widget/Login/index.ts index 5ecdd1f..ed58495 100644 --- a/src/widget/Login/index.ts +++ b/src/widget/Login/index.ts @@ -1 +1 @@ -export * from './ui'; +export * from './ui' diff --git a/src/widget/Login/ui/LoginButton.tsx b/src/widget/Login/ui/LoginButton.tsx index b9c5b3a..fddc12a 100644 --- a/src/widget/Login/ui/LoginButton.tsx +++ b/src/widget/Login/ui/LoginButton.tsx @@ -1,13 +1,13 @@ -import { useCustomTranslation } from '../../../feature/translation'; +import { useCustomTranslation } from '~/feature/translation' type Props = { - labelKey: string; - onClick: () => void; - isLoading: boolean; -}; + labelKey: string + onClick: () => void + isLoading: boolean +} export const LoginButton = ({ onClick, isLoading, labelKey }: Props) => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() return (
@@ -22,5 +22,5 @@ export const LoginButton = ({ onClick, isLoading, labelKey }: Props) => { )}
- ); -}; + ) +} diff --git a/src/widget/Login/ui/LoginHeader.tsx b/src/widget/Login/ui/LoginHeader.tsx index 94b87fa..5aaa92f 100644 --- a/src/widget/Login/ui/LoginHeader.tsx +++ b/src/widget/Login/ui/LoginHeader.tsx @@ -1,12 +1,12 @@ -import { useCustomTranslation } from '../../../feature/translation'; +import { useCustomTranslation } from '~/feature/translation' export const LoginHeader = () => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() return (
-
-
+
+

@@ -14,5 +14,5 @@ export const LoginHeader = () => {

- ); -}; + ) +} diff --git a/src/widget/Login/ui/index.tsx b/src/widget/Login/ui/index.tsx index 8ff29ce..8eefa1d 100644 --- a/src/widget/Login/ui/index.tsx +++ b/src/widget/Login/ui/index.tsx @@ -1,137 +1,133 @@ -import z from 'zod'; -import { useCallback, useState } from 'react'; -import { useCustomTranslation } from '../../../feature/translation'; -import { useMutation } from '@tanstack/react-query'; -import { authService } from '../../../shared/api/auth.service'; -import { useNavigate } from 'react-router-dom'; -import { Routes } from '../../../shared/constants'; -import { useAuthenticateViaPasskeys } from '../../../feature/AuthorizePasskeys/lib/useAuthenticateViaPasskeys'; -import { browserSupportsWebAuthn, startAuthentication } from '@simplewebauthn/browser'; -import { LoginHeader } from './LoginHeader'; -import { LoginButton } from './LoginButton'; - -const emailSchema = z.string().email(); - -const codeSchema = z.string().regex(/^\d{6}$/, { message: 'Код должен содержать ровно 6 цифр' }); +import { useCallback, useState } from 'react' + +import { browserSupportsWebAuthn, startAuthentication } from '@simplewebauthn/browser' +import { useMutation } from '@tanstack/react-query' +import { useNavigate } from 'react-router-dom' +import z from 'zod' + +import { LoginButton } from './LoginButton' +import { LoginHeader } from './LoginHeader' + +import { useAuthenticateViaPasskeys } from '~/feature/AuthorizePasskeys/' +import { useCustomTranslation } from '~/feature/translation' +import { authService } from '~/shared/api/auth.service' +import { Routes } from '~/shared/constants' + +const emailSchema = z.string().email() + +const codeSchema = z.string().regex(/^\d{6}$/, { message: 'Код должен содержать ровно 6 цифр' }) export const LoginWidget = () => { - const { t } = useCustomTranslation(); + const { t } = useCustomTranslation() - const navigate = useNavigate(); + const navigate = useNavigate() const codeInputVisibility = new Map([ [true, '40px'], [false, '0px'], - ]); + ]) - const [email, setEmail] = useState(''); - const [code, setCode] = useState(''); - const [loginFlow, setLoginFlow] = useState<'otp' | 'passkeys' | null>(null); + const [email, setEmail] = useState('') + const [code, setCode] = useState('') + const [loginFlow, setLoginFlow] = useState<'otp' | 'passkeys' | null>(null) const loginMutation = useMutation({ mutationFn: authService.login, onSuccess: (data) => { - console.info('[LoginMutation:onSuccess]', data); + console.info('[LoginMutation:onSuccess]', data) }, onError: (err) => { - console.error(`[LoginMutation:onError] ${JSON.stringify(err)}`); + console.error(`[LoginMutation:onError] ${JSON.stringify(err)}`) }, - }); + }) const verifyLoginChallenge = useMutation({ mutationFn: authService.verifyAuthentication, onError: (err) => { - console.info(`[VerifyLoginChallenge:onError]: ${JSON.stringify(err)}`); + console.info(`[VerifyLoginChallenge:onError]: ${JSON.stringify(err)}`) }, - }); + }) const codeMutation = useMutation({ mutationFn: authService.confirmLogin, onSuccess: (data) => { - console.info('[CodeMutation:onSuccess]', data); + console.info('[CodeMutation:onSuccess]', data) - return navigate(Routes.HOME); + return navigate(Routes.HOME) }, onError: (err) => { - console.info(`[CodeMutation:onError]: ${JSON.stringify(err)}`); + console.info(`[CodeMutation:onError]: ${JSON.stringify(err)}`) }, - }); + }) const passkeysMutation = useAuthenticateViaPasskeys({ loginIfNoCredentials: (email: string) => { - console.info(`[LoginWidget:passkeysMutation] No credentials for: ${email}`); + console.info(`[LoginWidget:passkeysMutation] No credentials for: ${email}`) }, - }); + }) - const isEmailSent = loginMutation.isSuccess && !!loginMutation.data; + const isEmailSent = loginMutation.isSuccess && !!loginMutation.data const confirmLogin = () => { if (!isEmailSent) { - throw new Error('Email should be sent first'); + throw new Error('Email should be sent first') } - const emailSafeParse = emailSchema.safeParse(email); + const emailSafeParse = emailSchema.safeParse(email) if (emailSafeParse.error) { - throw new Error(JSON.stringify(emailSafeParse.error)); + throw new Error(JSON.stringify(emailSafeParse.error)) } - const codeSafeParse = codeSchema.safeParse(code); + const codeSafeParse = codeSchema.safeParse(code) if (codeSafeParse.error) { - throw new Error(JSON.stringify(codeSafeParse.error)); + throw new Error(JSON.stringify(codeSafeParse.error)) } - codeMutation.mutate({ email, code }); - }; + codeMutation.mutate({ email, code }) + } const handleLogin = useCallback(async () => { - const safeParse = emailSchema.safeParse(email); + const safeParse = emailSchema.safeParse(email) if (safeParse.error) { - throw new Error(JSON.stringify(safeParse.error)); + throw new Error(JSON.stringify(safeParse.error)) } if (!browserSupportsWebAuthn()) { - setLoginFlow('otp'); + setLoginFlow('otp') return loginMutation.mutate({ email: safeParse.data, - }); + }) } - const response = await passkeysMutation.mutateAsync(safeParse.data); + const response = await passkeysMutation.mutateAsync(safeParse.data) - const challengeOpts = response.data.options; + const challengeOpts = response.data.options const isCredentialExist = - challengeOpts.allowCredentials && challengeOpts.allowCredentials.length > 0; + challengeOpts.allowCredentials && challengeOpts.allowCredentials.length > 0 if (!isCredentialExist) { - setLoginFlow('otp'); + setLoginFlow('otp') return loginMutation.mutate({ email: safeParse.data, - }); + }) } - const result = await startAuthentication({ optionsJSON: challengeOpts }); + const result = await startAuthentication({ optionsJSON: challengeOpts }) - setLoginFlow('passkeys'); + setLoginFlow('passkeys') const verifyResult = await verifyLoginChallenge.mutateAsync({ email: safeParse.data, challengeResponse: result, - }); + }) if (verifyResult.data.success) { - return navigate(Routes.HOME); + return navigate(Routes.HOME) } - }, [ - email, - setLoginFlow, - loginMutation, - passkeysMutation, - verifyLoginChallenge, - startAuthentication, - ]); + }, [email, passkeysMutation, verifyLoginChallenge, loginMutation, navigate]) return (
@@ -180,5 +176,5 @@ export const LoginWidget = () => {

- ); -}; + ) +} diff --git a/tailwind.config.js b/tailwind.config.js index 25a819a..b6223e1 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -15,4 +15,4 @@ export default { }, }, plugins: [], -}; +} diff --git a/tsconfig.app.json b/tsconfig.app.json index 4b19ea7..6ff10d4 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -6,6 +6,11 @@ "module": "ESNext", "skipLibCheck": true, + "baseUrl": "./src", + "paths": { + "~/*": ["./*"] + }, + /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, diff --git a/tsconfig.app.tsbuildinfo b/tsconfig.app.tsbuildinfo new file mode 100644 index 0000000..429df7f --- /dev/null +++ b/tsconfig.app.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/vite-env.d.ts","./src/app/app.tsx","./src/app/main.tsx","./src/app/system/constant.ts","./src/app/system/i18n.manager.ts","./src/entity/user/index.ts","./src/entity/user/lib/useauthenticated.ts","./src/feature/authorizepasskeys/authorizepasskeys.tsx","./src/feature/authorizepasskeys/lib/useauthenticateviapasskeys.ts","./src/feature/logout/index.ts","./src/feature/logout/lib/uselogoutmutation.ts","./src/feature/logout/ui/logoutbutton.tsx","./src/feature/protectedroute/index.tsx","./src/feature/registerpasskeys/index.tsx","./src/feature/rememberme/index.ts","./src/feature/rememberme/lib/usedevicemeta.ts","./src/feature/translation/index.ts","./src/feature/translation/lib/usecustomtranslation.ts","./src/feature/translation/ui/languageswitcher.tsx","./src/pages/accountpage.tsx","./src/pages/challengebuilderpage.tsx","./src/pages/challengepage.tsx","./src/pages/homepage.tsx","./src/pages/loginpage.tsx","./src/pages/index.tsx","./src/shared/api/account.service.ts","./src/shared/api/api.ts","./src/shared/api/auth.service.ts","./src/shared/api/challenge.service.ts","./src/shared/api/types.ts","./src/shared/constants/index.ts","./src/shared/constants/routes.ts","./src/shared/hooks/index.ts","./src/shared/hooks/usevisitorid.ts","./src/shared/lib/browserextractor.ts","./src/shared/lib/eventemitter.ts","./src/shared/lib/fingerprintservice.ts","./src/shared/lib/localstorage.ts","./src/shared/lib/osextractor.ts","./src/shared/lib/hashdata.ts","./src/shared/lib/index.ts","./src/shared/types/extractor.ts","./src/shared/types/index.ts","./src/shared/ui/index.ts","./src/shared/ui/loader/index.tsx","./src/shared/ui/modal/modal.tsx","./src/shared/ui/modal/index.ts","./src/shared/ui/modal/usemodalstate.ts","./src/shared/ui/page/page.tsx","./src/shared/ui/page/pagecontent.tsx","./src/shared/ui/page/index.ts","./src/shared/ui/plusicon/index.tsx","./src/shared/ui/rightarrow/index.tsx","./src/widget/account/accountheader.tsx","./src/widget/account/userinfopanel.tsx","./src/widget/account/index.tsx","./src/widget/account/lib/mappers.ts","./src/widget/appversion/index.tsx","./src/widget/challenge/index.ts","./src/widget/challenge/lib/convertdate.ts","./src/widget/challenge/lib/usestreakstate.ts","./src/widget/challenge/ui/calendar.tsx","./src/widget/challenge/ui/calendardayitem.tsx","./src/widget/challenge/ui/calendarheaderitem.tsx","./src/widget/challenge/ui/calendarmanager.tsx","./src/widget/challenge/ui/metatext.tsx","./src/widget/challenge/ui/timer.tsx","./src/widget/challenge/ui/index.tsx","./src/widget/challengebuilder/index.tsx","./src/widget/challengemanager/challengegriditem.tsx","./src/widget/challengemanager/challengemanager.tsx","./src/widget/challengemanager/challengemanagerheader.tsx","./src/widget/challengemanager/createchallengebutton.tsx","./src/widget/challengemanager/index.ts","./src/widget/header/index.tsx","./src/widget/login/index.ts","./src/widget/login/ui/loginbutton.tsx","./src/widget/login/ui/loginheader.tsx","./src/widget/login/ui/index.tsx"],"version":"5.6.3"} \ No newline at end of file diff --git a/tsconfig.node.tsbuildinfo b/tsconfig.node.tsbuildinfo new file mode 100644 index 0000000..75ea001 --- /dev/null +++ b/tsconfig.node.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./vite.config.ts"],"version":"5.6.3"} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 70b284e..f1f543c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,8 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; -import mkcert from 'vite-plugin-mkcert'; -import { VitePWA } from 'vite-plugin-pwa'; +import react from '@vitejs/plugin-react' +import path from 'path' +import { defineConfig } from 'vite' +import mkcert from 'vite-plugin-mkcert' +import { VitePWA } from 'vite-plugin-pwa' // https://vitejs.dev/config/ export default defineConfig({ @@ -9,6 +10,12 @@ export default defineConfig({ manifest: true, }, + resolve: { + alias: { + '~': `${path.resolve(__dirname, 'src')}`, + }, + }, + plugins: [ react(), mkcert(), @@ -53,4 +60,4 @@ export default defineConfig({ }, }), ], -}); +}) diff --git a/yarn.lock b/yarn.lock index 37d1289..c3d5181 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,16 +35,6 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/code-frame@npm:7.24.7" - dependencies: - "@babel/highlight": "npm:^7.24.7" - picocolors: "npm:^1.0.0" - checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6 - languageName: node - linkType: hard - "@babel/code-frame@npm:^7.25.9": version: 7.25.9 resolution: "@babel/code-frame@npm:7.25.9" @@ -55,6 +45,17 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/code-frame@npm:7.26.0" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.25.9" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.0.0" + checksum: 10c0/46f7e367714be736b52ea3c01b24f47e2102e210fb83021d1c8237d8fc511b9538909e16e2fcdbb5cb6173e0794e28624309a59014e52fcfb7bde908f5284388 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.25.9": version: 7.25.9 resolution: "@babel/compat-data@npm:7.25.9" @@ -62,13 +63,6 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.25.2": - version: 7.25.4 - resolution: "@babel/compat-data@npm:7.25.4" - checksum: 10c0/50d79734d584a28c69d6f5b99adfaa064d0f41609a378aef04eb06accc5b44f8520e68549eba3a082478180957b7d5783f1bfb1672e4ae8574e797ce8bae79fa - languageName: node - linkType: hard - "@babel/core@npm:^7.24.4": version: 7.25.9 resolution: "@babel/core@npm:7.25.9" @@ -92,50 +86,51 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.24.5": - version: 7.25.2 - resolution: "@babel/core@npm:7.25.2" +"@babel/core@npm:^7.25.2": + version: 7.26.0 + resolution: "@babel/core@npm:7.26.0" dependencies: "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.24.7" - "@babel/generator": "npm:^7.25.0" - "@babel/helper-compilation-targets": "npm:^7.25.2" - "@babel/helper-module-transforms": "npm:^7.25.2" - "@babel/helpers": "npm:^7.25.0" - "@babel/parser": "npm:^7.25.0" - "@babel/template": "npm:^7.25.0" - "@babel/traverse": "npm:^7.25.2" - "@babel/types": "npm:^7.25.2" + "@babel/code-frame": "npm:^7.26.0" + "@babel/generator": "npm:^7.26.0" + "@babel/helper-compilation-targets": "npm:^7.25.9" + "@babel/helper-module-transforms": "npm:^7.26.0" + "@babel/helpers": "npm:^7.26.0" + "@babel/parser": "npm:^7.26.0" + "@babel/template": "npm:^7.25.9" + "@babel/traverse": "npm:^7.25.9" + "@babel/types": "npm:^7.26.0" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10c0/a425fa40e73cb72b6464063a57c478bc2de9dbcc19c280f1b55a3d88b35d572e87e8594e7d7b4880331addb6faef641bbeb701b91b41b8806cd4deae5d74f401 + checksum: 10c0/91de73a7ff5c4049fbc747930aa039300e4d2670c2a91f5aa622f1b4868600fc89b01b6278385fbcd46f9574186fa3d9b376a9e7538e50f8d118ec13cfbcb63e languageName: node linkType: hard -"@babel/generator@npm:^7.25.0, @babel/generator@npm:^7.25.6": - version: 7.25.6 - resolution: "@babel/generator@npm:7.25.6" +"@babel/generator@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/generator@npm:7.25.9" dependencies: - "@babel/types": "npm:^7.25.6" + "@babel/types": "npm:^7.25.9" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" - jsesc: "npm:^2.5.1" - checksum: 10c0/f89282cce4ddc63654470b98086994d219407d025497f483eb03ba102086e11e2b685b27122f6ff2e1d93b5b5fa0c3a6b7e974fbf2e4a75b685041a746a4291e + jsesc: "npm:^3.0.2" + checksum: 10c0/fca49a1440ac550bb835a73c0e8314849cd493a468a5431ca7f9dbb3d3443e3a1a6dcba2426752e8a97cc2feed4a3b7a0c639e1c45871c4a9dd0c994f08dd25a languageName: node linkType: hard -"@babel/generator@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/generator@npm:7.25.9" +"@babel/generator@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/generator@npm:7.26.0" dependencies: - "@babel/types": "npm:^7.25.9" + "@babel/parser": "npm:^7.26.0" + "@babel/types": "npm:^7.26.0" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^3.0.2" - checksum: 10c0/fca49a1440ac550bb835a73c0e8314849cd493a468a5431ca7f9dbb3d3443e3a1a6dcba2426752e8a97cc2feed4a3b7a0c639e1c45871c4a9dd0c994f08dd25a + checksum: 10c0/b6bb9185f19a97eaf58e04a6d39a13237076678e7ed16b6321dea914535d4bf6a8d7727c9dcb65539845aa0096b326eb67be4bab764bd74bcfd848e2eda68609 languageName: node linkType: hard @@ -171,19 +166,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.25.2": - version: 7.25.2 - resolution: "@babel/helper-compilation-targets@npm:7.25.2" - dependencies: - "@babel/compat-data": "npm:^7.25.2" - "@babel/helper-validator-option": "npm:^7.24.8" - browserslist: "npm:^4.23.1" - lru-cache: "npm:^5.1.1" - semver: "npm:^6.3.1" - checksum: 10c0/de10e986b5322c9f807350467dc845ec59df9e596a5926a3b5edbb4710d8e3b8009d4396690e70b88c3844fe8ec4042d61436dd4b92d1f5f75655cf43ab07e99 - languageName: node - linkType: hard - "@babel/helper-create-class-features-plugin@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-create-class-features-plugin@npm:7.25.9" @@ -249,41 +231,30 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-module-imports@npm:7.24.7" - dependencies: - "@babel/traverse": "npm:^7.24.7" - "@babel/types": "npm:^7.24.7" - checksum: 10c0/97c57db6c3eeaea31564286e328a9fb52b0313c5cfcc7eee4bc226aebcf0418ea5b6fe78673c0e4a774512ec6c86e309d0f326e99d2b37bfc16a25a032498af0 - languageName: node - linkType: hard - -"@babel/helper-module-transforms@npm:^7.25.2": - version: 7.25.2 - resolution: "@babel/helper-module-transforms@npm:7.25.2" +"@babel/helper-module-transforms@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-module-transforms@npm:7.25.9" dependencies: - "@babel/helper-module-imports": "npm:^7.24.7" - "@babel/helper-simple-access": "npm:^7.24.7" - "@babel/helper-validator-identifier": "npm:^7.24.7" - "@babel/traverse": "npm:^7.25.2" + "@babel/helper-module-imports": "npm:^7.25.9" + "@babel/helper-simple-access": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.25.9" + "@babel/traverse": "npm:^7.25.9" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10c0/adaa15970ace0aee5934b5a633789b5795b6229c6a9cf3e09a7e80aa33e478675eee807006a862aa9aa517935d81f88a6db8a9f5936e3a2a40ec75f8062bc329 + checksum: 10c0/cd005e7585806845d79c5c0ca9e8926f186b430b0a558dad08a3611365eaad3ac587672b0d903530117dec454f48b6bdc3d164b19ea1b71ca1b4eb3be7b452ef languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/helper-module-transforms@npm:7.25.9" +"@babel/helper-module-transforms@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/helper-module-transforms@npm:7.26.0" dependencies: "@babel/helper-module-imports": "npm:^7.25.9" - "@babel/helper-simple-access": "npm:^7.25.9" "@babel/helper-validator-identifier": "npm:^7.25.9" "@babel/traverse": "npm:^7.25.9" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10c0/cd005e7585806845d79c5c0ca9e8926f186b430b0a558dad08a3611365eaad3ac587672b0d903530117dec454f48b6bdc3d164b19ea1b71ca1b4eb3be7b452ef + checksum: 10c0/ee111b68a5933481d76633dad9cdab30c41df4479f0e5e1cc4756dc9447c1afd2c9473b5ba006362e35b17f4ebddd5fca090233bef8dfc84dca9d9127e56ec3a languageName: node linkType: hard @@ -303,13 +274,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.24.7": - version: 7.24.8 - resolution: "@babel/helper-plugin-utils@npm:7.24.8" - checksum: 10c0/0376037f94a3bfe6b820a39f81220ac04f243eaee7193774b983e956c1750883ff236b30785795abbcda43fac3ece74750566830c2daa4d6e3870bb0dff34c2d - languageName: node - linkType: hard - "@babel/helper-remap-async-to-generator@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-remap-async-to-generator@npm:7.25.9" @@ -336,16 +300,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-simple-access@npm:7.24.7" - dependencies: - "@babel/traverse": "npm:^7.24.7" - "@babel/types": "npm:^7.24.7" - checksum: 10c0/7230e419d59a85f93153415100a5faff23c133d7442c19e0cd070da1784d13cd29096ee6c5a5761065c44e8164f9f80e3a518c41a0256df39e38f7ad6744fed7 - languageName: node - linkType: hard - "@babel/helper-simple-access@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-simple-access@npm:7.25.9" @@ -394,13 +348,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/helper-validator-option@npm:7.24.8" - checksum: 10c0/73db93a34ae89201351288bee7623eed81a54000779462a986105b54ffe82069e764afd15171a428b82e7c7a9b5fec10b5d5603b216317a414062edf5c67a21f - languageName: node - linkType: hard - "@babel/helper-validator-option@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-validator-option@npm:7.25.9" @@ -419,16 +366,6 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.25.0": - version: 7.25.6 - resolution: "@babel/helpers@npm:7.25.6" - dependencies: - "@babel/template": "npm:^7.25.0" - "@babel/types": "npm:^7.25.6" - checksum: 10c0/448c1cdabccca42fd97a252f73f1e4bcd93776dbf24044f3b4f49b756bf2ece73ee6df05177473bb74ea7456dddd18d6f481e4d96d2cc7839d078900d48c696c - languageName: node - linkType: hard - "@babel/helpers@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helpers@npm:7.25.9" @@ -439,15 +376,13 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/highlight@npm:7.24.7" +"@babel/helpers@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/helpers@npm:7.26.0" dependencies: - "@babel/helper-validator-identifier": "npm:^7.24.7" - chalk: "npm:^2.4.2" - js-tokens: "npm:^4.0.0" - picocolors: "npm:^1.0.0" - checksum: 10c0/674334c571d2bb9d1c89bdd87566383f59231e16bcdcf5bb7835babdf03c9ae585ca0887a7b25bdf78f303984af028df52831c7989fecebb5101cc132da9393a + "@babel/template": "npm:^7.25.9" + "@babel/types": "npm:^7.26.0" + checksum: 10c0/343333cced6946fe46617690a1d0789346960910225ce359021a88a60a65bc0d791f0c5d240c0ed46cf8cc63b5fd7df52734ff14e43b9c32feae2b61b1647097 languageName: node linkType: hard @@ -463,7 +398,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.0, @babel/parser@npm:^7.25.6": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7": version: 7.25.6 resolution: "@babel/parser@npm:7.25.6" dependencies: @@ -485,6 +420,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.26.0": + version: 7.26.1 + resolution: "@babel/parser@npm:7.26.1" + dependencies: + "@babel/types": "npm:^7.26.0" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/dc7d4e6b7eb667fa0784e7e2c3f6f92ca12ad72242f6d4311995310dae55093f02acdb595b69b0dbbf04cb61ad87156ac03186ff32eacfa35149c655bc22c14b + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.9": version: 7.25.9 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.9" @@ -1038,25 +984,25 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-self@npm:^7.24.5": - version: 7.24.7 - resolution: "@babel/plugin-transform-react-jsx-self@npm:7.24.7" +"@babel/plugin-transform-react-jsx-self@npm:^7.24.7": + version: 7.25.9 + resolution: "@babel/plugin-transform-react-jsx-self@npm:7.25.9" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.25.9" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/dcf3b732401f47f06bb29d6016e48066f66de00029a0ded98ddd9983c770a00a109d91cd04d2700d15ee0bcec3ae3027a5f12d69e15ec56efc0bcbfac65e92cb + checksum: 10c0/ce0e289f6af93d7c4dc6b385512199c5bb138ae61507b4d5117ba88b6a6b5092f704f1bdf80080b7d69b1b8c36649f2a0b250e8198667d4d30c08bbb1546bd99 languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-source@npm:^7.24.1": - version: 7.24.7 - resolution: "@babel/plugin-transform-react-jsx-source@npm:7.24.7" +"@babel/plugin-transform-react-jsx-source@npm:^7.24.7": + version: 7.25.9 + resolution: "@babel/plugin-transform-react-jsx-source@npm:7.25.9" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.25.9" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/970ef1264c7c6c416ab11610665d5309aec2bd2b9086ae394e1132e65138d97b060a7dc9d31054e050d6dc475b5a213938c9707c0202a5022d55dcb4c5abe28f + checksum: 10c0/fc9ee08efc9be7cbd2cc6788bbf92579adf3cab37912481f1b915221be3d22b0613b5b36a721df5f4c0ab65efe8582fcf8673caab83e6e1ce4cc04ceebf57dfa languageName: node linkType: hard @@ -1286,7 +1232,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.24.8": +"@babel/runtime@npm:^7.23.2": version: 7.25.6 resolution: "@babel/runtime@npm:7.25.6" dependencies: @@ -1295,14 +1241,12 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/template@npm:7.25.0" +"@babel/runtime@npm:^7.25.0": + version: 7.26.0 + resolution: "@babel/runtime@npm:7.26.0" dependencies: - "@babel/code-frame": "npm:^7.24.7" - "@babel/parser": "npm:^7.25.0" - "@babel/types": "npm:^7.25.0" - checksum: 10c0/4e31afd873215744c016e02b04f43b9fa23205d6d0766fb2e93eb4091c60c1b88897936adb895fb04e3c23de98dfdcbe31bc98daaa1a4e0133f78bb948e1209b + regenerator-runtime: "npm:^0.14.0" + checksum: 10c0/12c01357e0345f89f4f7e8c0e81921f2a3e3e101f06e8eaa18a382b517376520cd2fa8c237726eb094dab25532855df28a7baaf1c26342b52782f6936b07c287 languageName: node linkType: hard @@ -1317,21 +1261,6 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.24.7, @babel/traverse@npm:^7.25.2": - version: 7.25.6 - resolution: "@babel/traverse@npm:7.25.6" - dependencies: - "@babel/code-frame": "npm:^7.24.7" - "@babel/generator": "npm:^7.25.6" - "@babel/parser": "npm:^7.25.6" - "@babel/template": "npm:^7.25.0" - "@babel/types": "npm:^7.25.6" - debug: "npm:^4.3.1" - globals: "npm:^11.1.0" - checksum: 10c0/964304c6fa46bd705428ba380bf73177eeb481c3f26d82ea3d0661242b59e0dd4329d23886035e9ca9a4ceb565c03a76fd615109830687a27bcd350059d6377e - languageName: node - linkType: hard - "@babel/traverse@npm:^7.25.9": version: 7.25.9 resolution: "@babel/traverse@npm:7.25.9" @@ -1347,7 +1276,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.0, @babel/types@npm:^7.25.2, @babel/types@npm:^7.25.6": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.6": version: 7.25.6 resolution: "@babel/types@npm:7.25.6" dependencies: @@ -1368,6 +1297,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/types@npm:7.26.0" + dependencies: + "@babel/helper-string-parser": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.25.9" + checksum: 10c0/b694f41ad1597127e16024d766c33a641508aad037abd08d0d1f73af753e1119fa03b4a107d04b5f92cc19c095a594660547ae9bead1db2299212d644b0a5cb8 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/aix-ppc64@npm:0.21.5" @@ -1558,10 +1497,10 @@ __metadata: languageName: node linkType: hard -"@eslint/core@npm:^0.6.0": - version: 0.6.0 - resolution: "@eslint/core@npm:0.6.0" - checksum: 10c0/fffdb3046ad6420f8cb9204b6466fdd8632a9baeebdaf2a97d458a4eac0e16653ba50d82d61835d7d771f6ced0ec942ec482b2fbccc300e45f2cbf784537f240 +"@eslint/core@npm:^0.7.0": + version: 0.7.0 + resolution: "@eslint/core@npm:0.7.0" + checksum: 10c0/3cdee8bc6cbb96ac6103d3ead42e59830019435839583c9eb352b94ed558bd78e7ffad5286dc710df21ec1e7bd8f52aa6574c62457a4dd0f01f3736fa4a7d87a languageName: node linkType: hard @@ -1582,10 +1521,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.11.1": - version: 9.11.1 - resolution: "@eslint/js@npm:9.11.1" - checksum: 10c0/22916ef7b09c6f60c62635d897c66e1e3e38d90b5a5cf5e62769033472ecbcfb6ec7c886090a4b32fe65d6ce371da54384e46c26a899e38184dfc152c6152f7b +"@eslint/js@npm:9.13.0": + version: 9.13.0 + resolution: "@eslint/js@npm:9.13.0" + checksum: 10c0/672257bffe17777b8a98bd80438702904cc7a0b98b9c2e426a8a10929198b3553edf8a3fc20feed4133c02e7c8f7331a0ef1b23e5dab8e4469f7f1791beff1e0 languageName: node linkType: hard @@ -1605,6 +1544,23 @@ __metadata: languageName: node linkType: hard +"@humanfs/core@npm:^0.19.0": + version: 0.19.0 + resolution: "@humanfs/core@npm:0.19.0" + checksum: 10c0/f87952d5caba6ae427a620eff783c5d0b6cef0cfc256dec359cdaa636c5f161edb8d8dad576742b3de7f0b2f222b34aad6870248e4b7d2177f013426cbcda232 + languageName: node + linkType: hard + +"@humanfs/node@npm:^0.16.5": + version: 0.16.5 + resolution: "@humanfs/node@npm:0.16.5" + dependencies: + "@humanfs/core": "npm:^0.19.0" + "@humanwhocodes/retry": "npm:^0.3.0" + checksum: 10c0/41c365ab09e7c9eaeed373d09243195aef616d6745608a36fc3e44506148c28843872f85e69e2bf5f1e992e194286155a1c1cecfcece6a2f43875e37cd243935 + languageName: node + linkType: hard + "@humanwhocodes/module-importer@npm:^1.0.1": version: 1.0.1 resolution: "@humanwhocodes/module-importer@npm:1.0.1" @@ -1619,6 +1575,13 @@ __metadata: languageName: node linkType: hard +"@humanwhocodes/retry@npm:^0.3.1": + version: 0.3.1 + resolution: "@humanwhocodes/retry@npm:0.3.1" + checksum: 10c0/f0da1282dfb45e8120480b9e2e275e2ac9bbe1cf016d046fdad8e27cc1285c45bb9e711681237944445157b430093412b4446c1ab3fc4bb037861b5904101d3b + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -1702,7 +1665,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": +"@nodelib/fs.walk@npm:^1.2.3": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -1873,10 +1836,10 @@ __metadata: languageName: node linkType: hard -"@remix-run/router@npm:1.19.1": - version: 1.19.1 - resolution: "@remix-run/router@npm:1.19.1" - checksum: 10c0/9101fc96646e5107b6b0ef248d4c93bd965590c37ac02d35bcc57d1902467db7fc6eeec0a1fb97d0ce5bc96fae58e75239555e44a983239a61badba18e82d3b8 +"@remix-run/router@npm:1.20.0": + version: 1.20.0 + resolution: "@remix-run/router@npm:1.20.0" + checksum: 10c0/2e017dea530717a6e93a16d478714c4c9165313a1c48e39172ec609bc20324ca6362e8ee2243602df6343644c9268d82a3f50f154d3bb8a17dddde6c37be6e83 languageName: node linkType: hard @@ -2119,32 +2082,32 @@ __metadata: languageName: node linkType: hard -"@tanstack/eslint-plugin-query@npm:^5.58.1": - version: 5.58.1 - resolution: "@tanstack/eslint-plugin-query@npm:5.58.1" +"@tanstack/eslint-plugin-query@npm:^5.59.7": + version: 5.59.7 + resolution: "@tanstack/eslint-plugin-query@npm:5.59.7" dependencies: "@typescript-eslint/utils": "npm:^8.3.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 - checksum: 10c0/81300bf159aaafe778d61dd06e294cd6b4121d1107bb5691f6ea8e4551c3dc7c371064fa220c2895f658a9f69b5951db1ebb24a6f4806f5fd6eefa53f80fb8bd + checksum: 10c0/83ab51531b613d5f1015e1d0483b05fb27c654a8b6f40ed5dd1a6dcc48b221dd987c0bb9d9c64b6dd4632819599d93d5a85b431fc91d7846fdb1483551bda460 languageName: node linkType: hard -"@tanstack/query-core@npm:5.56.2": - version: 5.56.2 - resolution: "@tanstack/query-core@npm:5.56.2" - checksum: 10c0/54ff55f02b01f6ba089f4965bfd46f430c18ce7e11d874de04c4d58cc8f698598b41e1c017ba029d08ae75e321e546b26f1ea7f788474db265eeba46e780f2f6 +"@tanstack/query-core@npm:5.59.16": + version: 5.59.16 + resolution: "@tanstack/query-core@npm:5.59.16" + checksum: 10c0/487a1ac0df5e02ca4ea5bf3b9ee0010ac7fe0856a36fa7bd10947598d3f0ba356c91aa21b123729b26f2116f05b8b69cd8fb17681c24cdd586de17b6fe021521 languageName: node linkType: hard -"@tanstack/react-query@npm:^5.56.2": - version: 5.56.2 - resolution: "@tanstack/react-query@npm:5.56.2" +"@tanstack/react-query@npm:^5.59.16": + version: 5.59.16 + resolution: "@tanstack/react-query@npm:5.59.16" dependencies: - "@tanstack/query-core": "npm:5.56.2" + "@tanstack/query-core": "npm:5.59.16" peerDependencies: react: ^18 || ^19 - checksum: 10c0/6e883b4ca1948f990215b7bce194251faf13a79c6ecf3f3c660af6c6788ed113ab629cefdafb496dfb04866f12dd48d7314e936b75c881b6749127b6496ac8fd + checksum: 10c0/82acf170d2d169ad18081e4dc52bdcec3b92bd1b134bb704ab6949937a59fd4d7d4ddb1172dd7fdbd1667a2d2e0ffa118e68bde5f53972649b8e55bd0e87244c languageName: node linkType: hard @@ -2224,6 +2187,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^22.8.1": + version: 22.8.1 + resolution: "@types/node@npm:22.8.1" + dependencies: + undici-types: "npm:~6.19.8" + checksum: 10c0/83550fdf72a7db5b55eceac3f4fb038844eaee20202bdd2297a8248370cfa08317bda1605b781a8043eda4f173b75e73632e652fc85509eb14dfef78fa17337f + languageName: node + linkType: hard + "@types/prop-types@npm:*": version: 15.7.12 resolution: "@types/prop-types@npm:15.7.12" @@ -2231,16 +2203,16 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.3.0": - version: 18.3.0 - resolution: "@types/react-dom@npm:18.3.0" +"@types/react-dom@npm:^18.3.1": + version: 18.3.1 + resolution: "@types/react-dom@npm:18.3.1" dependencies: "@types/react": "npm:*" - checksum: 10c0/6c90d2ed72c5a0e440d2c75d99287e4b5df3e7b011838cdc03ae5cd518ab52164d86990e73246b9d812eaf02ec351d74e3b4f5bd325bf341e13bf980392fd53b + checksum: 10c0/8b416551c60bb6bd8ec10e198c957910cfb271bc3922463040b0d57cf4739cdcd24b13224f8d68f10318926e1ec3cd69af0af79f0291b599a992f8c80d47f1eb languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:^18.3.3": +"@types/react@npm:*": version: 18.3.5 resolution: "@types/react@npm:18.3.5" dependencies: @@ -2250,6 +2222,16 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^18.3.12": + version: 18.3.12 + resolution: "@types/react@npm:18.3.12" + dependencies: + "@types/prop-types": "npm:*" + csstype: "npm:^3.0.2" + checksum: 10c0/8bae8d9a41619804561574792e29112b413044eb0d53746dde2b9720c1f9a59f71c895bbd7987cd8ce9500b00786e53bc032dced38cddf42910458e145675290 + languageName: node + linkType: hard + "@types/resolve@npm:1.20.2": version: 1.20.2 resolution: "@types/resolve@npm:1.20.2" @@ -2264,15 +2246,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^8.7.0": - version: 8.7.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.7.0" +"@typescript-eslint/eslint-plugin@npm:^8.11.0": + version: 8.11.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.11.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.7.0" - "@typescript-eslint/type-utils": "npm:8.7.0" - "@typescript-eslint/utils": "npm:8.7.0" - "@typescript-eslint/visitor-keys": "npm:8.7.0" + "@typescript-eslint/scope-manager": "npm:8.11.0" + "@typescript-eslint/type-utils": "npm:8.11.0" + "@typescript-eslint/utils": "npm:8.11.0" + "@typescript-eslint/visitor-keys": "npm:8.11.0" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -2283,25 +2265,35 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/f04d6fa6a30e32d51feba0f08789f75ca77b6b67cfe494bdbd9aafa241871edc918fa8b344dc9d13dd59ae055d42c3920f0e542534f929afbfdca653dae598fa + checksum: 10c0/be509f7bb0c0c596801059b06995a81a1c326cc6ac31d96a32f7b6b7d7b495f9bad4dc442aa6e923d22515e62c668d3c14695c68bd6e0be1d4bf72158b7fd2d6 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^8.7.0": - version: 8.7.0 - resolution: "@typescript-eslint/parser@npm:8.7.0" +"@typescript-eslint/parser@npm:^8.11.0": + version: 8.11.0 + resolution: "@typescript-eslint/parser@npm:8.11.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.7.0" - "@typescript-eslint/types": "npm:8.7.0" - "@typescript-eslint/typescript-estree": "npm:8.7.0" - "@typescript-eslint/visitor-keys": "npm:8.7.0" + "@typescript-eslint/scope-manager": "npm:8.11.0" + "@typescript-eslint/types": "npm:8.11.0" + "@typescript-eslint/typescript-estree": "npm:8.11.0" + "@typescript-eslint/visitor-keys": "npm:8.11.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/1d5020ff1f5d3eb726bc6034d23f0a71e8fe7a713756479a0a0b639215326f71c0b44e2c25cc290b4e7c144bd3c958f1405199711c41601f0ea9174068714a64 + checksum: 10c0/e83f239fec60697083e5dcb1c8948340e783ea6e043fe9a65d557faef8882963b09d69aacd736eb8ab18a768769a7bbfc3de0f1251d4bba080613541acb0741c + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:8.11.0": + version: 8.11.0 + resolution: "@typescript-eslint/scope-manager@npm:8.11.0" + dependencies: + "@typescript-eslint/types": "npm:8.11.0" + "@typescript-eslint/visitor-keys": "npm:8.11.0" + checksum: 10c0/0910da62d8ae261711dd9f89d5c7d8e96ff13c50054436256e5a661309229cb49e3b8189c9468d36b6c4d3f7cddd121519ea78f9b18c9b869a808834b079b2ea languageName: node linkType: hard @@ -2315,18 +2307,25 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.7.0": - version: 8.7.0 - resolution: "@typescript-eslint/type-utils@npm:8.7.0" +"@typescript-eslint/type-utils@npm:8.11.0": + version: 8.11.0 + resolution: "@typescript-eslint/type-utils@npm:8.11.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.7.0" - "@typescript-eslint/utils": "npm:8.7.0" + "@typescript-eslint/typescript-estree": "npm:8.11.0" + "@typescript-eslint/utils": "npm:8.11.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.3.0" peerDependenciesMeta: typescript: optional: true - checksum: 10c0/2bd9fb93a50ff1c060af41528e39c775ae93b09dd71450defdb42a13c68990dd388460ae4e81fb2f4a49c38dc12152c515d43e845eca6198c44b14aab66733bc + checksum: 10c0/b69e31c1599ceeb20c29052a4ddb33a554174a3a4c55ee37d90c9b8250af6ef978a0b9ddbeefef4e83d62c4caea1bfa2d8088527f397bde69fb4ab9b360d794a + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:8.11.0": + version: 8.11.0 + resolution: "@typescript-eslint/types@npm:8.11.0" + checksum: 10c0/5ccdd3eeee077a6fc8e7f4bc0e0cbc9327b1205a845253ec5c0c6c49ff915e853161df00c24a0ffb4b8ec745d3f153dd0e066400a021c844c026e31121f46699 languageName: node linkType: hard @@ -2337,6 +2336,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.11.0": + version: 8.11.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.11.0" + dependencies: + "@typescript-eslint/types": "npm:8.11.0" + "@typescript-eslint/visitor-keys": "npm:8.11.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^1.3.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/b629ad3cd32b005d5c1d67c36958a418f8672efebea869399834f4f201ebf90b942165eebb5c9d9799dcabdc2cc26e5fabb00629f76b158847f42e1a491a75a6 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:8.7.0": version: 8.7.0 resolution: "@typescript-eslint/typescript-estree@npm:8.7.0" @@ -2356,7 +2374,21 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.7.0, @typescript-eslint/utils@npm:^8.3.0": +"@typescript-eslint/utils@npm:8.11.0": + version: 8.11.0 + resolution: "@typescript-eslint/utils@npm:8.11.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@typescript-eslint/scope-manager": "npm:8.11.0" + "@typescript-eslint/types": "npm:8.11.0" + "@typescript-eslint/typescript-estree": "npm:8.11.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + checksum: 10c0/bb5bcc8d928a55b22298e76f834ea6a9fe125a9ffeb6ac23bee0258b3ed32f41e281888a3d0be226a05e1011bb3b70e42a71a40366acdefea6779131c46bc522 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:^8.3.0": version: 8.7.0 resolution: "@typescript-eslint/utils@npm:8.7.0" dependencies: @@ -2370,6 +2402,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.11.0": + version: 8.11.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.11.0" + dependencies: + "@typescript-eslint/types": "npm:8.11.0" + eslint-visitor-keys: "npm:^3.4.3" + checksum: 10c0/7a5a49609fdc47e114fe59eee56393c90b122ec8e9520f90b0c5e189635ae1ccfa8e00108f641342c2c8f4637fe9d40c77927cf7c8248a3a660812cb4b7d0c08 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:8.7.0": version: 8.7.0 resolution: "@typescript-eslint/visitor-keys@npm:8.7.0" @@ -2380,18 +2422,18 @@ __metadata: languageName: node linkType: hard -"@vitejs/plugin-react@npm:^4.3.1": - version: 4.3.1 - resolution: "@vitejs/plugin-react@npm:4.3.1" +"@vitejs/plugin-react@npm:^4.3.3": + version: 4.3.3 + resolution: "@vitejs/plugin-react@npm:4.3.3" dependencies: - "@babel/core": "npm:^7.24.5" - "@babel/plugin-transform-react-jsx-self": "npm:^7.24.5" - "@babel/plugin-transform-react-jsx-source": "npm:^7.24.1" + "@babel/core": "npm:^7.25.2" + "@babel/plugin-transform-react-jsx-self": "npm:^7.24.7" + "@babel/plugin-transform-react-jsx-source": "npm:^7.24.7" "@types/babel__core": "npm:^7.20.5" react-refresh: "npm:^0.14.2" peerDependencies: vite: ^4.2.0 || ^5.0.0 - checksum: 10c0/39a027feddfd6b3e307121d79631462ef1aae05714ba7a2f9a73d240d0f89c2bf281132568eb27b55d6ddaf08d86ad1bd8b0066090240e570de8c6320eb9a903 + checksum: 10c0/b370c25fb47bb96f0cc51f3aadbbcfce54e40f95a4de67cf932e5ec526f139648da703725c6ea2c71a1b525eb3dd6e3e8ff877da143627cd2575de5ec4f00aa3 languageName: node linkType: hard @@ -2791,7 +2833,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.1, browserslist@npm:^4.23.3": +"browserslist@npm:^4.23.3": version: 4.23.3 resolution: "browserslist@npm:4.23.3" dependencies: @@ -2914,36 +2956,37 @@ __metadata: dependencies: "@simplewebauthn/browser": "npm:^11.0.0" "@simplewebauthn/types": "npm:^11.0.0" - "@tanstack/eslint-plugin-query": "npm:^5.58.1" - "@tanstack/react-query": "npm:^5.56.2" - "@types/react": "npm:^18.3.3" - "@types/react-dom": "npm:^18.3.0" - "@typescript-eslint/eslint-plugin": "npm:^8.7.0" - "@typescript-eslint/parser": "npm:^8.7.0" - "@vitejs/plugin-react": "npm:^4.3.1" + "@tanstack/eslint-plugin-query": "npm:^5.59.7" + "@tanstack/react-query": "npm:^5.59.16" + "@types/node": "npm:^22.8.1" + "@types/react": "npm:^18.3.12" + "@types/react-dom": "npm:^18.3.1" + "@typescript-eslint/eslint-plugin": "npm:^8.11.0" + "@typescript-eslint/parser": "npm:^8.11.0" + "@vitejs/plugin-react": "npm:^4.3.3" autoprefixer: "npm:^10.4.20" axios: "npm:^1.7.7" date-fns: "npm:^3.6.0" - eslint: "npm:^9.11.1" + eslint: "npm:^9.13.0" eslint-config-airbnb: "npm:^19.0.4" eslint-config-prettier: "npm:^9.1.0" - eslint-plugin-import: "npm:^2.30.0" + eslint-plugin-import: "npm:^2.31.0" eslint-plugin-prettier: "npm:^5.2.1" - eslint-plugin-react: "npm:^7.37.0" - eslint-plugin-react-hooks: "npm:^4.6.2" - eslint-plugin-react-refresh: "npm:^0.4.12" - globals: "npm:^15.9.0" - i18next: "npm:^23.14.0" + eslint-plugin-react: "npm:^7.37.2" + eslint-plugin-react-hooks: "npm:^5.0.0" + eslint-plugin-react-refresh: "npm:^0.4.14" + globals: "npm:^15.11.0" + i18next: "npm:^23.16.4" i18next-browser-languagedetector: "npm:^8.0.0" - postcss: "npm:^8.4.42" + postcss: "npm:^8.4.47" prettier: "npm:^3.3.3" react: "npm:^18.3.1" react-dom: "npm:^18.3.1" - react-i18next: "npm:^15.0.1" - react-router-dom: "npm:^6.26.1" - tailwindcss: "npm:^3.4.10" - typescript: "npm:^5.5.3" - vite: "npm:^5.4.1" + react-i18next: "npm:^15.1.0" + react-router-dom: "npm:^6.27.0" + tailwindcss: "npm:^3.4.14" + typescript: "npm:^5.6.3" + vite: "npm:^5.4.10" vite-plugin-mkcert: "npm:^1.17.6" vite-plugin-pwa: "npm:^0.20.5" zod: "npm:^3.23.8" @@ -3394,9 +3437,9 @@ __metadata: languageName: node linkType: hard -"es-iterator-helpers@npm:^1.0.19": - version: 1.0.19 - resolution: "es-iterator-helpers@npm:1.0.19" +"es-iterator-helpers@npm:^1.1.0": + version: 1.1.0 + resolution: "es-iterator-helpers@npm:1.1.0" dependencies: call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" @@ -3405,14 +3448,14 @@ __metadata: es-set-tostringtag: "npm:^2.0.3" function-bind: "npm:^1.1.2" get-intrinsic: "npm:^1.2.4" - globalthis: "npm:^1.0.3" + globalthis: "npm:^1.0.4" 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.2" + iterator.prototype: "npm:^1.1.3" safe-array-concat: "npm:^1.1.2" - checksum: 10c0/ae8f0241e383b3d197383b9842c48def7fce0255fb6ed049311b686ce295595d9e389b466f6a1b7d4e7bb92d82f5e716d6fae55e20c1040249bf976743b038c5 + checksum: 10c0/84d6c240c7da6e62323b336cb1497781546dab16bebdbd879ccfdf588979712d3e941d41165b6c2ffce5a03a7b929d4e6131d3124d330da1a0e2bfa1da7cd99f languageName: node linkType: hard @@ -3611,7 +3654,7 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.9.0": +"eslint-module-utils@npm:^2.12.0": version: 2.12.0 resolution: "eslint-module-utils@npm:2.12.0" dependencies: @@ -3623,9 +3666,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-import@npm:^2.30.0": - version: 2.30.0 - resolution: "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: "@rtsao/scc": "npm:^1.1.0" array-includes: "npm:^3.1.8" @@ -3635,7 +3678,7 @@ __metadata: debug: "npm:^3.2.7" doctrine: "npm:^2.1.0" eslint-import-resolver-node: "npm:^0.3.9" - eslint-module-utils: "npm:^2.9.0" + eslint-module-utils: "npm:^2.12.0" hasown: "npm:^2.0.2" is-core-module: "npm:^2.15.1" is-glob: "npm:^4.0.3" @@ -3644,10 +3687,11 @@ __metadata: object.groupby: "npm:^1.0.3" object.values: "npm:^1.2.0" semver: "npm:^6.3.1" + string.prototype.trimend: "npm:^1.0.8" tsconfig-paths: "npm:^3.15.0" peerDependencies: - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: 10c0/4c9dcb1f27505c4d5dd891d2b551f56c70786d136aa3992a77e785bdc67c9f60200a2c7fb0ce55b7647fe550b12bc433d5dfa59e2c00ab44227791c5ab86badf + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + checksum: 10c0/e21d116ddd1900e091ad120b3eb68c5dd5437fe2c930f1211781cd38b246f090a6b74d5f3800b8255a0ed29782591521ad44eb21c5534960a8f1fb4040fd913a languageName: node linkType: hard @@ -3671,34 +3715,34 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react-hooks@npm:^4.6.2": - version: 4.6.2 - resolution: "eslint-plugin-react-hooks@npm:4.6.2" +"eslint-plugin-react-hooks@npm:^5.0.0": + version: 5.0.0 + resolution: "eslint-plugin-react-hooks@npm:5.0.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/bcb74b421f32e4203a7100405b57aab85526be4461e5a1da01bc537969a30012d2ee209a2c2a6cac543833a27188ce1e6ad71e4628d0bb4a2e5365cad86c5002 languageName: node linkType: hard -"eslint-plugin-react-refresh@npm:^0.4.12": - version: 0.4.12 - resolution: "eslint-plugin-react-refresh@npm:0.4.12" +"eslint-plugin-react-refresh@npm:^0.4.14": + version: 0.4.14 + resolution: "eslint-plugin-react-refresh@npm:0.4.14" peerDependencies: eslint: ">=7" - checksum: 10c0/33dd82450f7c5fa884c5c84ffaf9d9a8b363bc155432807dc09904c7db6ba724888fac4562b058268259aa7c9270b622ef411488011b3469a2add275ed5c2273 + checksum: 10c0/427108008ffcc2e0be36897398e61a2fae54c5bf092af0171bc4cf1927080d40619bb07be02ecd7c515372210228cf849023997cfa0252d37115f9b0c0debcd2 languageName: node linkType: hard -"eslint-plugin-react@npm:^7.37.0": - version: 7.37.0 - resolution: "eslint-plugin-react@npm:7.37.0" +"eslint-plugin-react@npm:^7.37.2": + version: 7.37.2 + resolution: "eslint-plugin-react@npm:7.37.2" dependencies: array-includes: "npm:^3.1.8" array.prototype.findlast: "npm:^1.2.5" array.prototype.flatmap: "npm:^1.3.2" array.prototype.tosorted: "npm:^1.1.4" doctrine: "npm:^2.1.0" - es-iterator-helpers: "npm:^1.0.19" + es-iterator-helpers: "npm:^1.1.0" estraverse: "npm:^5.3.0" hasown: "npm:^2.0.2" jsx-ast-utils: "npm:^2.4.1 || ^3.0.0" @@ -3713,17 +3757,17 @@ __metadata: string.prototype.repeat: "npm:^1.0.0" peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - checksum: 10c0/c9b02cd425c2a7c36b07fe223e405ae235500ac98f04898529cd8842c78fdd5b54f7c29189db5d6eab390ac5ac531af6e7b94a1f3fe9ad1e6564f7d78fd0eb6b + checksum: 10c0/01c498f263c201698bf653973760f86a07fa0cdec56c044f3eaa5ddaae71c64326015dfa5fde76ca8c5386ffe789fc79932624b614e13b6a1ad789fee3f7c491 languageName: node linkType: hard -"eslint-scope@npm:^8.0.2": - version: 8.0.2 - resolution: "eslint-scope@npm:8.0.2" +"eslint-scope@npm:^8.1.0": + version: 8.1.0 + resolution: "eslint-scope@npm:8.1.0" dependencies: esrecurse: "npm:^4.3.0" estraverse: "npm:^5.2.0" - checksum: 10c0/477f820647c8755229da913025b4567347fd1f0bf7cbdf3a256efff26a7e2e130433df052bd9e3d014025423dc00489bea47eb341002b15553673379c1a7dc36 + checksum: 10c0/ae1df7accae9ea90465c2ded70f7064d6d1f2962ef4cc87398855c4f0b3a5ab01063e0258d954bb94b184f6759febe04c3118195cab5c51978a7229948ba2875 languageName: node linkType: hard @@ -3741,20 +3785,27 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^9.11.1": - version: 9.11.1 - resolution: "eslint@npm:9.11.1" +"eslint-visitor-keys@npm:^4.1.0": + version: 4.1.0 + resolution: "eslint-visitor-keys@npm:4.1.0" + checksum: 10c0/5483ef114c93a136aa234140d7aa3bd259488dae866d35cb0d0b52e6a158f614760a57256ac8d549acc590a87042cb40f6951815caa821e55dc4fd6ef4c722eb + languageName: node + linkType: hard + +"eslint@npm:^9.13.0": + version: 9.13.0 + resolution: "eslint@npm:9.13.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.11.0" "@eslint/config-array": "npm:^0.18.0" - "@eslint/core": "npm:^0.6.0" + "@eslint/core": "npm:^0.7.0" "@eslint/eslintrc": "npm:^3.1.0" - "@eslint/js": "npm:9.11.1" + "@eslint/js": "npm:9.13.0" "@eslint/plugin-kit": "npm:^0.2.0" + "@humanfs/node": "npm:^0.16.5" "@humanwhocodes/module-importer": "npm:^1.0.1" - "@humanwhocodes/retry": "npm:^0.3.0" - "@nodelib/fs.walk": "npm:^1.2.8" + "@humanwhocodes/retry": "npm:^0.3.1" "@types/estree": "npm:^1.0.6" "@types/json-schema": "npm:^7.0.15" ajv: "npm:^6.12.4" @@ -3762,9 +3813,9 @@ __metadata: cross-spawn: "npm:^7.0.2" debug: "npm:^4.3.2" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.0.2" - eslint-visitor-keys: "npm:^4.0.0" - espree: "npm:^10.1.0" + eslint-scope: "npm:^8.1.0" + eslint-visitor-keys: "npm:^4.1.0" + espree: "npm:^10.2.0" esquery: "npm:^1.5.0" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" @@ -3774,13 +3825,11 @@ __metadata: ignore: "npm:^5.2.0" imurmurhash: "npm:^0.1.4" is-glob: "npm:^4.0.0" - is-path-inside: "npm:^3.0.3" json-stable-stringify-without-jsonify: "npm:^1.0.1" lodash.merge: "npm:^4.6.2" minimatch: "npm:^3.1.2" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" - strip-ansi: "npm:^6.0.1" text-table: "npm:^0.2.0" peerDependencies: jiti: "*" @@ -3789,11 +3838,11 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/fc9afc31155fef8c27fc4fd00669aeafa4b89ce5abfbf6f60e05482c03d7ff1d5e7546e416aa47bf0f28c9a56597a94663fd0264c2c42a1890f53cac49189f24 + checksum: 10c0/d3577444152182a9d8ea8c6a6acb073d3a2773ad73a6b646f432746583ec4bfcd6a44fcc2e37d05d276984e583c46c2d289b3b981ca8f8b4052756a152341d19 languageName: node linkType: hard -"espree@npm:^10.0.1, espree@npm:^10.1.0": +"espree@npm:^10.0.1": version: 10.1.0 resolution: "espree@npm:10.1.0" dependencies: @@ -3804,6 +3853,17 @@ __metadata: languageName: node linkType: hard +"espree@npm:^10.2.0": + version: 10.2.0 + resolution: "espree@npm:10.2.0" + dependencies: + acorn: "npm:^8.12.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^4.1.0" + checksum: 10c0/2b6bfb683e7e5ab2e9513949879140898d80a2d9867ea1db6ff5b0256df81722633b60a7523a7c614f05a39aeea159dd09ad2a0e90c0e218732fc016f9086215 + languageName: node + linkType: hard + "esquery@npm:^1.5.0": version: 1.6.0 resolution: "esquery@npm:1.6.0" @@ -4209,14 +4269,14 @@ __metadata: languageName: node linkType: hard -"globals@npm:^15.9.0": - version: 15.9.0 - resolution: "globals@npm:15.9.0" - checksum: 10c0/de4b553e412e7e830998578d51b605c492256fb2a9273eaeec6ec9ee519f1c5aa50de57e3979911607fd7593a4066420e01d8c3d551e7a6a236e96c521aee36c +"globals@npm:^15.11.0": + version: 15.11.0 + resolution: "globals@npm:15.11.0" + checksum: 10c0/861e39bb6bd9bd1b9f355c25c962e5eb4b3f0e1567cf60fa6c06e8c502b0ec8706b1cce055d69d84d0b7b8e028bec5418cf629a54e7047e116538d1c1c1a375c languageName: node linkType: hard -"globalthis@npm:^1.0.3": +"globalthis@npm:^1.0.3, globalthis@npm:^1.0.4": version: 1.0.4 resolution: "globalthis@npm:1.0.4" dependencies: @@ -4356,12 +4416,12 @@ __metadata: languageName: node linkType: hard -"i18next@npm:^23.14.0": - version: 23.14.0 - resolution: "i18next@npm:23.14.0" +"i18next@npm:^23.16.4": + version: 23.16.4 + resolution: "i18next@npm:23.16.4" dependencies: "@babel/runtime": "npm:^7.23.2" - checksum: 10c0/c26638a816e6213e515c158b17894ca3c54693911e1dc637cd279e11d53f8ded2494c3dcc6e2e94c639987ef63718193a1e2b4f3d5873fbcf89f266582140cd5 + checksum: 10c0/b016d98bb8a11fa16e2598897b0ea388b664e28e38acbd7e6580ce8ff300a9b4c66eb849883b1c36dbee9800abb830887293adbb1e598b679ea29006a3bc6af9 languageName: node linkType: hard @@ -4623,13 +4683,6 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.3": - version: 3.0.3 - resolution: "is-path-inside@npm:3.0.3" - checksum: 10c0/cf7d4ac35fb96bab6a1d2c3598fe5ebb29aafb52c0aaa482b5a3ed9d8ba3edc11631e3ec2637660c44b3ce0e61a08d54946e8af30dec0b60a7c27296c68ffd05 - languageName: node - linkType: hard - "is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -4744,16 +4797,16 @@ __metadata: languageName: node linkType: hard -"iterator.prototype@npm:^1.1.2": - version: 1.1.2 - resolution: "iterator.prototype@npm:1.1.2" +"iterator.prototype@npm:^1.1.3": + version: 1.1.3 + resolution: "iterator.prototype@npm:1.1.3" dependencies: define-properties: "npm:^1.2.1" get-intrinsic: "npm:^1.2.1" has-symbols: "npm:^1.0.3" reflect.getprototypeof: "npm:^1.0.4" set-function-name: "npm:^2.0.1" - checksum: 10c0/a32151326095e916f306990d909f6bbf23e3221999a18ba686419535dcd1749b10ded505e89334b77dc4c7a58a8508978f0eb16c2c8573e6d412eb7eb894ea79 + checksum: 10c0/68b0320c14291fbb3d8ed5a17e255d3127e7971bec19108076667e79c9ff4c7d69f99de4b0b3075c789c3f318366d7a0a35bb086eae0f2cf832dd58465b2f9e6 languageName: node linkType: hard @@ -4818,15 +4871,6 @@ __metadata: languageName: node linkType: hard -"jsesc@npm:^2.5.1": - version: 2.5.2 - resolution: "jsesc@npm:2.5.2" - bin: - jsesc: bin/jsesc - checksum: 10c0/dbf59312e0ebf2b4405ef413ec2b25abb5f8f4d9bc5fb8d9f90381622ebca5f2af6a6aa9a8578f65903f9e33990a6dc798edd0ce5586894bf0e9e31803a1de88 - languageName: node - linkType: hard - "jsesc@npm:^3.0.2, jsesc@npm:~3.0.2": version: 3.0.2 resolution: "jsesc@npm:3.0.2" @@ -5631,7 +5675,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.23, postcss@npm:^8.4.41, postcss@npm:^8.4.42": +"postcss@npm:^8.4.23": version: 8.4.42 resolution: "postcss@npm:8.4.42" dependencies: @@ -5642,6 +5686,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.43, postcss@npm:^8.4.47": + 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 + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -5751,11 +5806,11 @@ __metadata: languageName: node linkType: hard -"react-i18next@npm:^15.0.1": - version: 15.0.1 - resolution: "react-i18next@npm:15.0.1" +"react-i18next@npm:^15.1.0": + version: 15.1.0 + resolution: "react-i18next@npm:15.1.0" dependencies: - "@babel/runtime": "npm:^7.24.8" + "@babel/runtime": "npm:^7.25.0" html-parse-stringify: "npm:^3.0.1" peerDependencies: i18next: ">= 23.2.3" @@ -5765,7 +5820,7 @@ __metadata: optional: true react-native: optional: true - checksum: 10c0/74ed8903cb0dbc4d05f8eef176eba2d60ffad65c48bf6695e8294d70dd035563ebdb52b012319ef2835010fe4ccfc95a3e431d5b22d2b0a264141299aa6beb30 + checksum: 10c0/a812a8345222e4c0b9423cec6bf0997abf5b72802d29c4b5c91c3a8ba0be87f536f5968456d6e3b6526e978ea5db7addde24d5855dd98a1d4ca7eb9aed8a9b20 languageName: node linkType: hard @@ -5783,27 +5838,27 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:^6.26.1": - version: 6.26.1 - resolution: "react-router-dom@npm:6.26.1" +"react-router-dom@npm:^6.27.0": + version: 6.27.0 + resolution: "react-router-dom@npm:6.27.0" dependencies: - "@remix-run/router": "npm:1.19.1" - react-router: "npm:6.26.1" + "@remix-run/router": "npm:1.20.0" + react-router: "npm:6.27.0" peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: 10c0/9d9d8ed54d1c95497c6fa35a6ab46992efeccf1cfc6f0f6089c6c9b040af3eae09568fbb80c690bae08051a955d92d7aa3a0e730f626eb69285114993d31d430 + checksum: 10c0/7db48ffd0b387af0eed060ceaf42075d074e63fbd30f4cf60993526b3610883a9ff82615965001165ed69d2bf2f1bce05c594a21c8d0d845e7b9bf203201116e languageName: node linkType: hard -"react-router@npm:6.26.1": - version: 6.26.1 - resolution: "react-router@npm:6.26.1" +"react-router@npm:6.27.0": + version: 6.27.0 + resolution: "react-router@npm:6.27.0" dependencies: - "@remix-run/router": "npm:1.19.1" + "@remix-run/router": "npm:1.20.0" peerDependencies: react: ">=16.8" - checksum: 10c0/463078e740462b42bb5ba8004448f33fc9e63778f432a4ed55c57b93c5b519e25fb17913ee8435b0fda33c6b9f75df8ef6fcb2c3a4f8db84fb546d202e29aa51 + checksum: 10c0/440d6ee00890cec92a0c2183164149fbb96363efccf52bb132a964f44e51aec2f4b5a0520c67f6f17faddaa4097090fd76f7efe58263947532fceeb11dd4cdf3 languageName: node linkType: hard @@ -6267,6 +6322,13 @@ __metadata: languageName: node linkType: hard +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + "source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -6505,9 +6567,9 @@ __metadata: languageName: node linkType: hard -"tailwindcss@npm:^3.4.10": - version: 3.4.10 - resolution: "tailwindcss@npm:3.4.10" +"tailwindcss@npm:^3.4.14": + version: 3.4.14 + resolution: "tailwindcss@npm:3.4.14" dependencies: "@alloc/quick-lru": "npm:^5.2.0" arg: "npm:^5.0.2" @@ -6534,7 +6596,7 @@ __metadata: bin: tailwind: lib/cli.js tailwindcss: lib/cli.js - checksum: 10c0/5212f4d90f21fc21b87008570a37de46af9c955a7c197d0f0e1d888edd5ef597f1f41194bec6b1fdc8a21e9460089d370bf728ce39b13abe93324a768b7043fd + checksum: 10c0/f6c23f8a3293ce3b2511bca1e50008ac94bd8562cb09fec32fe4f8e8a4f54d9e9fc10e567b7f974abdd4b33e550564a2616d4e793c736955432f28448141ce45 languageName: node linkType: hard @@ -6748,23 +6810,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.5.3": - version: 5.5.4 - resolution: "typescript@npm:5.5.4" +"typescript@npm:^5.6.3": + version: 5.6.3 + resolution: "typescript@npm:5.6.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/422be60f89e661eab29ac488c974b6cc0a660fb2228003b297c3d10c32c90f3bcffc1009b43876a082515a3c376b1eefcce823d6e78982e6878408b9a923199c + checksum: 10c0/44f61d3fb15c35359bc60399cb8127c30bae554cd555b8e2b46d68fa79d680354b83320ad419ff1b81a0bdf324197b29affe6cc28988cd6a74d4ac60c94f9799 languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.5.3#optional!builtin": - version: 5.5.4 - resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=379a07" +"typescript@patch:typescript@npm%3A^5.6.3#optional!builtin": + version: 5.6.3 + resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=8c6c40" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/73409d7b9196a5a1217b3aaad929bf76294d3ce7d6e9766dd880ece296ee91cf7d7db6b16c6c6c630ee5096eccde726c0ef17c7dfa52b01a243e57ae1f09ef07 + checksum: 10c0/7c9d2e07c81226d60435939618c91ec2ff0b75fbfa106eec3430f0fcf93a584bc6c73176676f532d78c3594fe28a54b36eb40b3d75593071a7ec91301533ace7 languageName: node linkType: hard @@ -6780,6 +6842,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~6.19.8": + version: 6.19.8 + resolution: "undici-types@npm:6.19.8" + checksum: 10c0/078afa5990fba110f6824823ace86073b4638f1d5112ee26e790155f481f2a868cc3e0615505b6f4282bdf74a3d8caad715fd809e870c2bb0704e3ea6082f344 + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.1 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.1" @@ -6938,13 +7007,13 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.4.1": - version: 5.4.2 - resolution: "vite@npm:5.4.2" +"vite@npm:^5.4.10": + version: 5.4.10 + resolution: "vite@npm:5.4.10" dependencies: esbuild: "npm:^0.21.3" fsevents: "npm:~2.3.3" - postcss: "npm:^8.4.41" + postcss: "npm:^8.4.43" rollup: "npm:^4.20.0" peerDependencies: "@types/node": ^18.0.0 || >=20.0.0 @@ -6977,7 +7046,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/23e347ca8aa6f0a774227e4eb7abae228f12c6806a727b046aa75e7ee37ffc2d68cff74360e12a42c347f79adc294e2363bc723b957bf4b382b5a8fb39e4df9d + checksum: 10c0/4ef4807d2fd166a920de244dbcec791ba8a903b017a7d8e9f9b4ac40d23f8152c1100610583d08f542b47ca617a0505cfc5f8407377d610599d58296996691ed languageName: node linkType: hard