mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
Compare commits
No commits in common. "main" and "v2.8.1" have entirely different histories.
2
.github/workflows/Deploy.yml
vendored
2
.github/workflows/Deploy.yml
vendored
@ -1,6 +1,4 @@
|
|||||||
name: Build and push Docker image
|
name: Build and push Docker image
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
name: Auto Fix Lint and Format
|
name: Auto Fix Lint and Format
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request_target:
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
- Cloudflare
|
- Cloudflare
|
||||||
- Docker
|
- Docker
|
||||||
|
|
||||||
[演示站点](https://nezha-vercel.vercel.app)
|
[演示站点](https://nezha-cf.buycoffee.top)
|
||||||
[说明文档](https://nezhadash-docs.vercel.app)
|
[说明文档](https://nezhadash-docs.vercel.app)
|
||||||
|
|
||||||
### 如何更新
|
### 如何更新
|
||||||
|
@ -83,7 +83,6 @@ export default function ServerDetailClient({
|
|||||||
net_out_transfer,
|
net_out_transfer,
|
||||||
net_in_transfer,
|
net_in_transfer,
|
||||||
last_active_time_string,
|
last_active_time_string,
|
||||||
boot_time_string,
|
|
||||||
} = formatNezhaInfo(serverData)
|
} = formatNezhaInfo(serverData)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -121,8 +120,8 @@ export default function ServerDetailClient({
|
|||||||
<div className="text-xs">
|
<div className="text-xs">
|
||||||
{" "}
|
{" "}
|
||||||
{uptime / 86400 >= 1
|
{uptime / 86400 >= 1
|
||||||
? `${Math.floor(uptime / 86400)} ${t("Days")} ${Math.floor((uptime % 86400) / 3600)} ${t("Hours")}`
|
? `${(uptime / 86400).toFixed(0)} ${t("Days")}`
|
||||||
: `${Math.floor(uptime / 3600)} ${t("Hours")}`}
|
: `${(uptime / 3600).toFixed(0)} ${t("Hours")}`}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@ -252,14 +251,6 @@ export default function ServerDetailClient({
|
|||||||
</Card>
|
</Card>
|
||||||
</section>
|
</section>
|
||||||
<section className="mt-1 flex flex-wrap gap-2">
|
<section className="mt-1 flex flex-wrap gap-2">
|
||||||
<Card className="rounded-[10px] border-none bg-transparent shadow-none">
|
|
||||||
<CardContent className="px-1.5 py-1">
|
|
||||||
<section className="flex flex-col items-start gap-0.5">
|
|
||||||
<p className="text-muted-foreground text-xs">{t("BootTime")}</p>
|
|
||||||
<div className="text-xs">{boot_time_string ? boot_time_string : "N/A"}</div>
|
|
||||||
</section>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<Card className="rounded-[10px] border-none bg-transparent shadow-none">
|
<Card className="rounded-[10px] border-none bg-transparent shadow-none">
|
||||||
<CardContent className="px-1.5 py-1">
|
<CardContent className="px-1.5 py-1">
|
||||||
<section className="flex flex-col items-start gap-0.5">
|
<section className="flex flex-col items-start gap-0.5">
|
||||||
|
@ -21,8 +21,7 @@
|
|||||||
"useSortedClasses": "error"
|
"useSortedClasses": "error"
|
||||||
},
|
},
|
||||||
"a11y": {
|
"a11y": {
|
||||||
"useKeyWithClickEvents": "off",
|
"useKeyWithClickEvents": "off"
|
||||||
"noLabelWithoutControl": "off"
|
|
||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"noDangerouslySetInnerHtml": "off"
|
"noDangerouslySetInnerHtml": "off"
|
||||||
|
@ -20,8 +20,10 @@ export default function Switch({
|
|||||||
const tagRefs = useRef(allTag.map(() => createRef<HTMLDivElement>()))
|
const tagRefs = useRef(allTag.map(() => createRef<HTMLDivElement>()))
|
||||||
const t = useTranslations("ServerListClient")
|
const t = useTranslations("ServerListClient")
|
||||||
const locale = useLocale()
|
const locale = useLocale()
|
||||||
const [indicator, setIndicator] = useState<{ x: number; w: number } | null>(null)
|
const [indicator, setIndicator] = useState<{ x: number; w: number }>({
|
||||||
const [isFirstRender, setIsFirstRender] = useState(true)
|
x: 0,
|
||||||
|
w: 0,
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedTag = sessionStorage.getItem("selectedTag")
|
const savedTag = sessionStorage.getItem("selectedTag")
|
||||||
@ -57,13 +59,7 @@ export default function Switch({
|
|||||||
w: currentTagElement.offsetWidth,
|
w: currentTagElement.offsetWidth,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}, [nowTag, locale])
|
||||||
if (isFirstRender) {
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsFirstRender(false)
|
|
||||||
}, 50)
|
|
||||||
}
|
|
||||||
}, [nowTag, locale, allTag, isFirstRender])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentTagElement = tagRefs.current[allTag.indexOf(nowTag)]?.current
|
const currentTagElement = tagRefs.current[allTag.indexOf(nowTag)]?.current
|
||||||
@ -88,14 +84,14 @@ export default function Switch({
|
|||||||
className="scrollbar-hidden z-50 flex flex-col items-start overflow-x-scroll rounded-[50px]"
|
className="scrollbar-hidden z-50 flex flex-col items-start overflow-x-scroll rounded-[50px]"
|
||||||
>
|
>
|
||||||
<div className="relative flex items-center gap-1 rounded-[50px] bg-stone-100 p-[3px] dark:bg-stone-800">
|
<div className="relative flex items-center gap-1 rounded-[50px] bg-stone-100 p-[3px] dark:bg-stone-800">
|
||||||
{indicator && (
|
{indicator.w > 0 && (
|
||||||
<div
|
<div
|
||||||
className="absolute top-[3px] left-0 z-10 h-[35px] bg-white shadow-black/5 shadow-lg dark:bg-stone-700 dark:shadow-white/5"
|
className="absolute top-[3px] left-0 z-10 h-[35px] bg-white shadow-black/5 shadow-lg dark:bg-stone-700 dark:shadow-white/5"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 24,
|
borderRadius: 24,
|
||||||
width: `${indicator.w}px`,
|
width: `${indicator.w}px`,
|
||||||
transform: `translateX(${indicator.x}px)`,
|
transform: `translateX(${indicator.x}px)`,
|
||||||
transition: isFirstRender ? "none" : "all 0.5s cubic-bezier(0.4, 0, 0.2, 1)",
|
transition: "all 0.5s cubic-bezier(0.4, 0, 0.2, 1)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -4,28 +4,23 @@ import { Button } from "@/components/ui/button"
|
|||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { CheckIcon, MinusIcon, Moon, Sun } from "lucide-react"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { CheckCircleIcon } from "@heroicons/react/20/solid"
|
||||||
|
import { Moon, Sun } from "lucide-react"
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
import { useTheme } from "next-themes"
|
import { useTheme } from "next-themes"
|
||||||
import { useId } from "react"
|
|
||||||
import { RadioGroup, RadioGroupItem } from "./ui/radio-group"
|
|
||||||
|
|
||||||
const items = [
|
|
||||||
{ value: "light", label: "Light", image: "/ui-light.png" },
|
|
||||||
{ value: "dark", label: "Dark", image: "/ui-dark.png" },
|
|
||||||
{ value: "system", label: "System", image: "/ui-system.png" },
|
|
||||||
]
|
|
||||||
|
|
||||||
export function ModeToggle() {
|
export function ModeToggle() {
|
||||||
const { setTheme, theme } = useTheme()
|
const { setTheme, theme } = useTheme()
|
||||||
const t = useTranslations("ThemeSwitcher")
|
const t = useTranslations("ThemeSwitcher")
|
||||||
|
|
||||||
const handleSelect = (newTheme: string) => {
|
const handleSelect = (e: Event, newTheme: string) => {
|
||||||
|
e.preventDefault()
|
||||||
setTheme(newTheme)
|
setTheme(newTheme)
|
||||||
}
|
}
|
||||||
const id = useId()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@ -40,40 +35,31 @@ export function ModeToggle() {
|
|||||||
<span className="sr-only">Toggle theme</span>
|
<span className="sr-only">Toggle theme</span>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="px-2 pt-2 pb-1.5" align="end">
|
<DropdownMenuContent className="flex flex-col gap-0.5" align="end">
|
||||||
<fieldset className="space-y-4">
|
<DropdownMenuItem
|
||||||
<RadioGroup className="flex gap-2" defaultValue={theme} onValueChange={handleSelect}>
|
className={cn("rounded-b-[5px]", {
|
||||||
{items.map((item) => (
|
"gap-3 bg-muted font-semibold": theme === "light",
|
||||||
<label key={`${id}-${item.value}`}>
|
})}
|
||||||
<RadioGroupItem
|
onSelect={(e) => handleSelect(e, "light")}
|
||||||
id={`${id}-${item.value}`}
|
>
|
||||||
value={item.value}
|
{t("Light")} {theme === "light" && <CheckCircleIcon className="size-4" />}
|
||||||
className="peer sr-only after:absolute after:inset-0"
|
</DropdownMenuItem>
|
||||||
/>
|
<DropdownMenuItem
|
||||||
<img
|
className={cn("rounded-[5px]", {
|
||||||
src={item.image}
|
"gap-3 bg-muted font-semibold": theme === "dark",
|
||||||
alt={item.label}
|
})}
|
||||||
width={88}
|
onSelect={(e) => handleSelect(e, "dark")}
|
||||||
height={70}
|
>
|
||||||
className="relative cursor-pointer overflow-hidden rounded-[8px] border border-neutral-300 shadow-xs outline-none transition-[color,box-shadow] peer-focus-visible:ring-[3px] peer-focus-visible:ring-ring/50 peer-data-disabled:cursor-not-allowed peer-data-[state=checked]:bg-accent peer-data-disabled:opacity-50 dark:border-neutral-700"
|
{t("Dark")} {theme === "dark" && <CheckCircleIcon className="size-4" />}
|
||||||
/>
|
</DropdownMenuItem>
|
||||||
<span className="group mt-2 flex items-center gap-1 peer-data-[state=unchecked]:text-muted-foreground/70">
|
<DropdownMenuItem
|
||||||
<CheckIcon
|
className={cn("rounded-t-[5px]", {
|
||||||
size={16}
|
"gap-3 bg-muted font-semibold": theme === "system",
|
||||||
className="group-peer-data-[state=unchecked]:hidden"
|
})}
|
||||||
aria-hidden="true"
|
onSelect={(e) => handleSelect(e, "system")}
|
||||||
/>
|
>
|
||||||
<MinusIcon
|
{t("System")} {theme === "system" && <CheckCircleIcon className="size-4" />}
|
||||||
size={16}
|
</DropdownMenuItem>
|
||||||
className="group-peer-data-[state=checked]:hidden"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span className="font-medium text-xs">{t(item.label)}</span>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</RadioGroup>
|
|
||||||
</fieldset>
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
)
|
)
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
|
||||||
import type * as React from "react"
|
|
||||||
|
|
||||||
function RadioGroup({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
|
|
||||||
return (
|
|
||||||
<RadioGroupPrimitive.Root
|
|
||||||
data-slot="radio-group"
|
|
||||||
className={cn("grid gap-3", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function RadioGroupItem({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
|
|
||||||
return (
|
|
||||||
<RadioGroupPrimitive.Item
|
|
||||||
data-slot="radio-group-item"
|
|
||||||
className={cn(
|
|
||||||
"aspect-square size-4 shrink-0 rounded-full border border-input shadow-xs outline-none transition-shadow focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:aria-invalid:ring-destructive/40",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center text-current">
|
|
||||||
<svg
|
|
||||||
role="img"
|
|
||||||
aria-label="Radio indicator"
|
|
||||||
width="6"
|
|
||||||
height="6"
|
|
||||||
viewBox="0 0 6 6"
|
|
||||||
fill="currentcolor"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<circle cx="3" cy="3" r="3" />
|
|
||||||
</svg>
|
|
||||||
</RadioGroupPrimitive.Indicator>
|
|
||||||
</RadioGroupPrimitive.Item>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { RadioGroup, RadioGroupItem }
|
|
@ -4,7 +4,7 @@ import getEnv from "./lib/env-entry"
|
|||||||
export const localeItems = [
|
export const localeItems = [
|
||||||
{ code: "en", name: "English" },
|
{ code: "en", name: "English" },
|
||||||
{ code: "ja", name: "日本語" },
|
{ code: "ja", name: "日本語" },
|
||||||
{ code: "zh-TW", name: "中文繁體" },
|
{ code: "zh-t", name: "中文繁體" },
|
||||||
{ code: "zh", name: "中文简体" },
|
{ code: "zh", name: "中文简体" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -16,10 +16,6 @@ export function formatNezhaInfo(serverInfo: NezhaAPISafe) {
|
|||||||
last_active_time_string: serverInfo.last_active
|
last_active_time_string: serverInfo.last_active
|
||||||
? new Date(serverInfo.last_active * 1000).toLocaleString()
|
? new Date(serverInfo.last_active * 1000).toLocaleString()
|
||||||
: "",
|
: "",
|
||||||
boot_time: serverInfo.host.BootTime,
|
|
||||||
boot_time_string: serverInfo.host.BootTime
|
|
||||||
? new Date(serverInfo.host.BootTime * 1000).toLocaleString()
|
|
||||||
: "",
|
|
||||||
online: serverInfo.online_status,
|
online: serverInfo.online_status,
|
||||||
uptime: serverInfo.status.Uptime || 0,
|
uptime: serverInfo.status.Uptime || 0,
|
||||||
version: serverInfo.host.Version || null,
|
version: serverInfo.host.Version || null,
|
||||||
|
@ -86,8 +86,7 @@
|
|||||||
"Upload": "Upload",
|
"Upload": "Upload",
|
||||||
"Download": "Download",
|
"Download": "Download",
|
||||||
"Load": "Load",
|
"Load": "Load",
|
||||||
"LastActive": "Last Active",
|
"LastActive": "Last Active"
|
||||||
"BootTime": "Boot Time"
|
|
||||||
},
|
},
|
||||||
"ServerDetailChartClient": {
|
"ServerDetailChartClient": {
|
||||||
"chart_fetch_error_message": "Please check your environment variables and review the server console",
|
"chart_fetch_error_message": "Please check your environment variables and review the server console",
|
||||||
|
@ -86,8 +86,7 @@
|
|||||||
"Load": "負荷",
|
"Load": "負荷",
|
||||||
"Upload": "Upload",
|
"Upload": "Upload",
|
||||||
"Download": "Download",
|
"Download": "Download",
|
||||||
"LastActive": "Last Active",
|
"LastActive": "Last Active"
|
||||||
"BootTime": "Boot Time"
|
|
||||||
},
|
},
|
||||||
"ServerDetailChartClient": {
|
"ServerDetailChartClient": {
|
||||||
"chart_fetch_error_message": "環境変数を確認し、サーバーコンソールを確認してください",
|
"chart_fetch_error_message": "環境変数を確認し、サーバーコンソールを確認してください",
|
||||||
|
@ -86,8 +86,7 @@
|
|||||||
"Upload": "上傳",
|
"Upload": "上傳",
|
||||||
"Download": "下載",
|
"Download": "下載",
|
||||||
"Load": "負載",
|
"Load": "負載",
|
||||||
"LastActive": "最後上報時間",
|
"LastActive": "最後上報時間"
|
||||||
"BootTime": "啟動時間"
|
|
||||||
},
|
},
|
||||||
"ServerDetailChartClient": {
|
"ServerDetailChartClient": {
|
||||||
"chart_fetch_error_message": "獲取伺服器詳情失敗,請檢查您的環境變數並檢查伺服器控制台",
|
"chart_fetch_error_message": "獲取伺服器詳情失敗,請檢查您的環境變數並檢查伺服器控制台",
|
@ -86,8 +86,7 @@
|
|||||||
"Upload": "上传",
|
"Upload": "上传",
|
||||||
"Download": "下载",
|
"Download": "下载",
|
||||||
"Load": "负载",
|
"Load": "负载",
|
||||||
"LastActive": "最后上报时间",
|
"LastActive": "最后上报时间"
|
||||||
"BootTime": "启动时间"
|
|
||||||
},
|
},
|
||||||
"ServerDetailChartClient": {
|
"ServerDetailChartClient": {
|
||||||
"chart_fetch_error_message": "获取服务器详情失败,请检查您的环境变量并检查服务器控制台",
|
"chart_fetch_error_message": "获取服务器详情失败,请检查您的环境变量并检查服务器控制台",
|
||||||
|
73
package.json
73
package.json
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "nezha-dash",
|
"name": "nezha-dash",
|
||||||
"version": "2.9.3",
|
"version": "2.8.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 3040",
|
"dev": "next dev --turbopack -p 3040",
|
||||||
"start": "node .next/standalone/server.js",
|
"start": "node .next/standalone/server.js",
|
||||||
"lint": "biome lint",
|
"lint": "biome lint",
|
||||||
"lint:fix": "biome lint --fix",
|
"lint:fix": "biome lint --fix",
|
||||||
@ -15,26 +15,25 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ducanh2912/next-pwa": "^10.2.9",
|
"@ducanh2912/next-pwa": "^10.2.9",
|
||||||
"@heroicons/react": "^2.2.0",
|
"@heroicons/react": "^2.2.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.11",
|
"@radix-ui/react-dialog": "^1.1.6",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.12",
|
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
||||||
"@radix-ui/react-label": "^2.1.4",
|
"@radix-ui/react-label": "^2.1.2",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.10",
|
"@radix-ui/react-navigation-menu": "^1.2.5",
|
||||||
"@radix-ui/react-popover": "^1.1.11",
|
"@radix-ui/react-popover": "^1.1.6",
|
||||||
"@radix-ui/react-progress": "^1.1.4",
|
"@radix-ui/react-progress": "^1.1.2",
|
||||||
"@radix-ui/react-radio-group": "^1.3.4",
|
"@radix-ui/react-separator": "^1.1.2",
|
||||||
"@radix-ui/react-separator": "^1.1.4",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@radix-ui/react-slot": "^1.2.0",
|
"@radix-ui/react-switch": "^1.1.3",
|
||||||
"@radix-ui/react-switch": "^1.2.2",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"@radix-ui/react-tooltip": "^1.2.4",
|
|
||||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/d3-geo": "^3.1.0",
|
"@types/d3-geo": "^3.1.0",
|
||||||
"@types/luxon": "^3.6.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"babel-plugin-react-compiler": "^19.0.0-beta-ebf51a3-20250411",
|
"babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112",
|
||||||
"caniuse-lite": "^1.0.30001715",
|
"caniuse-lite": "^1.0.30001701",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.0.4",
|
||||||
"country-flag-icons": "^1.5.18",
|
"country-flag-icons": "^1.5.18",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"d3-geo": "^3.1.1",
|
"d3-geo": "^3.1.1",
|
||||||
@ -42,35 +41,35 @@
|
|||||||
"flag-icons": "^7.3.2",
|
"flag-icons": "^7.3.2",
|
||||||
"i18n-iso-countries": "^7.14.0",
|
"i18n-iso-countries": "^7.14.0",
|
||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"luxon": "^3.6.1",
|
"luxon": "^3.5.0",
|
||||||
"maxmind": "^4.3.24",
|
"maxmind": "^4.3.24",
|
||||||
"next": "^15.3.1",
|
"next": "^15.2.0",
|
||||||
"next-auth": "^5.0.0-beta.26",
|
"next-auth": "^5.0.0-beta.25",
|
||||||
"next-intl": "^4.0.3",
|
"next-intl": "^3.26.5",
|
||||||
"next-runtime-env": "^3.3.0",
|
"next-runtime-env": "^3.2.2",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.4",
|
||||||
"react": "^19.1.0",
|
"react": "^19.0.0",
|
||||||
"react-device-detect": "^2.2.3",
|
"react-device-detect": "^2.2.3",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-intersection-observer": "^9.16.0",
|
"react-intersection-observer": "^9.15.1",
|
||||||
"react-wrap-balancer": "^1.1.1",
|
"react-wrap-balancer": "^1.1.1",
|
||||||
"recharts": "^2.15.3",
|
"recharts": "^2.15.1",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
"swr": "^2.3.3",
|
"swr": "^2.3.2",
|
||||||
"tailwind-merge": "^3.2.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "1.9.4",
|
||||||
"@next/bundle-analyzer": "^15.3.1",
|
"@next/bundle-analyzer": "^15.2.0",
|
||||||
"@tailwindcss/postcss": "^4.1.4",
|
"@tailwindcss/postcss": "^4.0.9",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.13.5",
|
||||||
"@types/react": "^19.1.2",
|
"@types/react": "^19.0.10",
|
||||||
"@types/react-dom": "^19.1.2",
|
"@types/react-dom": "^19.0.4",
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"tailwindcss": "^4.1.4",
|
"tailwindcss": "^4.0.9",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.7.3",
|
||||||
"vercel": "^41.6.2"
|
"vercel": "^39.4.2"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"react-is": "^19.0.0-rc-69d4b800-20241021"
|
"react-is": "^19.0.0-rc-69d4b800-20241021"
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 364 B |
Binary file not shown.
Before Width: | Height: | Size: 370 B |
Binary file not shown.
Before Width: | Height: | Size: 414 B |
Loading…
Reference in New Issue
Block a user