diff --git a/packages/pl-api/lib/client.ts b/packages/pl-api/lib/client.ts index 47b40adbf..0ad6a52d4 100644 --- a/packages/pl-api/lib/client.ts +++ b/packages/pl-api/lib/client.ts @@ -4398,7 +4398,7 @@ class PlApiClient { resolveReport: async (reportId: string, action_taken_comment?: string) => { let response; if (this.features.mastodonAdmin) { - response = await this.request(`/api/v1/admin/reports/${reportId}/resolve`, { method: 'POST', body: { action_taken_comment } }); + response = await this.request(`/api/v1/admin/reports/${reportId}/resolve`, { method: 'POST', body: { action_taken_comment } }); } else { response = await this.request(`/api/v1/pleroma/admin/reports/${reportId}`, { method: 'PATCH', diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json index 1380564fd..94c8f3a97 100644 --- a/packages/pl-fe/package.json +++ b/packages/pl-fe/package.json @@ -123,7 +123,6 @@ "react-intl": "^7.0.4", "react-motion": "^0.5.2", "react-redux": "^9.0.4", - "react-router-dom": "^5.3.4", "react-sparklines": "^1.7.0", "react-sticky-box": "^2.0.5", "react-swipeable-views": "^0.14.0", diff --git a/packages/pl-fe/src/actions/compose.ts b/packages/pl-fe/src/actions/compose.ts index 12698cbb7..31f488243 100644 --- a/packages/pl-fe/src/actions/compose.ts +++ b/packages/pl-fe/src/actions/compose.ts @@ -18,6 +18,7 @@ import { uploadFile, updateMedia } from './media'; import { saveSettings } from './settings'; import { createStatus } from './statuses'; +import type { LinkOptions } from '@tanstack/react-router'; import type { EditorState } from 'lexical'; import type { Account, CreateStatusParams, CustomEmoji, Group, MediaAttachment, Status as BaseStatus, Tag, Poll, ScheduledStatus, InteractionPolicy, UpdateMediaParams } from 'pl-api'; import type { AutoSuggestion } from 'pl-fe/components/autosuggest-input'; @@ -306,14 +307,17 @@ const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, c } if (data.scheduled_at === null) { + const linkOptions: LinkOptions = (data.visibility === 'direct' && getClient(getState()).features.conversations) + ? { to: '/conversations' } + : { to: '/@{$username}/posts/$statusId', params: { username: data.account.acct, statusId: data.id } }; toast.success(redact ? messages.redactSuccess : edit ? messages.editSuccess : messages.success, { actionLabel: messages.view, - actionLink: (data.visibility === 'direct' && getClient(getState()).features.conversations) ? '/conversations' : `/@${data.account.acct}/posts/${data.id}`, + actionLinkOptions: linkOptions, }); } else { toast.success(messages.scheduledSuccess, { actionLabel: messages.view, - actionLink: '/scheduled_statuses', + actionLinkOptions: { to: '/scheduled_statuses' }, }); } }; diff --git a/packages/pl-fe/src/actions/events.ts b/packages/pl-fe/src/actions/events.ts index 65e2ed8f9..fe8c531fe 100644 --- a/packages/pl-fe/src/actions/events.ts +++ b/packages/pl-fe/src/actions/events.ts @@ -76,7 +76,10 @@ const submitEvent = ({ statusId ? messages.editSuccess : messages.success, { actionLabel: messages.view, - actionLink: `/@${data.account.acct}/events/${data.id}`, + actionLinkOptions: { + to: '/@{$username}/events/$statusId', + params: { username: data.account.acct, statusId: data.id }, + }, }, ); diff --git a/packages/pl-fe/src/components/account.tsx b/packages/pl-fe/src/components/account.tsx index 4e2121475..607a3f076 100644 --- a/packages/pl-fe/src/components/account.tsx +++ b/packages/pl-fe/src/components/account.tsx @@ -220,7 +220,8 @@ const Account = ({ const LinkEl: any = withLinkToProfile ? Link : 'div'; const linkProps = withLinkToProfile ? { - to: `/@${account.acct}`, + to: '/@{$username}', + params: { username: account.acct }, title: account.acct, onClick: (event: React.MouseEvent) => event.stopPropagation(), } : {}; diff --git a/packages/pl-fe/src/components/ui/toast.tsx b/packages/pl-fe/src/components/ui/toast.tsx index dd5a1e77f..381cf8379 100644 --- a/packages/pl-fe/src/components/ui/toast.tsx +++ b/packages/pl-fe/src/components/ui/toast.tsx @@ -1,4 +1,4 @@ -import { Link } from '@tanstack/react-router'; +import { Link, type LinkOptions } from '@tanstack/react-router'; import clsx from 'clsx'; import React from 'react'; import toast, { Toast as RHToast } from 'react-hot-toast'; @@ -24,7 +24,7 @@ interface IToast { message: ToastText; type: ToastType; action?(): void; - actionLink?: string; + actionLinkOptions?: LinkOptions; actionLabel?: ToastText; summary?: string; } @@ -33,7 +33,7 @@ interface IToast { * Customizable Toasts for in-app notifications. */ const Toast = (props: IToast) => { - const { t, message, type, action, actionLink, actionLabel, summary } = props; + const { t, message, type, action, actionLinkOptions, actionLabel, summary } = props; const dismissToast = () => toast.dismiss(t.id); @@ -85,10 +85,10 @@ const Toast = (props: IToast) => { ); } - if (actionLink && actionLabel) { + if (actionLinkOptions && actionLabel) { return ( ({ id, shouldCondense, autoFocus, clickab toast.success(messages.draftSaved, { actionLabel: messages.view, - actionLink: '/draft_statuses', + actionLinkOptions: { to: '/draft_statuses' }, }); }; diff --git a/packages/pl-fe/src/features/ui/index.tsx b/packages/pl-fe/src/features/ui/index.tsx index f59f68947..8c8ca036b 100644 --- a/packages/pl-fe/src/features/ui/index.tsx +++ b/packages/pl-fe/src/features/ui/index.tsx @@ -2,7 +2,6 @@ import { Outlet, useNavigate } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { Suspense, useEffect, useRef } from 'react'; import { Toaster } from 'react-hot-toast'; -import { Redirect, Switch, useLocation } from 'react-router-dom'; import { fetchConfig } from 'pl-fe/actions/admin'; import { fetchFilters } from 'pl-fe/actions/filters'; @@ -21,8 +20,6 @@ import { useDraggedFiles } from 'pl-fe/hooks/use-dragged-files'; import { useFeatures } from 'pl-fe/hooks/use-features'; import { useInstance } from 'pl-fe/hooks/use-instance'; import { useOwnAccount } from 'pl-fe/hooks/use-own-account'; -import DefaultLayout from 'pl-fe/layouts/default-layout'; -import EmptyLayout from 'pl-fe/layouts/empty-layout'; import { prefetchFollowRequests } from 'pl-fe/queries/accounts/use-follow-requests'; import { queryClient } from 'pl-fe/queries/client'; import { prefetchCustomEmojis } from 'pl-fe/queries/instance/use-custom-emojis'; @@ -39,81 +36,14 @@ import { AccountHoverCard, ChatWidget, DropdownNavigation, - GenericNotFound, - Status, StatusHoverCard, } from './util/async-components'; import GlobalHotkeys from './util/global-hotkeys'; -import { WrappedRoute } from './util/react-router-helpers'; // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import 'pl-fe/components/status'; -interface ISwitchingColumnsArea { - children: React.ReactNode; -} - -const SwitchingColumnsArea: React.FC = React.memo(({ children }) => { - const { search } = useLocation(); - - // NOTE: Mastodon and Pleroma route some basenames to the backend. - // When adding new routes, use a basename that does NOT conflict - // with a known backend route, but DO redirect the backend route - // to the corresponding component as a fallback. - // Ex: use /login instead of /auth, but redirect /auth to /login - return ( - - {/* Mastodon web routes */} - - - - - - - - - {/* Pleroma FE web routes */} - - - - - - - - - - - - {/* Iceshrimp.NET web routes */} - - - {/* Mastodon rendered pages */} - - - - - - - - - - - - - - {/* Pleroma hard-coded email URLs */} - - - - - - - ); -}); - -SwitchingColumnsArea.displayName = '_'; - const UI: React.FC = React.memo(() => { const navigate = useNavigate(); const dispatch = useAppDispatch(); diff --git a/packages/pl-fe/src/features/ui/components/error-column.tsx b/packages/pl-fe/src/features/ui/router/error-column.tsx similarity index 88% rename from packages/pl-fe/src/features/ui/components/error-column.tsx rename to packages/pl-fe/src/features/ui/router/error-column.tsx index 718df0439..36595b7a7 100644 --- a/packages/pl-fe/src/features/ui/components/error-column.tsx +++ b/packages/pl-fe/src/features/ui/router/error-column.tsx @@ -7,22 +7,19 @@ import Stack from 'pl-fe/components/ui/stack'; import Text from 'pl-fe/components/ui/text'; import { isNetworkError } from 'pl-fe/utils/errors'; +import type { ErrorRouteComponent } from '@tanstack/react-router'; + const messages = defineMessages({ title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' }, body: { id: 'bundle_column_error.body', defaultMessage: 'Something went wrong while loading this page.' }, retry: { id: 'bundle_column_error.retry', defaultMessage: 'Try again' }, }); -interface IErrorColumn { - error: Error; - onRetry?: () => void; -} - -const ErrorColumn: React.FC = ({ error, onRetry = () => location.reload() }) => { +const ErrorColumn: ErrorRouteComponent = ({ error, reset }) => { const intl = useIntl(); const handleRetry = () => { - onRetry?.(); + reset(); }; if (!isNetworkError(error)) { diff --git a/packages/pl-fe/src/features/ui/router.tsx b/packages/pl-fe/src/features/ui/router/index.tsx similarity index 87% rename from packages/pl-fe/src/features/ui/router.tsx rename to packages/pl-fe/src/features/ui/router/index.tsx index 9298d98ae..3ee5e997b 100644 --- a/packages/pl-fe/src/features/ui/router.tsx +++ b/packages/pl-fe/src/features/ui/router/index.tsx @@ -38,12 +38,11 @@ import StatusLayout from 'pl-fe/layouts/status-layout'; import { instanceInitialState } from 'pl-fe/reducers/instance'; import { isStandalone } from 'pl-fe/utils/state'; -import ChatPageMain from '../chats/components/chat-page/components/chat-page-main'; -import ChatPageNew from '../chats/components/chat-page/components/chat-page-new'; -import ChatPageSettings from '../chats/components/chat-page/components/chat-page-settings'; -import ChatPageShoutbox from '../chats/components/chat-page/components/chat-page-shoutbox'; - -import ColumnLoading from './components/column-loading'; +import ChatPageMain from '../../chats/components/chat-page/components/chat-page-main'; +import ChatPageNew from '../../chats/components/chat-page/components/chat-page-new'; +import ChatPageSettings from '../../chats/components/chat-page/components/chat-page-settings'; +import ChatPageShoutbox from '../../chats/components/chat-page/components/chat-page-shoutbox'; +import ColumnLoading from '../components/column-loading'; import { AboutPage, AccountGallery, @@ -146,7 +145,9 @@ import { EditEvent, Reports, AwaitingApproval, -} from './util/async-components'; +} from '../util/async-components'; + +import ErrorColumn from './error-column'; import type { Features } from 'pl-api'; @@ -1184,6 +1185,82 @@ export const federationRestrictionsRoute = createRoute({ }, }); +// Redirect routes +const redirectTagRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/tag/$id', + component: () => { + const { id } = redirectTagRoute.useParams(); + return ; + }, +}); +const redirectNoticeStatusRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/notice/$statusId', + component: () => { + const { statusId } = redirectNoticeStatusRoute.useParams(); + return ; + }, +}); +const redirectPleromaStatusRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/users/@{$username}/statuses/$statusId', + component: () => { + const { username, statusId } = redirectPleromaStatusRoute.useParams(); + return ; + }, +}); +const redirectPleromaUsernameRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/users/@{$username}', + component: () => { + const { username } = redirectPleromaUsernameRoute.useParams(); + return ; + }, +}); +const redirectIceshrimpStatusRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/notes/$statusId', + component: () => { + const { statusId } = redirectIceshrimpStatusRoute.useParams(); + return ; + }, +}); +const redirectInviteRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/registration/$token', + component: () => { + const { token } = redirectInviteRoute.useParams(); + return ; + }, +}); +const redirectRoutes = [ + createRoute({ getParentRoute: () => rootRoute, path: '/timelines/home', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/timelines/public/local', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/timelines/public', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/timelines/direct', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/main/all', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/main/public', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/main/friends', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/user-settings', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/registration', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/admin', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/terms', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/settings/preferences', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/settings/two_factor_authentication_methods', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/settings/applications', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/auth/edit', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/auth/reset_password', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/auth/sign_in', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/auth/sign_out', component: () => }), + createRoute({ getParentRoute: () => rootRoute, path: '/auth/password/new', component: () => }), + redirectTagRoute, + redirectNoticeStatusRoute, + redirectPleromaStatusRoute, + redirectPleromaUsernameRoute, + redirectIceshrimpStatusRoute, +]; + const routeTree = rootRoute.addChildren([ layouts.admin.addChildren([ adminDashboardRoute, @@ -1314,6 +1391,7 @@ const routeTree = rootRoute.addChildren([ statusRoute, statusQuotesRoute, ]), + ...redirectRoutes, ]); const FallbackLayout: React.FC<{ children: JSX.Element }> = ({ children }) => ( @@ -1344,6 +1422,7 @@ const router = createRouter({ }, defaultNotFoundComponent: GenericNotFound, defaultPendingComponent: PendingComponent, + defaultErrorComponent: ErrorColumn, scrollRestoration: true, pathParamsAllowedCharacters: ['@'], }); diff --git a/packages/pl-fe/src/features/ui/util/react-router-helpers.tsx b/packages/pl-fe/src/features/ui/util/react-router-helpers.tsx deleted file mode 100644 index 2aedca14b..000000000 --- a/packages/pl-fe/src/features/ui/util/react-router-helpers.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { Outlet } from '@tanstack/react-router'; -import React, { Suspense, useEffect, useRef } from 'react'; -import { ErrorBoundary, type FallbackProps } from 'react-error-boundary'; -import { Redirect, Route, useHistory, RouteProps, RouteComponentProps, match as MatchType, useLocation } from 'react-router-dom'; - -import Layout from 'pl-fe/components/ui/layout'; -import { useOwnAccount } from 'pl-fe/hooks/use-own-account'; - -import ColumnForbidden from '../components/column-forbidden'; -import ColumnLoading from '../components/column-loading'; -import ErrorColumn from '../components/error-column'; - -type LayoutProps = { - params?: MatchType['params']; - children: React.ReactNode; -}; - -interface IWrappedRoute extends RouteProps { - component: React.ExoticComponent; - layout: React.ComponentType; - content?: React.ReactNode; - componentParams?: Record; - publicRoute?: boolean; - staffOnly?: boolean; - adminOnly?: boolean; -} - -const WrappedRoute: React.FC = ({ - component: Component, - layout: Layout, - content, - componentParams = {}, - publicRoute = false, - staffOnly = false, - adminOnly = false, - ...rest -}) => { - const history = useHistory(); - - const { account } = useOwnAccount(); - - const renderComponent = ({ match }: RouteComponentProps) => ( - - }> - - - {content} - - - - - ); - - const loginRedirect = () => { - const actualUrl = encodeURIComponent(`${history.location.pathname}${history.location.search}`); - localStorage.setItem('plfe:redirect_uri', actualUrl); - return ; - }; - - const authorized = [ - account || publicRoute, - staffOnly ? account && (account.is_admin || account.is_moderator) : true, - adminOnly ? account && account.is_admin : true, - ].every(c => c); - - if (!authorized) { - if (!account) { - return loginRedirect(); - } else { - return ; - } - } - - return ; -}; - -interface IFallbackLayout { - children: JSX.Element; -} - -const FallbackLayout: React.FC = () => ( - <> - - - - - - -); - -const FallbackLoading: React.FC = () => ( - - - -); - -const FallbackForbidden: React.FC = () => ( - - - -); - -const FallbackError: React.FC = ({ error, resetErrorBoundary }) => { - const location = useLocation(); - const firstUpdate = useRef(true); - - useEffect(() => { - if (firstUpdate.current) { - firstUpdate.current = false; - } else { - resetErrorBoundary(); - } - }, [location]); - - return ( - - - - ); -}; - -export { - WrappedRoute, -}; diff --git a/packages/pl-fe/src/pages/statuses/status.tsx b/packages/pl-fe/src/pages/statuses/status.tsx index a1dc29fe5..718920e96 100644 --- a/packages/pl-fe/src/pages/statuses/status.tsx +++ b/packages/pl-fe/src/pages/statuses/status.tsx @@ -37,7 +37,7 @@ const messages = defineMessages({ }); const StatusPage: React.FC = () => { - const { statusId } = statusRoute.useParams(); + const { username, statusId } = statusRoute.useParams(); const dispatch = useAppDispatch(); const intl = useIntl(); @@ -102,6 +102,12 @@ const StatusPage: React.FC = () => { ); } + if (username && status && username !== status.account.acct) { + return ( + + ); + } + if (!status && isLoaded) { return ( diff --git a/packages/pl-fe/src/queries/statuses/use-event-interactions.ts b/packages/pl-fe/src/queries/statuses/use-event-interactions.ts index b242d8cc8..d88230d34 100644 --- a/packages/pl-fe/src/queries/statuses/use-event-interactions.ts +++ b/packages/pl-fe/src/queries/statuses/use-event-interactions.ts @@ -42,7 +42,10 @@ const useJoinEventMutation = (statusId: string, withToast = true) => { status.event?.join_state === 'pending' ? messages.joinRequestSuccess : messages.joinSuccess, { actionLabel: messages.view, - actionLink: `/@${status.account.acct}/events/${status.id}`, + actionLinkOptions: { + to: '/@{$username}/events/$statusId', + params: { username: status.account.acct, statusId: status.id }, + }, }, ); } diff --git a/packages/pl-fe/src/queries/statuses/use-status-interactions.ts b/packages/pl-fe/src/queries/statuses/use-status-interactions.ts index 7f19b5448..b73291677 100644 --- a/packages/pl-fe/src/queries/statuses/use-status-interactions.ts +++ b/packages/pl-fe/src/queries/statuses/use-status-interactions.ts @@ -197,7 +197,10 @@ const useBookmarkStatus = (statusId: string) => { let opts: IToastOptions = { actionLabel: messages.view, - actionLink: folderId ? `/bookmarks/${folderId}` : '/bookmarks/all', + actionLinkOptions: { + to: '/bookmarks/$folderId', + params: { folderId: folderId || 'all' }, + }, }; if (features.bookmarkFolders && typeof folderId !== 'string') { diff --git a/packages/pl-fe/src/toast.tsx b/packages/pl-fe/src/toast.tsx index 9a753ac33..744c6035b 100644 --- a/packages/pl-fe/src/toast.tsx +++ b/packages/pl-fe/src/toast.tsx @@ -6,13 +6,14 @@ import Toast from './components/ui/toast'; import { httpErrorMessages } from './utils/errors'; import type { PlfeResponse } from './api'; +import type { LinkOptions } from '@tanstack/react-router'; type ToastText = string | MessageDescriptor type ToastType = 'success' | 'error' | 'info' interface IToastOptions { action?(): void; - actionLink?: string; + actionLinkOptions?: LinkOptions; actionLabel?: ToastText; duration?: number; summary?: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b3ef8a57..7735afe58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -370,9 +370,6 @@ importers: react-redux: specifier: ^9.0.4 version: 9.2.0(@types/react@18.3.23)(react@18.3.1)(redux@5.0.1) - react-router-dom: - specifier: ^5.3.4 - version: 5.3.4(react@18.3.1) react-sparklines: specifier: ^1.7.0 version: 1.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -4253,9 +4250,6 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - history@4.10.1: - resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} - hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -4537,9 +4531,6 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} - isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -5147,9 +5138,6 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@1.9.0: - resolution: {integrity: sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -5597,16 +5585,6 @@ packages: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} - react-router-dom@5.3.4: - resolution: {integrity: sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==} - peerDependencies: - react: '>=15' - - react-router@5.3.4: - resolution: {integrity: sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==} - peerDependencies: - react: '>=15' - react-sparklines@1.7.0: resolution: {integrity: sha512-bJFt9K4c5Z0k44G8KtxIhbG+iyxrKjBZhdW6afP+R7EnIq+iKjbWbEFISrf3WKNFsda+C46XAfnX0StS5fbDcg==} peerDependencies: @@ -5724,9 +5702,6 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve-pathname@3.0.0: - resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -6615,9 +6590,6 @@ packages: typescript: optional: true - value-equal@1.0.1: - resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==} - varint@6.0.0: resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} @@ -11304,15 +11276,6 @@ snapshots: he@1.2.0: {} - history@4.10.1: - dependencies: - '@babel/runtime': 7.28.2 - loose-envify: 1.4.0 - resolve-pathname: 3.0.0 - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - value-equal: 1.0.1 - hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 @@ -11582,8 +11545,6 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 - isarray@0.0.1: {} - isarray@2.0.5: {} isbot@5.1.32: {} @@ -12204,10 +12165,6 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-to-regexp@1.9.0: - dependencies: - isarray: 0.0.1 - path-type@4.0.0: {} pathe@0.2.0: {} @@ -12616,30 +12573,6 @@ snapshots: react-refresh@0.17.0: {} - react-router-dom@5.3.4(react@18.3.1): - dependencies: - '@babel/runtime': 7.28.2 - history: 4.10.1 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.3.1 - react-router: 5.3.4(react@18.3.1) - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - - react-router@5.3.4(react@18.3.1): - dependencies: - '@babel/runtime': 7.28.2 - history: 4.10.1 - hoist-non-react-statics: 3.3.2 - loose-envify: 1.4.0 - path-to-regexp: 1.9.0 - prop-types: 15.8.1 - react: 18.3.1 - react-is: 16.13.1 - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - react-sparklines@1.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: prop-types: 15.8.1 @@ -12769,8 +12702,6 @@ snapshots: resolve-from@5.0.0: {} - resolve-pathname@3.0.0: {} - resolve-pkg-maps@1.0.0: {} resolve@1.22.10: @@ -13751,8 +13682,6 @@ snapshots: optionalDependencies: typescript: 5.9.2 - value-equal@1.0.1: {} - varint@6.0.0: {} vite-node@2.1.9(@types/node@22.17.0)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0):