From 6952f6034ea0e1f1d27252cda6b934199e5d6d8a Mon Sep 17 00:00:00 2001 From: Chenyme <118253778+chenyme@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:30:17 +0800 Subject: [PATCH 1/2] style: update leaderboard podium styles for improved visual consistency --- .../common/leaderboard/leaderboard-main.tsx | 42 ++-- .../common/leaderboard/leaderboard-podium.tsx | 191 +++++++++--------- .../common/leaderboard/leaderboard-table.tsx | 155 +++++++------- .../common/leaderboard/rank-row-item.tsx | 43 +++- frontend/components/ui/scroll-area.tsx | 8 +- 5 files changed, 246 insertions(+), 193 deletions(-) diff --git a/frontend/components/common/leaderboard/leaderboard-main.tsx b/frontend/components/common/leaderboard/leaderboard-main.tsx index 5c06ae10..a1c6dbc3 100644 --- a/frontend/components/common/leaderboard/leaderboard-main.tsx +++ b/frontend/components/common/leaderboard/leaderboard-main.tsx @@ -19,8 +19,6 @@ import { cn } from "@/lib/utils" /** * 排行榜主组件 - * - * 负责组装排行榜的各个子组件,包括领奖台、用户排名卡片和排名列表 */ export function LeaderboardMain() { const { user } = useUser() @@ -56,16 +54,16 @@ export function LeaderboardMain() { } return ( -
-
+
+

全局排行榜

-
+
- @@ -79,24 +77,30 @@ export function LeaderboardMain() { -
- - - + +
+

完整榜单

+ +
) } diff --git a/frontend/components/common/leaderboard/leaderboard-podium.tsx b/frontend/components/common/leaderboard/leaderboard-podium.tsx index f49bad40..a34be48f 100644 --- a/frontend/components/common/leaderboard/leaderboard-podium.tsx +++ b/frontend/components/common/leaderboard/leaderboard-podium.tsx @@ -5,80 +5,114 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Skeleton } from "@/components/ui/skeleton" import { cn } from "@/lib/utils" import type { LeaderboardEntry } from "@/lib/services/leaderboard" -import { Crown, Medal, Trophy } from "lucide-react" + interface LeaderboardPodiumProps { items: LeaderboardEntry[] loading?: boolean } -const rankStyles = { +type PodiumRank = 1 | 2 | 3 + +function formatBalance(value: string) { + return Number.parseFloat(value).toLocaleString("zh-CN", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }) +} + +const rankConfig = { 1: { - icon: Crown, - iconClassName: "text-amber-500 dark:text-amber-300", - rowClassName: "h-[68px] grid-cols-[44px_40px_minmax(0,1fr)_112px] bg-amber-500/[0.10] border-amber-400/45 px-4 dark:bg-amber-300/[0.08] dark:border-amber-200/20", - badgeClassName: "text-amber-700 dark:text-amber-200", - avatarClassName: "size-10", - nameClassName: "text-sm", - metaClassName: "text-amber-700/70 dark:text-amber-100/65", - valueClassName: "text-lg leading-none text-amber-700 dark:text-amber-100", + numColor: "from-amber-500/60 to-amber-500/8", + scoreColor: "text-amber-500", + avatarSize: "size-16 sm:size-[72px]", + numSize: "text-[72px] sm:text-[88px]", + nameSize: "text-base font-bold", + scoreSize: "text-lg font-black", + avatarNegMt: "-mt-6 sm:-mt-7", + order: "md:order-2", + shift: "", }, 2: { - icon: Trophy, - iconClassName: "text-slate-500 dark:text-slate-200", - rowClassName: "h-[68px] grid-cols-[44px_40px_minmax(0,1fr)_112px] bg-slate-100/70 border-slate-200/80 px-4 dark:bg-slate-200/[0.04] dark:border-slate-200/12", - badgeClassName: "text-slate-600 dark:text-slate-200", - avatarClassName: "size-10", - nameClassName: "text-sm", - metaClassName: "text-slate-600/75 dark:text-slate-300/70", - valueClassName: "text-lg leading-none text-foreground dark:text-slate-100", + numColor: "from-zinc-400/45 to-zinc-400/5", + scoreColor: "text-zinc-400", + avatarSize: "size-12 sm:size-[52px]", + numSize: "text-[52px] sm:text-[64px]", + nameSize: "text-sm font-semibold", + scoreSize: "text-sm font-bold", + avatarNegMt: "-mt-5", + order: "md:order-1", + shift: "md:translate-y-9", }, 3: { - icon: Medal, - iconClassName: "text-orange-600 dark:text-orange-300", - rowClassName: "h-[68px] grid-cols-[44px_40px_minmax(0,1fr)_112px] bg-orange-100/60 border-orange-200/70 px-4 dark:bg-orange-300/[0.05] dark:border-orange-200/12", - badgeClassName: "text-orange-700 dark:text-orange-200", - avatarClassName: "size-10", - nameClassName: "text-sm", - metaClassName: "text-orange-700/75 dark:text-orange-200/70", - valueClassName: "text-lg leading-none text-foreground dark:text-orange-100", + numColor: "from-orange-500/55 to-orange-500/8", + scoreColor: "text-orange-500", + avatarSize: "size-12 sm:size-[52px]", + numSize: "text-[52px] sm:text-[64px]", + nameSize: "text-sm font-semibold", + scoreSize: "text-sm font-bold", + avatarNegMt: "-mt-5", + order: "md:order-3", + shift: "md:translate-y-14", }, } as const -function TopRankRow({ - entry, - rank, -}: { - entry: LeaderboardEntry - rank: 1 | 2 | 3 -}) { - const style = rankStyles[rank] - const Icon = style.icon +function PodiumSkeleton() { + return ( +
+
+ {([2, 1, 3] as PodiumRank[]).map((rank) => { + const cfg = rankConfig[rank] + return ( +
+ + + + +
+ ) + })} +
+
+ ) +} + +function PodiumColumn({ entry, rank }: { entry: LeaderboardEntry; rank: PodiumRank }) { + const cfg = rankConfig[rank] return ( -
-
- +
+ + {/* 排名大字 */} +
+ {String(rank).padStart(2, "0")}
- - - + {/* 头像 — 圆形 */} + + + {entry.username.slice(0, 2).toUpperCase()} -
-
{entry.username}
+ {/* 用户名 */} +
+ {entry.username}
-
-
可用积分
-
- {parseFloat(entry.available_balance).toFixed(2)} -
+ {/* 积分 */} +
+ {formatBalance(entry.available_balance)}
-
+ +
) } @@ -86,58 +120,25 @@ export const LeaderboardPodium = React.memo(function LeaderboardPodium({ items, loading, }: LeaderboardPodiumProps) { - if (loading) { - return ( -
-

Top 3

-
- {Array.from({ length: 3 }).map((_, index) => ( -
- - - -
- - -
-
- ))} -
-
- ) - } + if (loading) return if (items.length === 0) { - return ( -
-

Top 3

-
- 暂无排行数据 -
-
- ) + return
暂无排行数据
} + const ordered = [ + items[1] ? { entry: items[1], rank: 2 as PodiumRank } : null, + items[0] ? { entry: items[0], rank: 1 as PodiumRank } : null, + items[2] ? { entry: items[2], rank: 3 as PodiumRank } : null, + ].filter(Boolean) as Array<{ entry: LeaderboardEntry; rank: PodiumRank }> + return ( -
-

Top 3

-
- {items.map((entry, index) => ( - +
+
+ {ordered.map(({ entry, rank }) => ( + ))}
-
+
) }) diff --git a/frontend/components/common/leaderboard/leaderboard-table.tsx b/frontend/components/common/leaderboard/leaderboard-table.tsx index 1c77180b..aebd1563 100644 --- a/frontend/components/common/leaderboard/leaderboard-table.tsx +++ b/frontend/components/common/leaderboard/leaderboard-table.tsx @@ -3,9 +3,13 @@ import * as React from "react" import { useVirtualizer } from "@tanstack/react-virtual" import { Skeleton } from "@/components/ui/skeleton" +import { ScrollArea } from "@/components/ui/scroll-area" import { RankRowItem } from "./rank-row-item" import type { LeaderboardEntry } from "@/lib/services/leaderboard" +const leaderboardTableColumns = "grid-cols-[72px_minmax(0,1fr)_132px] sm:grid-cols-[88px_minmax(0,1fr)_152px]" +const leaderboardRowHeight = 60 + interface LeaderboardTableProps { items: LeaderboardEntry[] loading?: boolean @@ -28,21 +32,28 @@ export const LeaderboardTable = React.memo(function LeaderboardTable({ startRank = 1, }: LeaderboardTableProps) { const parentRef = React.useRef(null) + + const maxBalance = React.useMemo(() => { + if (items.length === 0) return 1 + return Math.max(...items.map(item => parseFloat(item.available_balance))) || 1 + }, [items]) + const rankedItems = React.useMemo( () => items .map((entry, index) => ({ entry, rank: startRank + index, + percentage: (parseFloat(entry.available_balance) / maxBalance) * 100, })) .filter(({ entry }) => entry.user_id !== currentUserEntry?.user_id), - [items, startRank, currentUserEntry?.user_id] + [items, startRank, currentUserEntry?.user_id, maxBalance] ) const virtualizer = useVirtualizer({ count: rankedItems.length, getScrollElement: () => parentRef.current, - estimateSize: () => 68, + estimateSize: () => leaderboardRowHeight, overscan: 6, useFlushSync: false, }) @@ -52,106 +63,110 @@ export const LeaderboardTable = React.memo(function LeaderboardTable({ React.useEffect(() => { const lastItem = virtualItems[virtualItems.length - 1] if (!lastItem) return - if (lastItem.index >= rankedItems.length - 1 && hasMore && !loading && onLoadMore) { onLoadMore() } }, [virtualItems, rankedItems.length, hasMore, loading, onLoadMore]) + const currentUserPercentage = currentUserEntry + ? (parseFloat(currentUserEntry.available_balance) / maxBalance) * 100 + : 0 + if (loading && items.length === 0) { return ( -
-

完整榜单

-
+
+
+
排名
+
用户
+
可用积分
+
+
{Array.from({ length: 8 }).map((_, i) => ( -
- +
+
-
- - -
+
))}
-
+
) } if (items.length === 0) { - return ( -
-

完整榜单

-
- 暂无排行数据 -
-
- ) + return
暂无排行数据
} return ( -
-

完整榜单

- -
-
-
排名
-
用户
-
可用积分
-
+
+ {/* 列头 */} +
+
排名
+
用户
+
可用积分
+
- {currentUserEntry && currentUserRank && ( + {/* 当前用户置顶行 */} + {currentUserEntry && currentUserRank && ( +
- )} - -
-
- {virtualItems.map((virtualRow) => { - const { entry, rank } = rankedItems[virtualRow.index] - return ( -
- -
- ) - })} -
+ )} - {loading && items.length > 0 && ( -
- 正在加载更多排行... -
- )} + {/* 榜单主体 — 从 muted 过渡到页面背景色 */} +
+
+ +
+ {virtualItems.map((virtualRow) => { + const { entry, rank, percentage } = rankedItems[virtualRow.index] + return ( +
+ +
+ ) + })} +
+
+
-
+ + {loading && items.length > 0 && ( +
+ 正在加载更多... +
+ )} +
) }) diff --git a/frontend/components/common/leaderboard/rank-row-item.tsx b/frontend/components/common/leaderboard/rank-row-item.tsx index b447cb7a..9073cbd2 100644 --- a/frontend/components/common/leaderboard/rank-row-item.tsx +++ b/frontend/components/common/leaderboard/rank-row-item.tsx @@ -5,33 +5,58 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { cn } from "@/lib/utils" import type { LeaderboardEntry } from "@/lib/services/leaderboard" +const leaderboardRowColumns = "grid-cols-[72px_minmax(0,1fr)_112px] sm:grid-cols-[88px_minmax(0,1fr)_128px]" + interface RankRowItemProps { entry: LeaderboardEntry rank: number + percentage?: number isCurrentUser?: boolean metaLabel?: string pinned?: boolean } + export const RankRowItem = React.memo(function RankRowItem({ entry, rank, + percentage = 0, isCurrentUser, metaLabel, pinned = false, }: RankRowItemProps) { + const rankClass = + rank === 1 ? "text-amber-500 font-black" : + rank === 2 ? "text-zinc-400 font-bold" : + rank === 3 ? "text-orange-500 font-bold" : + "text-muted-foreground" + return (
-
{rank}
+ {/* 进度条背景 */} + {percentage > 0 && ( +
+ )} + +
+ #{rank} +
-
- +
+ {entry.username.slice(0, 2).toUpperCase()} @@ -40,12 +65,14 @@ export const RankRowItem = React.memo(function RankRowItem({
{entry.username}
{metaLabel && ( -
{metaLabel}
+
+ {metaLabel} +
)}
-
+
{parseFloat(entry.available_balance).toFixed(2)}
diff --git a/frontend/components/ui/scroll-area.tsx b/frontend/components/ui/scroll-area.tsx index 8e4fa13f..abf0d822 100644 --- a/frontend/components/ui/scroll-area.tsx +++ b/frontend/components/ui/scroll-area.tsx @@ -5,11 +5,16 @@ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" import { cn } from "@/lib/utils" +type ScrollAreaProps = React.ComponentProps & { + viewportRef?: React.Ref +} + function ScrollArea({ className, children, + viewportRef, ...props -}: React.ComponentProps) { +}: ScrollAreaProps) { return ( From 33d4b44d8e3aac8204538e265befe48380ccbedb Mon Sep 17 00:00:00 2001 From: Chenyme <118253778+chenyme@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:43:33 +0800 Subject: [PATCH 2/2] chore: update axios and form-data versions, enhance login page session handling, and improve leaderboard component loading states --- frontend/components/auth/login-page.tsx | 97 ++++++++++++++++--- .../common/leaderboard/leaderboard-main.tsx | 10 +- .../common/leaderboard/leaderboard-podium.tsx | 10 +- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 27 +++--- 5 files changed, 102 insertions(+), 44 deletions(-) diff --git a/frontend/components/auth/login-page.tsx b/frontend/components/auth/login-page.tsx index f02f63d0..5bab9f08 100644 --- a/frontend/components/auth/login-page.tsx +++ b/frontend/components/auth/login-page.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useState } from "react" +import { useCallback, useEffect, useState } from "react" import { motion, AnimatePresence } from "motion/react" import { useRouter, useSearchParams } from "next/navigation" import { toast } from "sonner" @@ -12,6 +12,7 @@ import { Check } from "lucide-react" import { AuroraBackground } from "@/components/ui/aurora-background" import services from "@/lib/services" +import type { ApiResponse } from "@/lib/services/core/types" /** @@ -34,6 +35,7 @@ export function LoginPage() { const code = searchParams.get('code') return !!(state && code) }) + const [isCheckingSession, setIsCheckingSession] = useState(() => !searchParams.get('state') || !searchParams.get('code')) const [loginSuccess, setLoginSuccess] = useState(false) const [needsPayKeySetup, setNeedsPayKeySetup] = useState(false) @@ -47,6 +49,18 @@ export function LoginPage() { const isConfirmValid = confirmPayKey.length === 6 && /^\d{6}$/.test(confirmPayKey) const passwordsMatch = payKey === confirmPayKey + const resolveRedirectTarget = useCallback(() => { + const callbackUrl = searchParams.get('callbackUrl') + const storedRedirect = sessionStorage.getItem('redirect_after_login') + const target = callbackUrl || storedRedirect || '/home' + + if (storedRedirect) { + sessionStorage.removeItem('redirect_after_login') + } + + return target + }, [searchParams]) + /* 安全密码输入 */ const handlePayKeyChange = (value: string) => { const numericValue = value.replace(/\D/g, '') @@ -59,6 +73,56 @@ export function LoginPage() { setConfirmPayKey(numericValue) } + /* 登录页兜底:已登录用户直接跳转 */ + useEffect(() => { + const state = searchParams.get('state') + const code = searchParams.get('code') + + if (state && code) { + setIsCheckingSession(false) + return + } + + let cancelled = false + + const checkExistingSession = async () => { + setIsCheckingSession(true) + + try { + const response = await fetch('/api/v1/oauth/user-info', { + credentials: 'include', + cache: 'no-store', + }) + + if (cancelled) return + + if (response.ok) { + await response.json() as ApiResponse + router.replace(resolveRedirectTarget()) + return + } + + if (response.status !== 401) { + console.error('Session probe failed:', response.status) + } + } catch (error) { + if (!cancelled) { + console.error('Session probe error:', error) + } + } finally { + if (!cancelled) { + setIsCheckingSession(false) + } + } + } + + checkExistingSession() + + return () => { + cancelled = true + } + }, [router, searchParams, resolveRedirectTarget]) + /* 回调逻辑 */ useEffect(() => { const handleOAuthCallback = async () => { @@ -78,13 +142,8 @@ export function LoginPage() { setLoginSuccess(true) toast.success("登录成功") - const callbackUrl = searchParams.get('callbackUrl') || sessionStorage.getItem('redirect_after_login') || '/home' - if (sessionStorage.getItem('redirect_after_login')) { - sessionStorage.removeItem('redirect_after_login') - } - setTimeout(() => { - router.replace(callbackUrl) + router.replace(resolveRedirectTarget()) }, 1500) } } catch (error) { @@ -96,7 +155,7 @@ export function LoginPage() { } } handleOAuthCallback() - }, [searchParams, router]) + }, [searchParams, router, resolveRedirectTarget]) /* 安全密码设置 */ const handlePayKeySubmit = async (e: React.FormEvent) => { @@ -131,11 +190,7 @@ export function LoginPage() { setConfirmPayKey("") setSetupStep('password') setTimeout(() => { - const callbackUrl = searchParams.get('callbackUrl') || sessionStorage.getItem('redirect_after_login') || '/home' - if (sessionStorage.getItem('redirect_after_login')) { - sessionStorage.removeItem('redirect_after_login') - } - router.replace(callbackUrl) + router.replace(resolveRedirectTarget()) }, 1500) } catch (error) { const errorMessage = error instanceof Error ? error.message : "设置安全密码失败" @@ -266,15 +321,25 @@ export function LoginPage() {
- {isProcessingCallback ? ( + {isProcessingCallback || isCheckingSession ? ( - {needsPayKeySetup ? ( + {isCheckingSession ? ( +
+
+ +
+
+

正在检查登录状态

+

请稍候,我们正在确认当前会话...

+
+
+ ) : needsPayKeySetup ? ( renderPayKeySetup("oauth-pay-key-setup") ) : loginSuccess ? (
diff --git a/frontend/components/common/leaderboard/leaderboard-main.tsx b/frontend/components/common/leaderboard/leaderboard-main.tsx index a1c6dbc3..830cce03 100644 --- a/frontend/components/common/leaderboard/leaderboard-main.tsx +++ b/frontend/components/common/leaderboard/leaderboard-main.tsx @@ -14,7 +14,6 @@ import { useUser } from "@/contexts/user-context" import { useLeaderboard } from "@/hooks/use-leaderboard" import { LeaderboardPodium } from "@/components/common/leaderboard/leaderboard-podium" import { LeaderboardTable } from "@/components/common/leaderboard/leaderboard-table" -import { LoadingState } from "@/components/layout/loading" import { cn } from "@/lib/utils" /** @@ -32,6 +31,7 @@ export function LeaderboardMain() { loadNextPage, refresh, } = useLeaderboard() + const isInitialLoading = loading && items.length === 0 const currentUserEntry = React.useMemo(() => { if (!myRank?.user) return undefined @@ -49,10 +49,6 @@ export function LeaderboardMain() { } }, [items, myRank, user]) - if (loading && items.length === 0) { - return - } - return (
@@ -85,14 +81,14 @@ export function LeaderboardMain() {

完整榜单

- +
@@ -83,10 +83,9 @@ function PodiumColumn({ entry, rank }: { entry: LeaderboardEntry; rank: PodiumRa return (
- {/* 排名大字 */}
- {/* 头像 — 圆形 */} @@ -102,13 +100,11 @@ function PodiumColumn({ entry, rank }: { entry: LeaderboardEntry; rank: PodiumRa - {/* 用户名 */}
{entry.username}
- {/* 积分 */} -
+
{formatBalance(entry.available_balance)}
diff --git a/frontend/package.json b/frontend/package.json index dc9a917a..1a7ac323 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,7 +38,7 @@ "@tabler/icons-react": "^3.35.0", "@tanstack/react-table": "^8.21.3", "@tanstack/react-virtual": "^3.13.16", - "axios": "^1.13.2", + "axios": "^1.15.0", "babel-plugin-react-compiler": "^1.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index fc6293b0..1fd51fb7 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -96,8 +96,8 @@ importers: specifier: ^3.13.16 version: 3.13.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) axios: - specifier: ^1.13.2 - version: 1.13.2 + specifier: ^1.15.0 + version: 1.15.0 babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 @@ -1811,8 +1811,8 @@ packages: resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} engines: {node: '>=4'} - axios@1.13.2: - resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + axios@1.15.0: + resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==} axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} @@ -2328,8 +2328,8 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} format@0.2.2: @@ -3135,8 +3135,9 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -5223,11 +5224,11 @@ snapshots: axe-core@4.11.0: {} - axios@1.13.2: + axios@1.15.0: dependencies: follow-redirects: 1.15.11 - form-data: 4.0.4 - proxy-from-env: 1.1.0 + form-data: 4.0.5 + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug @@ -5877,7 +5878,7 @@ snapshots: dependencies: is-callable: 1.2.7 - form-data@4.0.4: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -6908,7 +6909,7 @@ snapshots: property-information@7.1.0: {} - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} punycode@2.3.1: {}