mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-19 14:04:08 +01:00
ui: add global 404, loading and error page
This commit is contained in:
parent
5f51b67d74
commit
0dddff308e
103
frontend/app/error.tsx
Normal file
103
frontend/app/error.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AlertTriangle, RefreshCcw, Home, ArrowLeft, HelpCircle } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
|
||||
interface ErrorProps {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
export default function Error({ error, reset }: ErrorProps) {
|
||||
useEffect(() => {
|
||||
// Log the error to an error reporting service
|
||||
console.error("Application error:", error);
|
||||
}, [error]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// Determine error type to show appropriate message
|
||||
const getErrorMessage = () => {
|
||||
if (error.message.includes("FARM_NOT_FOUND")) {
|
||||
return "The farm you're looking for could not be found.";
|
||||
}
|
||||
if (error.message.includes("CROP_NOT_FOUND")) {
|
||||
return "The crop you're looking for could not be found.";
|
||||
}
|
||||
if (error.message.includes("UNAUTHORIZED")) {
|
||||
return "You don't have permission to access this resource.";
|
||||
}
|
||||
if (error.message.includes("NETWORK")) {
|
||||
return "Network error. Please check your internet connection.";
|
||||
}
|
||||
return "We apologize for the inconvenience. An unexpected error has occurred.";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-red-50 to-white dark:from-red-950/30 dark:to-gray-950 flex flex-col items-center justify-center p-6">
|
||||
<div className="w-full max-w-lg text-center">
|
||||
<div className="relative mb-8">
|
||||
{/* Decorative elements */}
|
||||
<div className="absolute -top-16 -left-16 w-32 h-32 bg-red-100 dark:bg-red-900/30 rounded-full blur-3xl opacity-70"></div>
|
||||
<div className="absolute -bottom-8 -right-8 w-24 h-24 bg-red-200 dark:bg-red-800/30 rounded-full blur-2xl opacity-70"></div>
|
||||
|
||||
{/* Main icon */}
|
||||
<div className="relative bg-gradient-to-br from-red-100 to-red-200 dark:from-red-800 dark:to-red-900 h-24 w-24 rounded-full flex items-center justify-center mx-auto shadow-lg">
|
||||
<AlertTriangle className="h-12 w-12 text-red-600 dark:text-red-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl font-bold text-gray-800 dark:text-gray-100 mb-3">Something went wrong</h1>
|
||||
<p className="text-muted-foreground mb-6 max-w-md mx-auto">{getErrorMessage()}</p>
|
||||
|
||||
{error.message && !["FARM_NOT_FOUND", "CROP_NOT_FOUND", "UNAUTHORIZED"].includes(error.message) && (
|
||||
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-8 text-left">
|
||||
<p className="text-sm text-red-600 dark:text-red-400 font-medium mb-1">Error details:</p>
|
||||
<p className="text-sm text-muted-foreground">{error.message}</p>
|
||||
{error.digest && <p className="text-xs text-muted-foreground mt-2">Error ID: {error.digest}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-center mb-8">
|
||||
<Button
|
||||
variant="default"
|
||||
className="gap-2 bg-gradient-to-r from-green-600 to-emerald-500 hover:from-green-700 hover:to-emerald-600 h-12 px-6"
|
||||
onClick={() => reset()}>
|
||||
<RefreshCcw className="h-4 w-4" />
|
||||
Try Again
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2 h-12 border-green-200 dark:border-green-800"
|
||||
onClick={() => router.back()}>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
Go Back
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2 h-12 border-green-200 dark:border-green-800"
|
||||
onClick={() => router.push("/")}>
|
||||
<Home className="h-4 w-4" />
|
||||
Home
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<p className="mb-2">
|
||||
Need help?{" "}
|
||||
<Link href="/contact" className="text-green-600 hover:underline">
|
||||
Contact Support
|
||||
</Link>
|
||||
</p>
|
||||
<p className="flex items-center justify-center gap-1">
|
||||
<HelpCircle className="h-3 w-3" />
|
||||
<span>Support Code: {error.digest ? error.digest.substring(0, 8) : "Unknown"}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
51
frontend/app/global-error.tsx
Normal file
51
frontend/app/global-error.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AlertTriangle, RefreshCcw, Home } from "lucide-react";
|
||||
|
||||
interface GlobalErrorProps {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
export default function GlobalError({ error, reset }: GlobalErrorProps) {
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
<div className="min-h-screen bg-gradient-to-b from-red-50 to-white flex flex-col items-center justify-center p-6">
|
||||
<div className="w-full max-w-md text-center">
|
||||
<div className="bg-red-100 h-24 w-24 rounded-full flex items-center justify-center mx-auto mb-8">
|
||||
<AlertTriangle className="h-12 w-12 text-red-600" />
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl font-bold mb-3">Critical Error</h1>
|
||||
<p className="text-gray-600 mb-6">The application has encountered a critical error and cannot continue.</p>
|
||||
|
||||
{error.message && (
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-8 text-left">
|
||||
<p className="text-sm text-red-600 font-medium mb-1">Error details:</p>
|
||||
<p className="text-sm text-gray-600">{error.message}</p>
|
||||
{error.digest && <p className="text-xs text-gray-500 mt-2">Error ID: {error.digest}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-center">
|
||||
<Button
|
||||
className="gap-2 bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded"
|
||||
onClick={() => reset()}>
|
||||
<RefreshCcw className="h-4 w-4" />
|
||||
Restart Application
|
||||
</Button>
|
||||
<Button
|
||||
className="gap-2 bg-white border border-gray-300 text-gray-700 py-2 px-4 rounded"
|
||||
onClick={() => (window.location.href = "/")}>
|
||||
<Home className="h-4 w-4" />
|
||||
Return to Home
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-family: var(--font-poppins);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Open_Sans, Roboto_Mono } from "next/font/google";
|
||||
import { Poppins } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
|
||||
import { SessionProvider } from "@/context/SessionContext";
|
||||
|
||||
const openSans = Open_Sans({
|
||||
const poppins = Poppins({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-opensans",
|
||||
});
|
||||
|
||||
const robotoMono = Roboto_Mono({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-roboto-mono",
|
||||
variable: "--font-poppins",
|
||||
weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"],
|
||||
});
|
||||
|
||||
// const geistMono = Geist_Mono({
|
||||
@ -36,7 +31,7 @@ export default function RootLayout({
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<head />
|
||||
<SessionProvider>
|
||||
<body className={`${openSans.variable} ${robotoMono.variable} antialiased`}>
|
||||
<body className={`${poppins.variable}`}>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<div className="relative flex min-h-screen flex-col">
|
||||
<div className="flex-1 bg-background">{children}</div>
|
||||
|
||||
27
frontend/app/loading.tsx
Normal file
27
frontend/app/loading.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { Leaf } from "lucide-react";
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-green-50/50 to-white flex flex-col items-center justify-center p-6">
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<div className="relative">
|
||||
<div className="h-20 w-20 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<Leaf className="h-10 w-10 text-green-600" />
|
||||
</div>
|
||||
<div className="absolute inset-0 rounded-full border-4 border-t-green-600 border-r-transparent border-b-transparent border-l-transparent animate-spin"></div>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<h2 className="text-xl font-semibold text-green-800 mb-2">Loading...</h2>
|
||||
<p className="text-muted-foreground max-w-md">
|
||||
We're preparing your farming data. This will only take a moment.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-64 h-2 bg-green-100 rounded-full overflow-hidden">
|
||||
<div className="h-full bg-green-600 rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
92
frontend/app/not-found.tsx
Normal file
92
frontend/app/not-found.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
"use client";
|
||||
|
||||
import type React from "react";
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Leaf, Home, Search, ArrowLeft, MapPin } from "lucide-react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function NotFound() {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const router = useRouter();
|
||||
|
||||
const handleSearch = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (searchQuery.trim()) {
|
||||
// In a real app, this would navigate to search results
|
||||
router.push(`/search?q=${encodeURIComponent(searchQuery)}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-green-50 to-white dark:from-green-950 dark:to-gray-950 flex flex-col items-center justify-center p-6">
|
||||
<div className="w-full max-w-lg text-center">
|
||||
<div className="relative mb-8">
|
||||
{/* Decorative elements */}
|
||||
<div className="absolute -top-16 -left-16 w-32 h-32 bg-green-100 dark:bg-green-900/30 rounded-full blur-3xl opacity-70"></div>
|
||||
<div className="absolute -bottom-8 -right-8 w-24 h-24 bg-green-200 dark:bg-green-800/30 rounded-full blur-2xl opacity-70"></div>
|
||||
|
||||
{/* Main icon */}
|
||||
<div className="relative bg-gradient-to-br from-green-100 to-green-200 dark:from-green-800 dark:to-green-900 h-24 w-24 rounded-full flex items-center justify-center mx-auto shadow-lg">
|
||||
<Leaf className="h-12 w-12 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-7xl font-bold bg-gradient-to-r from-green-600 to-emerald-500 dark:from-green-400 dark:to-emerald-300 bg-clip-text text-transparent mb-4">
|
||||
404
|
||||
</h1>
|
||||
<h2 className="text-3xl font-bold mb-3 text-gray-800 dark:text-gray-100">Page Not Found</h2>
|
||||
<p className="text-muted-foreground mb-8 max-w-md mx-auto">
|
||||
Looks like you've wandered into uncharted territory. This page doesn't exist or has been moved.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col gap-6 mb-8">
|
||||
<form onSubmit={handleSearch} className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search for farms, crops, or pages..."
|
||||
className="pl-10 h-12 bg-white/80 dark:bg-gray-900/80 backdrop-blur-sm"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</form>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-center">
|
||||
<Button
|
||||
variant="default"
|
||||
className="gap-2 bg-gradient-to-r from-green-600 to-emerald-500 hover:from-green-700 hover:to-emerald-600 h-12 px-6"
|
||||
onClick={() => router.push("/")}>
|
||||
<Home className="h-4 w-4" />
|
||||
Return Home
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2 h-12 border-green-200 dark:border-green-800"
|
||||
onClick={() => router.back()}>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
Go Back
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-6 justify-center text-sm text-muted-foreground">
|
||||
<Link href="/farms" className="flex items-center gap-1 hover:text-green-600 transition-colors">
|
||||
<MapPin className="h-3 w-3" />
|
||||
View Farms
|
||||
</Link>
|
||||
<Link href="/knowledge-hub" className="flex items-center gap-1 hover:text-green-600 transition-colors">
|
||||
<Leaf className="h-3 w-3" />
|
||||
Knowledge Hub
|
||||
</Link>
|
||||
<Link href="/contact" className="flex items-center gap-1 hover:text-green-600 transition-colors">
|
||||
<Search className="h-3 w-3" />
|
||||
Contact Support
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
59
frontend/components/ui/alert.tsx
Normal file
59
frontend/components/ui/alert.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Alert.displayName = "Alert"
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertTitle.displayName = "AlertTitle"
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDescription.displayName = "AlertDescription"
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
||||
29
frontend/components/ui/hover-card.tsx
Normal file
29
frontend/components/ui/hover-card.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const HoverCard = HoverCardPrimitive.Root
|
||||
|
||||
const HoverCardTrigger = HoverCardPrimitive.Trigger
|
||||
|
||||
const HoverCardContent = React.forwardRef<
|
||||
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||
<HoverCardPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
|
||||
|
||||
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
||||
@ -2,6 +2,9 @@ import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
devIndicators: {
|
||||
buildActivity: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"@radix-ui/react-collapsible": "^1.1.3",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
||||
"@radix-ui/react-hover-card": "^1.1.6",
|
||||
"@radix-ui/react-label": "^2.1.2",
|
||||
"@radix-ui/react-popover": "^1.1.6",
|
||||
"@radix-ui/react-progress": "^1.1.2",
|
||||
@ -33,6 +34,7 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"framer-motion": "^12.4.10",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.475.0",
|
||||
"next": "15.1.0",
|
||||
|
||||
@ -26,6 +26,9 @@ importers:
|
||||
'@radix-ui/react-dropdown-menu':
|
||||
specifier: ^2.1.6
|
||||
version: 2.1.6(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-hover-card':
|
||||
specifier: ^1.1.6
|
||||
version: 1.1.6(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-label':
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
@ -80,6 +83,9 @@ importers:
|
||||
date-fns:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
framer-motion:
|
||||
specifier: ^12.4.10
|
||||
version: 12.4.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
js-cookie:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
@ -605,6 +611,19 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-hover-card@1.1.6':
|
||||
resolution: {integrity: sha512-E4ozl35jq0VRlrdc4dhHrNSV0JqBb4Jy73WAhBEK7JoYnQ83ED5r0Rb/XdVKw89ReAJN38N492BAPBZQ57VmqQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-id@1.1.0':
|
||||
resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
|
||||
peerDependencies:
|
||||
@ -1510,6 +1529,20 @@ packages:
|
||||
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
framer-motion@12.4.10:
|
||||
resolution: {integrity: sha512-3Msuyjcr1Pb5hjkn4EJcRe1HumaveP0Gbv4DBMKTPKcV/1GSMkQXj+Uqgneys+9DPcZM18Hac9qY9iUEF5LZtg==}
|
||||
peerDependencies:
|
||||
'@emotion/is-prop-valid': '*'
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/is-prop-valid':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
@ -1865,6 +1898,12 @@ packages:
|
||||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
motion-dom@12.4.10:
|
||||
resolution: {integrity: sha512-ISP5u6FTceoD6qKdLupIPU/LyXBrxGox+P2e3mBbm1+pLdlBbwv01YENJr7+1WZnW5ucVKzFScYsV1eXTCG4Xg==}
|
||||
|
||||
motion-utils@12.4.10:
|
||||
resolution: {integrity: sha512-NPwZd94V013SwRf++jMrk2+HEBgPkeIE2RiOzhAuuQlqxMJPkKt/LXVh6Upl+iN8oarSGD2dlY5/bqgsYXDABA==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@ -2918,6 +2957,23 @@ snapshots:
|
||||
'@types/react': 19.0.8
|
||||
'@types/react-dom': 19.0.3(@types/react@19.0.8)
|
||||
|
||||
'@radix-ui/react-hover-card@1.1.6(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.1
|
||||
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.8)(react@19.0.0)
|
||||
'@radix-ui/react-context': 1.1.1(@types/react@19.0.8)(react@19.0.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.3(@types/react@19.0.8))(@types/react@19.0.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.8)(react@19.0.0)
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.8
|
||||
'@types/react-dom': 19.0.3(@types/react@19.0.8)
|
||||
|
||||
'@radix-ui/react-id@1.1.0(@types/react@19.0.8)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.8)(react@19.0.0)
|
||||
@ -4033,6 +4089,15 @@ snapshots:
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
|
||||
framer-motion@12.4.10(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
motion-dom: 12.4.10
|
||||
motion-utils: 12.4.10
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
@ -4388,6 +4453,12 @@ snapshots:
|
||||
|
||||
minipass@7.1.2: {}
|
||||
|
||||
motion-dom@12.4.10:
|
||||
dependencies:
|
||||
motion-utils: 12.4.10
|
||||
|
||||
motion-utils@12.4.10: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
mz@2.7.0:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user