Compare commits

...

6 Commits

Author SHA1 Message Date
iamkiddy
c80c05e74d Refactor Navbar component to use linear gradient classes for improved styling consistency. Update background gradient classes for both the heart icon and the title text. 2025-11-07 13:46:06 +00:00
iamkiddy
ce73cbde6c Enhance admin panel with new features and UI improvements. Add remote image patterns to Next.js config for Unsplash integration. Update package.json with new dependencies including Radix UI components and date-fns. Refactor layout components to include a header in the admin layout, improve styling in notifications and side navigation, and enhance the booking and dashboard pages for better user experience. 2025-11-07 13:45:14 +00:00
iamkiddy
7a6a2a3a20 Implement booking management features in the admin panel. Add booking data fetching with mock API response, search functionality, and display of booking details in a table format. Enhance dashboard with statistics overview and loading states. Update layout for improved user experience. 2025-11-07 11:53:00 +00:00
iamkiddy
c9440d3924 Merge branch 'frontend/website' of http://35.207.46.142/ATTUNE-HEART-THERAPY/website into feat/authentication 2025-11-07 11:52:51 +00:00
abubakarim78
f3fb1100ca Advance the website with images and possibly some image of the client 2025-11-06 22:44:29 +00:00
iamkiddy
3c27026371 Refactor SideNav component for improved styling and responsiveness. Adjust layout in Booking and Dashboard pages to match updated SideNav dimensions. Enhance Login page with gradient text and button styles for a more modern appearance. 2025-11-06 14:40:30 +00:00
26 changed files with 2651 additions and 172 deletions

View File

@ -0,0 +1,211 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Inbox,
Calendar,
LayoutGrid,
Heart,
UserCog,
Bell,
Settings,
LogOut,
} from "lucide-react";
export function Header() {
const pathname = usePathname();
const router = useRouter();
const [notificationsOpen, setNotificationsOpen] = useState(false);
const [userMenuOpen, setUserMenuOpen] = useState(false);
// Mock notifications data
const notifications = [
{
id: 1,
title: "New appointment scheduled",
message: "John Smith booked an appointment for tomorrow",
time: "2 hours ago",
type: "info",
read: false,
},
{
id: 2,
title: "Payment received",
message: "Payment of $150 received from Jane Doe",
time: "5 hours ago",
type: "success",
read: false,
},
];
const unreadCount = notifications.filter((n) => !n.read).length;
return (
<header className="bg-white border-b border-gray-200 fixed top-0 left-0 right-0 z-50">
<div className="px-3 sm:px-4 md:px-6 lg:px-8">
<div className="flex items-center justify-between h-14 sm:h-16">
{/* Logo */}
<Link href="/dashboard" className="flex items-center gap-2 sm:gap-3">
<div className="flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-linear-to-r from-rose-100 to-pink-100">
<Heart className="w-4 h-4 sm:w-6 sm:h-6 text-rose-600" fill="currentColor" />
</div>
<span className="text-base sm:text-lg md:text-xl font-semibold text-gray-900 hidden sm:inline">Attune Heart</span>
</Link>
{/* Navigation Links */}
<nav className="flex items-center gap-0.5 sm:gap-1">
<Link
href="/dashboard"
className={`flex items-center gap-1 sm:gap-2 px-2 sm:px-3 md:px-4 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${
pathname === "/dashboard"
? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
: "text-gray-600 hover:bg-gray-100"
}`}
>
<LayoutGrid className="w-4 h-4 sm:w-5 sm:h-5" />
<span className="hidden sm:inline">Dashboard</span>
</Link>
<Link
href="/booking"
className={`flex items-center gap-1 sm:gap-2 px-2 sm:px-3 md:px-4 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${
pathname === "/booking"
? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
: "text-gray-600 hover:bg-gray-100"
}`}
>
<Calendar className="w-4 h-4 sm:w-5 sm:h-5" />
<span className="hidden sm:inline">Book Appointment</span>
</Link>
</nav>
{/* Right Side Actions */}
<div className="flex items-center gap-1.5 sm:gap-2 md:gap-3">
<Popover open={notificationsOpen} onOpenChange={setNotificationsOpen}>
<PopoverTrigger asChild>
<Button variant="ghost" size="icon" className="relative w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10 cursor-pointer">
<Inbox className="w-5 h-5 sm:w-6 sm:h-6 md:w-8 md:h-8 text-gray-600" />
{unreadCount > 0 && (
<span className="absolute top-0.5 right-0.5 sm:top-1 sm:right-1 w-1.5 h-1.5 sm:w-2 sm:h-2 bg-green-500 rounded-full"></span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-[calc(100vw-2rem)] sm:w-80 md:w-96 p-0 bg-white shadow-xl" align="end">
{/* Thumbtack Design at Top Right */}
<div className="relative">
<div className="absolute -top-2 right-8 w-4 h-4 bg-white border-l border-t border-gray-200 rotate-45"></div>
<div className="absolute -top-1 right-8 w-2 h-2 bg-white translate-x-1/2"></div>
</div>
<div className="flex items-center justify-between p-4 border-b">
<h3 className="font-semibold text-gray-900">Notifications</h3>
{unreadCount > 0 && (
<span className="px-2 py-1 text-xs font-medium bg-rose-100 text-rose-700 rounded-full">
{unreadCount} new
</span>
)}
</div>
<div className="max-h-96 overflow-y-auto">
{notifications.length === 0 ? (
<div className="p-8 text-center text-gray-500">
<Bell className="w-12 h-12 mx-auto mb-2 text-gray-300" />
<p className="text-sm">No notifications</p>
</div>
) : (
<div className="divide-y">
{notifications.map((notification) => {
return (
<div
key={notification.id}
className={`p-4 hover:bg-gray-50 transition-colors cursor-pointer ${
!notification.read ? "bg-rose-50/50" : ""
}`}
>
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2">
<p
className={`text-sm font-medium text-gray-900 ${
!notification.read ? "font-semibold" : ""
}`}
>
{notification.title}
</p>
{!notification.read && (
<span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span>
)}
</div>
<p className="text-sm text-gray-600 mt-1">
{notification.message}
</p>
<p className="text-xs text-gray-400 mt-1">
{notification.time}
</p>
</div>
</div>
);
})}
</div>
)}
</div>
<div className="p-3 border-t bg-gray-50">
<Link
href="/notifications"
onClick={() => setNotificationsOpen(false)}
className="block w-full text-center text-sm font-medium text-rose-600 hover:text-rose-700 hover:underline transition-colors"
>
View all notifications
</Link>
</div>
</PopoverContent>
</Popover>
<Popover open={userMenuOpen} onOpenChange={setUserMenuOpen}>
<PopoverTrigger asChild>
<Button variant="ghost" size="icon" className="rounded-full bg-linear-to-r from-rose-100 to-pink-100 hover:from-rose-200 hover:to-pink-200 cursor-pointer w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10">
<UserCog className="w-4 h-4 sm:w-5 sm:h-5 text-rose-600" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-56 sm:w-64 p-0 bg-white shadow-xl" align="end">
{/* Thumbtack Design at Top Right */}
<div className="relative">
<div className="absolute -top-2 right-8 w-4 h-4 bg-white border-l border-t border-gray-200 rotate-45"></div>
<div className="absolute -top-1 right-8 w-2 h-2 bg-white translate-x-1/2"></div>
</div>
<div className="py-2">
<Button
variant="ghost"
onClick={() => {
setUserMenuOpen(false);
// Add settings navigation here
}}
className="w-full flex items-center gap-3 px-4 py-3 justify-start hover:bg-gray-50 transition-colors cursor-pointer"
>
<Settings className="w-5 h-5 text-gray-600" />
<span className="text-sm font-medium text-gray-900">Settings</span>
</Button>
<Button
variant="ghost"
onClick={() => {
setUserMenuOpen(false);
router.push("/");
}}
className="w-full flex items-center gap-3 px-4 py-3 justify-start hover:bg-gray-50 transition-colors cursor-pointer"
>
<LogOut className="w-5 h-5 text-red-500" />
<span className="text-sm font-medium text-red-500">Logout</span>
</Button>
</div>
</PopoverContent>
</Popover>
</div>
</div>
</div>
</header>
);
}

View File

@ -71,7 +71,7 @@ export function Notifications({
<Bell className="w-6 h-6 text-gray-900" /> <Bell className="w-6 h-6 text-gray-900" />
<h2 className="text-2xl font-bold text-gray-900">Notifications</h2> <h2 className="text-2xl font-bold text-gray-900">Notifications</h2>
{unreadCount > 0 && ( {unreadCount > 0 && (
<span className="px-2.5 py-0.5 bg-gradient-to-r from-rose-500 to-pink-500 text-white text-sm font-medium rounded-full"> <span className="px-2.5 py-0.5 bg-linear-to-r from-rose-500 to-pink-500 text-white text-sm font-medium rounded-full">
{unreadCount} {unreadCount}
</span> </span>
)} )}

View File

@ -1,8 +1,9 @@
"use client"; "use client";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { usePathname } from "next/navigation"; import { usePathname, useRouter } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import { Button } from "@/components/ui/button";
import { import {
LayoutGrid, LayoutGrid,
Calendar, Calendar,
@ -21,6 +22,7 @@ const navItems = [
export default function SideNav() { export default function SideNav() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const pathname = usePathname(); const pathname = usePathname();
const router = useRouter();
const getActiveIndex = () => { const getActiveIndex = () => {
return navItems.findIndex((item) => pathname?.includes(item.href)) ?? -1; return navItems.findIndex((item) => pathname?.includes(item.href)) ?? -1;
@ -43,14 +45,19 @@ export default function SideNav() {
{/* Mobile Top Bar */} {/* Mobile Top Bar */}
<div className="flex md:hidden items-center justify-between px-4 py-3 border-b border-gray-200 bg-white z-30 fixed top-0 left-0 right-0"> <div className="flex md:hidden items-center justify-between px-4 py-3 border-b border-gray-200 bg-white z-30 fixed top-0 left-0 right-0">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-gradient-to-br from-rose-100 to-pink-100"> <div className="flex items-center justify-center w-8 h-8 rounded-lg bg-linear-to-r from-rose-100 to-pink-100">
<Heart className="w-5 h-5 text-rose-600" fill="currentColor" /> <Heart className="w-5 h-5 text-rose-600" fill="currentColor" />
</div> </div>
<span className="text-lg font-semibold text-gray-900">Attune Heart Therapy</span> <span className="text-lg font-semibold text-gray-900">Attune Heart Therapy</span>
</div> </div>
<button onClick={() => setOpen((v) => !v)} aria-label="Open menu"> <Button
variant="ghost"
size="icon"
onClick={() => setOpen((v) => !v)}
aria-label="Open menu"
>
{open ? <X size={28} /> : <Menu size={28} />} {open ? <X size={28} /> : <Menu size={28} />}
</button> </Button>
</div> </div>
{/* Mobile Drawer Overlay */} {/* Mobile Drawer Overlay */}
@ -63,21 +70,21 @@ export default function SideNav() {
{/* Side Navigation */} {/* Side Navigation */}
<aside <aside
className={`fixed top-0 left-0 z-50 h-screen bg-white border-r border-gray-200 flex flex-col transition-transform duration-200 w-[85vw] max-w-[250px] min-w-[200px] md:translate-x-0 md:w-[250px] md:min-w-[250px] md:max-w-[250px] ${ className={`fixed top-0 left-0 z-50 h-screen bg-white border-r border-gray-200 flex flex-col transition-transform duration-200 w-[85vw] max-w-[200px] min-w-[160px] md:translate-x-0 md:w-[200px] md:min-w-[200px] md:max-w-[200px] ${
open ? "translate-x-0" : "-translate-x-full" open ? "translate-x-0" : "-translate-x-full"
} md:translate-x-0`} } md:translate-x-0`}
> >
{/* Logo Section */} {/* Logo Section */}
<div className="flex-shrink-0 px-4 pb-4 flex flex-col gap-1 md:block mb-5 pt-16 md:pt-4"> <div className="shrink-0 px-3 pb-4 flex flex-col gap-1 md:block mb-5 pt-16 md:pt-4">
<div className="flex items-center gap-3 mb-1 ml-4 md:ml-6"> <div className="flex items-center gap-2 mb-1 ml-2 md:ml-3">
<div className="flex items-center justify-center w-10 h-10 rounded-lg bg-gradient-to-br from-rose-100 to-pink-100"> <div className="flex items-center justify-center w-8 h-8 rounded-lg bg-linear-to-r from-rose-100 to-pink-100">
<Heart className="w-6 h-6 text-rose-600" fill="currentColor" /> <Heart className="w-5 h-5 text-rose-600" fill="currentColor" />
</div> </div>
<span className="text-lg font-semibold text-gray-900">Attune Heart</span> <span className="text-sm font-semibold text-gray-900">Attune Heart</span>
</div> </div>
</div> </div>
<hr className="flex-shrink-0 -mt-10 mb-4 mx-4 border-gray-200 md:block hidden" /> <hr className="shrink-0 -mt-10 mb-4 mx-3 border-gray-200 md:block hidden" />
{/* Navigation Items */} {/* Navigation Items */}
<nav className="flex-1 overflow-y-auto flex flex-col gap-2 px-2 md:px-0"> <nav className="flex-1 overflow-y-auto flex flex-col gap-2 px-2 md:px-0">
@ -89,31 +96,31 @@ export default function SideNav() {
<div className="relative flex items-center w-full" key={item.label}> <div className="relative flex items-center w-full" key={item.label}>
{isActive && ( {isActive && (
<span <span
className="absolute left-0 top-0 h-[45px] w-[3px] bg-[#4A90A4]" className="absolute left-0 top-0 h-[40px] w-[3px] bg-linear-to-r from-rose-500 to-pink-600"
style={{ left: 0 }} style={{ left: 0 }}
/> />
)} )}
<Link <Link
href={item.href} href={item.href}
onClick={() => setOpen(false)} onClick={() => setOpen(false)}
className={`group flex items-center gap-3 py-3 pl-4 md:pl-4 pr-4 md:pr-4 transition-colors duration-200 focus:outline-none w-[90%] md:w-[90%] ml-2 md:ml-4 cursor-pointer justify-start ${ className={`group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 focus:outline-none w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start ${
isActive isActive
? "bg-[#4A90A4] text-white border border-[#4A90A4] rounded-[5px] shadow-sm" ? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm"
: "bg-transparent text-gray-600 hover:bg-[#4A90A4]/10 hover:text-[#4A90A4] rounded-lg" : "bg-transparent text-gray-600 hover:bg-rose-50 hover:text-rose-600 rounded-lg"
}`} }`}
style={isActive ? { height: 45 } : {}} style={isActive ? { height: 40 } : {}}
> >
<Icon <Icon
size={20} size={18}
strokeWidth={isActive ? 2.2 : 1.5} strokeWidth={isActive ? 2.2 : 1.5}
className={ className={
isActive isActive
? "text-white" ? "text-white"
: "text-gray-700 group-hover:text-[#4A90A4]" : "text-gray-700 group-hover:text-rose-600"
} }
/> />
<span <span
className="font-light leading-none text-[13px] md:text-[13px]" className="font-light leading-none text-[12px] md:text-[12px]"
style={{ fontWeight: 300 }} style={{ fontWeight: 300 }}
> >
{item.label} {item.label}
@ -128,25 +135,26 @@ export default function SideNav() {
<Link <Link
href="/settings" href="/settings"
onClick={() => setOpen(false)} onClick={() => setOpen(false)}
className="group flex items-center gap-3 py-3 pl-4 md:pl-4 pr-4 md:pr-4 transition-colors duration-200 w-[90%] md:w-[90%] ml-2 md:ml-4 cursor-pointer justify-start text-gray-600 hover:bg-gray-50 hover:text-gray-900 rounded-lg" className="group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start text-gray-600 hover:bg-gray-50 hover:text-gray-900 rounded-lg"
> >
<Settings size={20} strokeWidth={1.5} className="text-gray-700 group-hover:text-gray-900" /> <Settings size={18} strokeWidth={1.5} className="text-gray-700 group-hover:text-gray-900" />
<span className="font-light leading-none text-[13px]" style={{ fontWeight: 300 }}> <span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
Settings Settings
</span> </span>
</Link> </Link>
<button <Button
variant="ghost"
onClick={() => { onClick={() => {
setOpen(false); setOpen(false);
// Handle logout router.push("/");
}} }}
className="group flex items-center gap-3 py-3 pl-4 md:pl-4 pr-4 md:pr-4 transition-colors duration-200 w-[90%] md:w-[90%] ml-2 md:ml-4 cursor-pointer justify-start text-gray-600 hover:bg-gray-50 hover:text-gray-900 rounded-lg" className="group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start text-gray-600 hover:bg-gray-50 hover:text-gray-900 rounded-lg"
> >
<LogOut size={20} strokeWidth={1.5} className="text-gray-700 group-hover:text-gray-900" /> <LogOut size={18} strokeWidth={1.5} className="text-gray-700 group-hover:text-gray-900" />
<span className="font-light leading-none text-[13px]" style={{ fontWeight: 300 }}> <span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
Logout Logout
</span> </span>
</button> </Button>
</div> </div>
</nav> </nav>
</aside> </aside>

View File

@ -1,17 +1,324 @@
"use client"; "use client";
import SideNav from "@/app/(admin)/_components/side-nav"; import { useState, useEffect } from "react";
import {
Calendar,
Clock,
User,
Video,
FileText,
MoreVertical,
} from "lucide-react";
interface User {
ID: number;
CreatedAt?: string;
UpdatedAt?: string;
DeletedAt?: string | null;
first_name: string;
last_name: string;
email: string;
phone: string;
location: string;
date_of_birth?: string;
is_admin?: boolean;
bookings?: null;
}
interface Booking {
ID: number;
CreatedAt: string;
UpdatedAt: string;
DeletedAt: string | null;
user_id: number;
user: User;
scheduled_at: string;
duration: number;
status: string;
jitsi_room_id: string;
jitsi_room_url: string;
payment_id: string;
payment_status: string;
amount: number;
notes: string;
}
interface BookingsResponse {
bookings: Booking[];
limit: number;
offset: number;
total: number;
}
export default function Booking() { export default function Booking() {
const [bookings, setBookings] = useState<Booking[]>([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState("");
useEffect(() => {
// Simulate API call
const fetchBookings = async () => {
setLoading(true);
await new Promise((resolve) => setTimeout(resolve, 500));
// Mock API response
const mockData: BookingsResponse = {
bookings: [
{
ID: 1,
CreatedAt: "2025-11-06T11:33:45.704633Z",
UpdatedAt: "2025-11-06T11:33:45.707543Z",
DeletedAt: null,
user_id: 3,
user: {
ID: 3,
CreatedAt: "2025-11-06T10:43:01.299311Z",
UpdatedAt: "2025-11-06T10:43:48.427284Z",
DeletedAt: null,
first_name: "John",
last_name: "Smith",
email: "john.doe@example.com",
phone: "+1234567891",
location: "Los Angeles, CA",
date_of_birth: "0001-01-01T00:00:00Z",
is_admin: true,
bookings: null,
},
scheduled_at: "2025-11-07T10:00:00Z",
duration: 60,
status: "scheduled",
jitsi_room_id: "booking-1-1762428825-22c92ced2870c17c",
jitsi_room_url:
"https://meet.jit.si/booking-1-1762428825-22c92ced2870c17c",
payment_id: "",
payment_status: "pending",
amount: 52,
notes: "Initial consultation session",
},
],
limit: 50,
offset: 0,
total: 1,
};
setBookings(mockData.bookings);
setLoading(false);
};
fetchBookings();
}, []);
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
};
const formatTime = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
hour12: true,
});
};
const getStatusColor = (status: string) => {
switch (status.toLowerCase()) {
case "scheduled":
return "bg-blue-100 text-blue-700";
case "completed":
return "bg-green-100 text-green-700";
case "cancelled":
return "bg-red-100 text-red-700";
case "pending":
return "bg-yellow-100 text-yellow-700";
default:
return "bg-gray-100 text-gray-700";
}
};
const getPaymentStatusColor = (status: string) => {
switch (status.toLowerCase()) {
case "paid":
return "bg-green-100 text-green-700";
case "pending":
return "bg-yellow-100 text-yellow-700";
case "failed":
return "bg-red-100 text-red-700";
default:
return "bg-gray-100 text-gray-700";
}
};
const filteredBookings = bookings.filter(
(booking) =>
booking.user.first_name
.toLowerCase()
.includes(searchTerm.toLowerCase()) ||
booking.user.last_name
.toLowerCase()
.includes(searchTerm.toLowerCase()) ||
booking.user.email.toLowerCase().includes(searchTerm.toLowerCase())
);
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
{/* Side Navigation */}
<SideNav />
<div className="md:ml-[250px]"> {/* Main Content */}
<main className="p-4 sm:p-6 lg:p-8"> <main className="p-3 sm:p-4 md:p-6 lg:p-8">
</main> {/* Page Header */}
<div className="mb-4 sm:mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<div>
<h1 className="text-xl sm:text-2xl font-semibold text-gray-900 mb-1">
Bookings
</h1>
<p className="text-xs sm:text-sm text-gray-500">
Manage and view all appointment bookings
</p>
</div> </div>
<button className="w-full sm:w-auto px-3 sm:px-4 py-2 bg-gray-900 text-white rounded-lg text-xs sm:text-sm font-medium hover:bg-gray-800 transition-colors">
+ New Booking
</button>
</div>
{loading ? (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-400"></div>
</div>
) : filteredBookings.length === 0 ? (
<div className="bg-white rounded-lg border border-gray-200 p-12 text-center">
<Calendar className="w-12 h-12 text-gray-400 mx-auto mb-4" />
<p className="text-gray-600 font-medium mb-1">No bookings found</p>
<p className="text-sm text-gray-500">
{searchTerm
? "Try adjusting your search terms"
: "Create a new booking to get started"}
</p>
</div>
) : (
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Patient
</th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden md:table-cell">
Date & Time
</th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden lg:table-cell">
Duration
</th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden lg:table-cell">
Payment
</th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden xl:table-cell">
Amount
</th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredBookings.map((booking) => (
<tr
key={booking.ID}
className="hover:bg-gray-50 transition-colors"
>
<td className="px-3 sm:px-4 md:px-6 py-4">
<div className="flex items-center">
<div className="shrink-0 h-8 w-8 sm:h-10 sm:w-10 rounded-full bg-gray-100 flex items-center justify-center">
<User className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" />
</div>
<div className="ml-2 sm:ml-4 min-w-0">
<div className="text-xs sm:text-sm font-medium text-gray-900 truncate">
{booking.user.first_name} {booking.user.last_name}
</div>
<div className="text-xs sm:text-sm text-gray-500 truncate hidden sm:block">
{booking.user.email}
</div>
<div className="text-xs text-gray-500 sm:hidden mt-0.5">
{formatDate(booking.scheduled_at)}
</div>
</div>
</div>
</td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap hidden md:table-cell">
<div className="text-xs sm:text-sm text-gray-900">
{formatDate(booking.scheduled_at)}
</div>
<div className="text-xs sm:text-sm text-gray-500 flex items-center gap-1">
<Clock className="w-3 h-3" />
{formatTime(booking.scheduled_at)}
</div>
</td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-xs sm:text-sm text-gray-900 hidden lg:table-cell">
{booking.duration} min
</td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap">
<span
className={`px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${getStatusColor(
booking.status
)}`}
>
{booking.status}
</span>
</td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap hidden lg:table-cell">
<span
className={`px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${getPaymentStatusColor(
booking.payment_status
)}`}
>
{booking.payment_status}
</span>
</td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-xs sm:text-sm font-medium text-gray-900 hidden xl:table-cell">
${booking.amount}
</td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-1 sm:gap-2">
{booking.jitsi_room_url && (
<a
href={booking.jitsi_room_url}
target="_blank"
rel="noopener noreferrer"
className="p-1.5 sm:p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"
title="Join Meeting"
>
<Video className="w-4 h-4" />
</a>
)}
{booking.notes && (
<button
className="p-1.5 sm:p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"
title="View Notes"
>
<FileText className="w-4 h-4" />
</button>
)}
<button className="p-1.5 sm:p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors">
<MoreVertical className="w-4 h-4" />
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</main>
</div> </div>
); );
} }

View File

@ -1,19 +1,206 @@
"use client"; "use client";
import SideNav from "@/app/(admin)/_components/side-nav"; import { useState, useEffect } from "react";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Users,
UserCheck,
Calendar,
CalendarCheck,
CalendarX,
DollarSign,
TrendingUp,
ArrowUpRight,
ArrowDownRight,
} from "lucide-react";
interface DashboardStats {
total_users: number;
active_users: number;
total_bookings: number;
upcoming_bookings: number;
completed_bookings: number;
cancelled_bookings: number;
total_revenue: number;
monthly_revenue: number;
}
export default function Dashboard() { export default function Dashboard() {
const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true);
const [timePeriod, setTimePeriod] = useState<string>("last_month");
useEffect(() => {
// Simulate API call
const fetchStats = async () => {
setLoading(true);
// Simulate network delay
await new Promise((resolve) => setTimeout(resolve, 500));
// Mock API response
const mockData: DashboardStats = {
total_users: 3,
active_users: 3,
total_bookings: 6,
upcoming_bookings: 6,
completed_bookings: 0,
cancelled_bookings: 0,
total_revenue: 0,
monthly_revenue: 0,
};
setStats(mockData);
setLoading(false);
};
fetchStats();
}, []);
const statCards = [
{
title: "Total Users",
value: stats?.total_users ?? 0,
icon: Users,
trend: "+12%",
trendUp: true,
},
{
title: "Active Users",
value: stats?.active_users ?? 0,
icon: UserCheck,
trend: "+8%",
trendUp: true,
},
{
title: "Total Bookings",
value: stats?.total_bookings ?? 0,
icon: Calendar,
trend: "+24%",
trendUp: true,
},
{
title: "Upcoming Bookings",
value: stats?.upcoming_bookings ?? 0,
icon: CalendarCheck,
trend: "+6",
trendUp: true,
},
{
title: "Completed Bookings",
value: stats?.completed_bookings ?? 0,
icon: CalendarCheck,
trend: "0%",
trendUp: true,
},
{
title: "Cancelled Bookings",
value: stats?.cancelled_bookings ?? 0,
icon: CalendarX,
trend: "0%",
trendUp: false,
},
{
title: "Total Revenue",
value: `$${stats?.total_revenue.toLocaleString() ?? 0}`,
icon: DollarSign,
trend: "+18%",
trendUp: true,
},
{
title: "Monthly Revenue",
value: `$${stats?.monthly_revenue.toLocaleString() ?? 0}`,
icon: TrendingUp,
trend: "+32%",
trendUp: true,
},
];
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
{/* Side Navigation */}
<SideNav />
<div className="md:ml-[250px]">
{/* Main Content */} {/* Main Content */}
<main className="p-4 sm:p-6 lg:p-8"> <main className="p-4 sm:p-6 lg:p-8 space-y-6">
<h1 className="text-3xl font-bold text-gray-900">Dashboard</h1> {/* Welcome Section */}
</main> <div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-2xl font-semibold text-gray-900 mb-1">
Welcome Back! Hammond
</h1>
<p className="text-sm text-gray-500">
Here's an overview of your practice today
</p>
</div> </div>
<Select value={timePeriod} onValueChange={setTimePeriod}>
<SelectTrigger className="w-[180px] cursor-pointer">
<SelectValue placeholder="Select period" />
</SelectTrigger>
<SelectContent className="bg-white cursor-pointer">
<SelectItem value="last_week">Last Week</SelectItem>
<SelectItem value="last_month">Last Month</SelectItem>
<SelectItem value="last_year">Last Year</SelectItem>
</SelectContent>
</Select>
</div>
{loading ? (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-400"></div>
</div>
) : (
<>
{/* Stats Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4">
{statCards.map((card, index) => {
const Icon = card.icon;
return (
<div
key={index}
className="bg-white rounded-lg border border-gray-200 p-4 sm:p-5 md:p-6 hover:shadow-md transition-shadow"
>
<div className="flex items-start justify-between mb-3 sm:mb-4">
<div className="p-2 sm:p-2.5 rounded-lg bg-gray-50">
<Icon className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" />
</div>
<div className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${
card.trendUp
? "bg-green-50 text-green-700"
: "bg-red-50 text-red-700"
}`}>
{card.trendUp ? (
<ArrowUpRight className="w-3 h-3" />
) : (
<ArrowDownRight className="w-3 h-3" />
)}
<span className="hidden sm:inline">{card.trend}</span>
</div>
</div>
<div>
<h3 className="text-xs font-medium text-rose-600 mb-1 sm:mb-2 uppercase tracking-wider">
{card.title}
</h3>
<p className="text-xl sm:text-2xl font-bold text-gray-900 mb-1">
{card.value}
</p>
<p className="text-xs text-gray-500">
vs last month
</p>
</div>
</div>
);
})}
</div>
</>
)}
</main>
</div> </div>
); );
} }

View File

@ -1,7 +1,12 @@
import { Header } from "./_components/header";
export default function AdminLayout({ children }: { children: React.ReactNode }) { export default function AdminLayout({ children }: { children: React.ReactNode }) {
return ( return (
<div> <div>
<Header />
<div className="pt-14 sm:pt-16">
{children} {children}
</div> </div>
</div>
) )
} }

View File

@ -1,30 +1,34 @@
"use client"; "use client";
import { useState } from "react"; import { useState } from "react";
import SideNav from "@/app/(admin)/_components/side-nav"; import { Bell } from "lucide-react";
import { Notifications, Notification } from "@/app/(admin)/_components/notifications";
interface Notification {
id: string;
title: string;
message: string;
time: string;
read: boolean;
}
export default function NotificationsPage() { export default function NotificationsPage() {
const [notifications, setNotifications] = useState<Notification[]>([ const [notifications, setNotifications] = useState<Notification[]>([
{ {
id: "1", id: "1",
type: "appointment", title: "New appointment scheduled",
title: "New Appointment Request", message: "John Smith booked an appointment for tomorrow",
message: "Sarah Johnson requested an appointment for tomorrow at 2:00 PM", time: "2 hours ago",
time: "2 minutes ago",
read: false, read: false,
}, },
{ {
id: "2", id: "2",
type: "success", title: "Payment received",
title: "Appointment Confirmed", message: "Payment of $150 received from Jane Doe",
message: "Your appointment with Michael Chen has been confirmed for today at 10:00 AM", time: "5 hours ago",
time: "1 hour ago",
read: false, read: false,
}, },
{ {
id: "3", id: "3",
type: "warning",
title: "Appointment Reminder", title: "Appointment Reminder",
message: "You have an appointment in 30 minutes with Emily Davis", message: "You have an appointment in 30 minutes with Emily Davis",
time: "3 hours ago", time: "3 hours ago",
@ -32,7 +36,6 @@ export default function NotificationsPage() {
}, },
{ {
id: "4", id: "4",
type: "info",
title: "New Message", title: "New Message",
message: "You received a new message from John Smith", message: "You received a new message from John Smith",
time: "5 hours ago", time: "5 hours ago",
@ -40,7 +43,6 @@ export default function NotificationsPage() {
}, },
{ {
id: "5", id: "5",
type: "appointment",
title: "Appointment Cancelled", title: "Appointment Cancelled",
message: "Robert Wilson cancelled his appointment scheduled for tomorrow", message: "Robert Wilson cancelled his appointment scheduled for tomorrow",
time: "1 day ago", time: "1 day ago",
@ -48,35 +50,71 @@ export default function NotificationsPage() {
}, },
]); ]);
const handleMarkAsRead = (id: string) => { const unreadCount = notifications.filter((n) => !n.read).length;
setNotifications((prev) =>
prev.map((n) => (n.id === id ? { ...n, read: true } : n))
);
};
const handleDismiss = (id: string) => {
setNotifications((prev) => prev.filter((n) => n.id !== id));
};
const handleMarkAllAsRead = () => {
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
};
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
{/* Side Navigation */} {/* Main Content */}
<SideNav /> <main className="p-3 sm:p-4 md:p-6 lg:p-8">
{/* Page Header */}
<div className="mb-4 sm:mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<div className="flex items-center gap-3">
<Bell className="w-5 h-5 sm:w-6 sm:h-6 text-gray-900" />
<h1 className="text-xl sm:text-2xl font-semibold text-gray-900">
Notifications
</h1>
{unreadCount > 0 && (
<span className="px-2 py-1 text-xs font-medium bg-rose-100 text-rose-700 rounded-full">
{unreadCount} new
</span>
)}
</div>
</div>
<div className="md:ml-[250px]"> {/* Notifications List */}
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<Notifications {notifications.length === 0 ? (
notifications={notifications} <div className="p-8 sm:p-12 text-center text-gray-500">
onMarkAsRead={handleMarkAsRead} <Bell className="w-12 h-12 mx-auto mb-2 text-gray-300" />
onDismiss={handleDismiss} <p className="text-sm">No notifications</p>
onMarkAllAsRead={handleMarkAllAsRead} </div>
/> ) : (
<div className="divide-y">
{notifications.map((notification) => {
return (
<div
key={notification.id}
className={`p-4 sm:p-6 hover:bg-gray-50 transition-colors cursor-pointer ${
!notification.read ? "bg-rose-50/50" : ""
}`}
>
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2">
<p
className={`text-sm sm:text-base font-medium text-gray-900 ${
!notification.read ? "font-semibold" : ""
}`}
>
{notification.title}
</p>
{!notification.read && (
<span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span>
)}
</div>
<p className="text-sm text-gray-600 mt-1">
{notification.message}
</p>
<p className="text-xs text-gray-400 mt-1">
{notification.time}
</p>
</div> </div>
</div> </div>
);
})}
</div>
)}
</div>
</main>
</div> </div>
); );
} }

View File

@ -47,11 +47,10 @@ export default function Login() {
className="ml-auto mb-6 w-8 h-8 rounded-full" className="ml-auto mb-6 w-8 h-8 rounded-full"
aria-label="Close" aria-label="Close"
> >
<X className="w-5 h-5 text-black" />
</Button> </Button>
{/* Heading */} {/* Heading */}
<h1 className="text-3xl font-bold text-black mb-2"> <h1 className="text-3xl font-bold bg-linear-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent mb-2">
Welcome back Welcome back
</h1> </h1>
@ -64,7 +63,10 @@ export default function Login() {
</p> </p>
{/* Login Form */} {/* Login Form */}
<form className="space-y-6" onSubmit={(e) => e.preventDefault()}> <form className="space-y-6" onSubmit={(e) => {
e.preventDefault();
router.push("/dashboard");
}}>
{/* Email Field */} {/* Email Field */}
<div className="space-y-2"> <div className="space-y-2">
<label htmlFor="email" className="text-sm font-medium text-black"> <label htmlFor="email" className="text-sm font-medium text-black">
@ -112,7 +114,7 @@ export default function Login() {
{/* Submit Button */} {/* Submit Button */}
<Button <Button
type="submit" type="submit"
className="w-full h-12 text-base font-semibold bg-[#4A90A4] hover:bg-[#3a7a8a] text-white" className="w-full h-12 text-base font-semibold bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all"
> >
Log in Log in
</Button> </Button>
@ -124,7 +126,7 @@ export default function Login() {
type="checkbox" type="checkbox"
checked={rememberMe} checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)} onChange={(e) => setRememberMe(e.target.checked)}
className="w-4 h-4 rounded border-gray-300 text-[#4A90A4] focus:ring-2 focus:ring-[#4A90A4] cursor-pointer" className="w-4 h-4 rounded border-gray-300 text-rose-600 focus:ring-2 focus:ring-rose-500 cursor-pointer"
/> />
<span className="text-black">Remember me</span> <span className="text-black">Remember me</span>
</label> </label>

View File

@ -49,22 +49,37 @@ export function About() {
ref={ref} ref={ref}
className="relative py-20 px-4 overflow-hidden" className="relative py-20 px-4 overflow-hidden"
> >
{/* Background Image */}
<div <div
className="absolute inset-0 z-0" className="absolute inset-0 z-0"
style={{ style={{
backgroundColor: isDark ? '#1a1e26' : '#ffffff' // backgroundImage: `url('https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}} }}
/> />
{/* Subtle overlay for readability - keeping background image clearly visible */}
<div
className="absolute inset-0 z-[1]"
style={{
backgroundColor: isDark ? 'rgba(0, 0, 0, 0.35)' : 'rgba(255, 255, 255, 0.30)'
}}
/>
{/* Very subtle gradient overlay */}
{!isDark && ( {!isDark && (
<div className="absolute inset-0 z-[1] bg-gradient-to-br from-rose-50/50 via-pink-50/50 to-orange-50/50" /> <div className="absolute inset-0 z-[2] bg-gradient-to-br from-rose-50/20 via-pink-50/15 to-orange-50/20" />
)} )}
{isDark && ( {isDark && (
<div className="absolute inset-0 z-[1] bg-gradient-to-br from-gray-900/80 via-gray-800/80 to-gray-900/80" /> <div className="absolute inset-0 z-[2] bg-gradient-to-br from-gray-900/25 via-gray-800/20 to-gray-900/25" />
)} )}
<div className="absolute inset-0 overflow-hidden z-[2]"> {/* Subtle animated blobs */}
<div className="absolute inset-0 overflow-hidden z-[3]">
<motion.div <motion.div
className="absolute top-10 left-10 w-64 h-64 bg-cyan-100 dark:bg-cyan-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-40 dark:opacity-60" className="absolute top-10 left-10 w-64 h-64 bg-cyan-100 dark:bg-cyan-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-30 dark:opacity-50"
animate={{ animate={{
x: [0, 80, 0], x: [0, 80, 0],
y: [0, 40, 0], y: [0, 40, 0],
@ -77,7 +92,7 @@ export function About() {
}} }}
/> />
<motion.div <motion.div
className="absolute bottom-20 right-10 w-80 h-80 bg-emerald-100 dark:bg-emerald-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-40 dark:opacity-60" className="absolute bottom-20 right-10 w-80 h-80 bg-emerald-100 dark:bg-emerald-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-30 dark:opacity-50"
animate={{ animate={{
x: [0, -80, 0], x: [0, -80, 0],
y: [0, 60, 0], y: [0, 60, 0],

View File

@ -39,24 +39,42 @@ export function ContactSection() {
return ( return (
<section id="contact" className="relative overflow-hidden py-20" ref={ref}> <section id="contact" className="relative overflow-hidden py-20" ref={ref}>
{/* Theme sync like Hero/About/Navbar */} {/* Background Image */}
<div className="absolute inset-0 z-0" style={{ backgroundColor: isDark ? "#1a1e26" : "#ffffff" }} /> <div
className="absolute inset-0 z-0"
style={{
// backgroundImage: `url('https://images.unsplash.com/photo-1556761175-5973dc0f32e7?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}}
/>
{/* Subtle overlay for readability - keeping background image clearly visible */}
<div
className="absolute inset-0 z-[1]"
style={{
backgroundColor: isDark ? 'rgba(0, 0, 0, 0.35)' : 'rgba(255, 255, 255, 0.30)'
}}
/>
{/* Very subtle gradient overlay */}
{!isDark && ( {!isDark && (
<div className="absolute inset-0 z-[1] bg-gradient-to-br from-rose-50/40 via-pink-50/40 to-orange-50/40" /> <div className="absolute inset-0 z-[2] bg-gradient-to-br from-rose-50/20 via-pink-50/15 to-orange-50/20" />
)} )}
{isDark && ( {isDark && (
<div className="absolute inset-0 z-[1] bg-gradient-to-br from-gray-900/70 via-gray-800/70 to-gray-900/70" /> <div className="absolute inset-0 z-[2] bg-gradient-to-br from-gray-900/25 via-gray-800/20 to-gray-900/25" />
)} )}
{/* Subtle animated blobs */} {/* Subtle animated blobs */}
<div className="absolute inset-0 z-[2] overflow-hidden"> <div className="absolute inset-0 z-[3] overflow-hidden">
<motion.div <motion.div
className={`absolute -top-10 left-10 h-64 w-64 rounded-full blur-3xl ${isDark ? "bg-pink-900/30 opacity-60" : "bg-pink-100 opacity-50"}`} className={`absolute -top-10 left-10 h-64 w-64 rounded-full blur-3xl ${isDark ? "bg-pink-900/30 opacity-50" : "bg-pink-100 opacity-40"}`}
animate={{ x: [0, 60, 0], y: [0, 40, 0], scale: [1, 1.05, 1] }} animate={{ x: [0, 60, 0], y: [0, 40, 0], scale: [1, 1.05, 1] }}
transition={{ duration: 18, repeat: Infinity, ease: "easeInOut" }} transition={{ duration: 18, repeat: Infinity, ease: "easeInOut" }}
/> />
<motion.div <motion.div
className={`absolute -bottom-10 right-10 h-72 w-72 rounded-full blur-3xl ${isDark ? "bg-orange-900/30 opacity-60" : "bg-orange-100 opacity-50"}`} className={`absolute -bottom-10 right-10 h-72 w-72 rounded-full blur-3xl ${isDark ? "bg-orange-900/30 opacity-50" : "bg-orange-100 opacity-40"}`}
animate={{ x: [0, -60, 0], y: [0, -40, 0], scale: [1, 1.08, 1] }} animate={{ x: [0, -60, 0], y: [0, -40, 0], scale: [1, 1.08, 1] }}
transition={{ duration: 22, repeat: Infinity, ease: "easeInOut" }} transition={{ duration: 22, repeat: Infinity, ease: "easeInOut" }}
/> />
@ -78,37 +96,66 @@ export function ContactSection() {
</p> </p>
</motion.div> </motion.div>
<div className="grid gap-12 lg:grid-cols-2"> <div className="grid gap-12 lg:grid-cols-2 lg:items-stretch">
{/* Left: Illustration replacing cards */} {/* Left: Illustration replacing cards */}
<motion.div <motion.div
initial={{ opacity: 0, x: -50 }} initial={{ opacity: 0, x: -50 }}
animate={isInView ? { opacity: 1, x: 0 } : {}} animate={isInView ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2 }} transition={{ duration: 0.6, delay: 0.2 }}
className="relative" className="relative flex"
> >
<Card className="bg-card/60 backdrop-blur-sm border border-border/50 overflow-hidden"> <Card className="bg-gradient-to-br from-rose-100/30 via-pink-100/30 to-orange-100/30 dark:from-rose-900/20 dark:via-pink-900/20 dark:to-orange-900/20 backdrop-blur-sm border border-border/50 overflow-hidden relative h-full flex flex-col rounded-3xl">
<CardContent className="p-0"> <CardContent className="p-0 flex-1 flex flex-col">
{/* Use a high-quality, license-friendly illustration */} {/* Background image for the card */}
<div className="relative"> <div
className="absolute inset-0 z-0 rounded-3xl"
style={{
// backgroundImage: `url('https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
opacity: 0.3,
}}
/>
{/* Content */}
<div className="relative z-10 p-6 flex-1 flex flex-col">
{/* Image and text side by side */}
<div className="flex flex-col sm:flex-row items-center sm:items-start gap-6 flex-1">
{/* Modern therapy-related illustration */}
<div className="relative flex-shrink-0">
<img <img
src="/3786819.jpg" src="/mmmfil.jpeg"
alt="Contact illustration" alt="Nathalie Mac Guffie, LCSW - Your therapist"
className="mx-auto w-full max-w-xl p-6 select-none rounded-xl object-cover" className="w-32 h-32 sm:w-40 sm:h-40 md:w-48 md:h-48 select-none rounded-full object-cover shadow-lg"
loading="lazy" loading="lazy"
/> />
<motion.div <motion.div
className="pointer-events-none absolute inset-0" className="pointer-events-none absolute inset-0 rounded-full"
animate={{ opacity: [0.3, 0.6, 0.3] }} animate={{ opacity: [0.3, 0.6, 0.3] }}
transition={{ duration: 3, repeat: Infinity }} transition={{ duration: 3, repeat: Infinity }}
style={{ background: "radial-gradient(600px circle at 20% 10%, rgba(244,114,182,0.15), transparent 40%), radial-gradient(600px circle at 80% 80%, rgba(251,146,60,0.12), transparent 40%)" }} style={{ background: "radial-gradient(circle, rgba(244,114,182,0.15), transparent 70%), radial-gradient(circle, rgba(251,146,60,0.12), transparent 70%)" }}
/> />
</div> </div>
<div className="px-6 pb-6"> {/* Text content */}
<h3 className="text-2xl font-bold mb-2 text-foreground">Let's Begin Your Healing Journey</h3> <div className="flex-1 text-center sm:text-left flex flex-col justify-center">
<p className="text-muted-foreground leading-relaxed"> <h3 className="text-2xl font-bold mb-4 text-foreground">Let's Begin Your Healing Journey</h3>
<div className="space-y-3 text-muted-foreground leading-relaxed">
<p>
Taking the first step toward therapy can feel daunting, but you're not alone. I'm here to support Taking the first step toward therapy can feel daunting, but you're not alone. I'm here to support
you through every stage of your journey toward wellness and growth. you through every stage of your journey toward wellness and growth.
</p> </p>
<p>
Whether you're seeking help for yourself or your child, I provide a warm, non-judgmental space where
healing can begin. My approach is relationship-based and tailored to meet your unique needs.
</p>
<p>
Reach out today to schedule a consultation. Together, we can explore how therapy can support you
in creating positive change and building resilience.
</p>
</div>
</div>
</div>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

98
components/DatePicker.tsx Normal file
View File

@ -0,0 +1,98 @@
'use client';
import * as React from 'react';
import { Calendar as CalendarIcon } from 'lucide-react';
import { format } from 'date-fns';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Calendar } from '@/components/ui/calendar';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
interface DatePickerProps {
date: Date | undefined;
setDate: (date: Date | undefined) => void;
label?: string;
}
export function DatePicker({ date, setDate, label }: DatePickerProps) {
const [open, setOpen] = React.useState(false);
return (
<div className="space-y-2">
{label && (
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
{label}
</label>
)}
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger asChild>
<Button
variant={'outline'}
size="sm"
className={cn(
'justify-start text-left font-normal',
!date && 'text-muted-foreground'
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, 'PPP') : <span>Pick a date</span>}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-auto p-5 bg-gradient-to-br from-white to-gray-50 dark:from-gray-800 dark:to-gray-900 shadow-2xl border-2 border-gray-100 dark:border-gray-700 rounded-2xl" align="end" sideOffset={5}>
<Calendar
mode="single"
selected={date}
onSelect={(selectedDate) => {
setDate(selectedDate);
setOpen(false);
}}
initialFocus
classNames={{
months: "space-y-4",
month: "space-y-4",
caption: "flex justify-center pt-3 pb-5 relative items-center border-b border-gray-200 dark:border-gray-700 mb-4",
caption_label: "text-lg font-bold text-gray-800 dark:text-gray-100",
nav: "flex items-center justify-between absolute inset-0",
nav_button: cn(
"h-9 w-9 rounded-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 hover:bg-rose-50 dark:hover:bg-gray-600 hover:border-rose-300 dark:hover:border-rose-500 p-0 transition-all shadow-sm"
),
nav_button_previous: "absolute left-0",
nav_button_next: "absolute right-0",
table: "w-full border-collapse space-y-3",
head_row: "flex mb-3",
head_cell: "text-gray-600 dark:text-gray-400 rounded-md w-11 font-semibold text-xs",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20",
"[&>button]:h-11 [&>button]:w-11 [&>button]:p-0 [&>button]:font-semibold [&>button]:cursor-pointer [&>button]:rounded-full [&>button]:transition-all"
),
day: cn(
"h-11 w-11 p-0 font-semibold aria-selected:opacity-100 hover:bg-rose-500 hover:text-white rounded-full transition-all cursor-pointer",
"hover:scale-110 active:scale-95 hover:shadow-md"
),
day_selected:
"bg-rose-600 text-white hover:bg-rose-700 hover:text-white focus:bg-rose-600 focus:text-white font-bold shadow-xl scale-110 ring-4 ring-rose-200 dark:ring-rose-800",
day_today: "bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 font-bold border-2 border-blue-300 dark:border-blue-600",
day_outside: "text-gray-300 dark:text-gray-600 opacity-50",
day_disabled: "text-gray-200 dark:text-gray-700 opacity-30 cursor-not-allowed",
day_range_middle:
"aria-selected:bg-rose-100 dark:aria-selected:bg-rose-900/30 aria-selected:text-rose-700 dark:aria-selected:text-rose-300",
day_hidden: "invisible",
}}
/>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}

View File

@ -28,22 +28,37 @@ export function HeroSection() {
id="home" id="home"
className="relative min-h-screen flex items-center justify-center overflow-hidden pt-20" className="relative min-h-screen flex items-center justify-center overflow-hidden pt-20"
> >
{/* Background Image */}
<div <div
className="absolute inset-0 z-0" className="absolute inset-0 z-0"
style={{ style={{
backgroundColor: isDark ? '#1a1e26' : '#ffffff' backgroundImage: `url('https://images.unsplash.com/photo-1506126613408-eca07ce68773?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}} }}
/> />
{/* Subtle dark overlay for better text readability - keeping background image clearly visible */}
<div
className="absolute inset-0 z-[1]"
style={{
backgroundColor: isDark ? 'rgba(0, 0, 0, 0.35)' : 'rgba(0, 0, 0, 0.25)'
}}
/>
{/* Very subtle gradient overlay for warmth */}
{!isDark && ( {!isDark && (
<div className="absolute inset-0 z-[1] bg-gradient-to-br from-rose-50/50 via-pink-50/50 to-orange-50/50" /> <div className="absolute inset-0 z-[2] bg-gradient-to-br from-rose-50/20 via-pink-50/15 to-orange-50/20" />
)} )}
{isDark && ( {isDark && (
<div className="absolute inset-0 z-[1] bg-gradient-to-br from-gray-900/80 via-gray-800/80 to-gray-900/80" /> <div className="absolute inset-0 z-[2] bg-gradient-to-br from-gray-900/25 via-gray-800/20 to-gray-900/25" />
)} )}
<div className="absolute inset-0 overflow-hidden z-[2]"> {/* Subtle animated blobs for depth */}
<div className="absolute inset-0 overflow-hidden z-[3]">
<motion.div <motion.div
className="absolute top-20 left-10 w-72 h-72 bg-rose-100 dark:bg-rose-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-50 dark:opacity-70" className="absolute top-20 left-10 w-72 h-72 bg-rose-100 dark:bg-rose-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-40 dark:opacity-50"
animate={{ animate={{
x: [0, 100, 0], x: [0, 100, 0],
y: [0, 50, 0], y: [0, 50, 0],
@ -56,7 +71,7 @@ export function HeroSection() {
}} }}
/> />
<motion.div <motion.div
className="absolute top-40 right-10 w-96 h-96 bg-pink-100 dark:bg-pink-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-50 dark:opacity-70" className="absolute top-40 right-10 w-96 h-96 bg-pink-100 dark:bg-pink-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-40 dark:opacity-50"
animate={{ animate={{
x: [0, -100, 0], x: [0, -100, 0],
y: [0, 100, 0], y: [0, 100, 0],
@ -69,7 +84,7 @@ export function HeroSection() {
}} }}
/> />
<motion.div <motion.div
className="absolute bottom-20 left-1/3 w-80 h-80 bg-orange-100 dark:bg-orange-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-50 dark:opacity-70" className="absolute bottom-20 left-1/3 w-80 h-80 bg-orange-100 dark:bg-orange-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-40 dark:opacity-50"
animate={{ animate={{
x: [0, 50, 0], x: [0, 50, 0],
y: [0, -50, 0], y: [0, -50, 0],
@ -91,7 +106,7 @@ export function HeroSection() {
transition={{ duration: 0.8 }} transition={{ duration: 0.8 }}
> >
<motion.h1 <motion.h1
className="text-5xl md:text-7xl font-bold mb-6 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent" className="text-5xl md:text-7xl font-bold mb-6 text-white drop-shadow-lg"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2 }} transition={{ duration: 0.8, delay: 0.2 }}
@ -100,7 +115,7 @@ export function HeroSection() {
</motion.h1> </motion.h1>
<motion.p <motion.p
className="text-xl md:text-2xl text-muted-foreground mb-4" className="text-xl md:text-2xl text-white/95 mb-4 drop-shadow-md"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.4 }} transition={{ duration: 0.8, delay: 0.4 }}
@ -109,7 +124,7 @@ export function HeroSection() {
</motion.p> </motion.p>
<motion.p <motion.p
className="text-lg md:text-xl text-muted-foreground mb-8 max-w-2xl mx-auto" className="text-lg md:text-xl text-white/90 mb-8 max-w-2xl mx-auto drop-shadow-md"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.6 }} transition={{ duration: 0.8, delay: 0.6 }}

View File

@ -49,10 +49,10 @@ export function Navbar() {
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
> >
<div className="bg-gradient-to-br from-rose-500 to-pink-600 p-2 rounded-xl"> <div className="bg-linear-to-r from-rose-500 to-pink-600 p-2 rounded-xl">
<Heart className="h-5 w-5 text-white fill-white" /> <Heart className="h-5 w-5 text-white fill-white" />
</div> </div>
<span className="font-bold text-lg bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent"> <span className="font-bold text-lg bg-linear-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent">
Attune Heart Therapy Attune Heart Therapy
</span> </span>
</motion.a> </motion.a>

View File

@ -30,31 +30,37 @@ export function Services() {
icon: Brain, icon: Brain,
title: "Trauma-Focused Therapy", title: "Trauma-Focused Therapy",
description: "Evidence-based TF-CBT to help children process and heal from traumatic experiences in a safe, supportive environment.", description: "Evidence-based TF-CBT to help children process and heal from traumatic experiences in a safe, supportive environment.",
backgroundImage: "https://images.unsplash.com/photo-1519494026892-80bbd2d6fd0d?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80",
}, },
{ {
icon: Sparkles, icon: Sparkles,
title: "Play Therapy", title: "Play Therapy",
description: "Child-centered play therapy allowing children to express themselves naturally and build emotional regulation skills.", description: "Child-centered play therapy allowing children to express themselves naturally and build emotional regulation skills.",
backgroundImage: "https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80",
}, },
{ {
icon: Baby, icon: Baby,
title: "Infant Mental Health", title: "Infant Mental Health",
description: "Specialized support for infants and toddlers, focusing on early attachment, developmental milestones, and caregiver relationships.", description: "Specialized support for infants and toddlers, focusing on early attachment, developmental milestones, and caregiver relationships.",
backgroundImage: "https://images.unsplash.com/photo-1515488042361-ee00e0ddd4e4?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80",
}, },
{ {
icon: Users2, icon: Users2,
title: "Dyadic Therapy", title: "Dyadic Therapy",
description: "Strengthening parent-child relationships through interactive sessions that enhance communication and connection.", description: "Strengthening parent-child relationships through interactive sessions that enhance communication and connection.",
backgroundImage: "https://images.unsplash.com/photo-1529156069898-49953e39b3ac?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80",
}, },
{ {
icon: HeartHandshake, icon: HeartHandshake,
title: "Social-Emotional Support", title: "Social-Emotional Support",
description: "Building emotional literacy and self-regulation skills to help children navigate relationships and challenges.", description: "Building emotional literacy and self-regulation skills to help children navigate relationships and challenges.",
backgroundImage: "https://images.unsplash.com/photo-1497486751825-1233686d5d80?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80",
}, },
{ {
icon: Shield, icon: Shield,
title: "Relationship-Based Care", title: "Relationship-Based Care",
description: "Fostering healing through nurturing therapeutic relationships and caregiver collaboration.", description: "Fostering healing through nurturing therapeutic relationships and caregiver collaboration.",
backgroundImage: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80",
}, },
]; ];
@ -64,22 +70,37 @@ export function Services() {
ref={ref} ref={ref}
className="relative py-20 px-4 overflow-hidden" className="relative py-20 px-4 overflow-hidden"
> >
{/* Background Image */}
<div <div
className="absolute inset-0 z-0" className="absolute inset-0 z-0"
style={{ style={{
backgroundColor: isDark ? '#1a1e26' : '#ffffff' backgroundImage: `url('/large.jpeg')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}} }}
/> />
{/* Minimal overlay - allowing background image to show at near original opaqueness */}
<div
className="absolute inset-0 z-[1]"
style={{
backgroundColor: isDark ? 'rgba(0, 0, 0, 0.15)' : 'rgba(255, 255, 255, 0.10)'
}}
/>
{/* Very subtle gradient overlay */}
{!isDark && ( {!isDark && (
<div className="absolute inset-0 z-[1] bg-gradient-to-br from-rose-50/50 via-pink-50/50 to-orange-50/50" /> <div className="absolute inset-0 z-[2] bg-gradient-to-br from-rose-50/10 via-pink-50/8 to-orange-50/10" />
)} )}
{isDark && ( {isDark && (
<div className="absolute inset-0 z-[1] bg-gradient-to-br from-gray-900/80 via-gray-800/80 to-gray-900/80" /> <div className="absolute inset-0 z-[2] bg-gradient-to-br from-gray-900/10 via-gray-800/8 to-gray-900/10" />
)} )}
<div className="absolute inset-0 overflow-hidden z-[2]"> {/* Subtle animated blobs */}
<div className="absolute inset-0 overflow-hidden z-[3]">
<motion.div <motion.div
className="absolute top-20 right-20 w-72 h-72 bg-pink-100 dark:bg-pink-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-40 dark:opacity-60" className="absolute top-20 right-20 w-72 h-72 bg-pink-100 dark:bg-pink-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-30 dark:opacity-50"
animate={{ animate={{
x: [0, -90, 0], x: [0, -90, 0],
y: [0, 50, 0], y: [0, 50, 0],
@ -92,7 +113,7 @@ export function Services() {
}} }}
/> />
<motion.div <motion.div
className="absolute bottom-10 left-20 w-96 h-96 bg-orange-100 dark:bg-orange-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-40 dark:opacity-60" className="absolute bottom-10 left-20 w-96 h-96 bg-orange-100 dark:bg-orange-900/20 rounded-full mix-blend-multiply dark:mix-blend-lighten filter blur-xl opacity-30 dark:opacity-50"
animate={{ animate={{
x: [0, 70, 0], x: [0, 70, 0],
y: [0, -60, 0], y: [0, -60, 0],
@ -142,12 +163,18 @@ export function Services() {
transition={{ duration: 0.5, delay: index * 0.1 }} transition={{ duration: 0.5, delay: index * 0.1 }}
className="group bg-card/50 backdrop-blur-sm rounded-2xl p-6 border border-border/50 hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/10 hover:scale-105 transition-all duration-300 cursor-pointer" className="group bg-card/50 backdrop-blur-sm rounded-2xl p-6 border border-border/50 hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/10 hover:scale-105 transition-all duration-300 cursor-pointer"
> >
{/* Content */}
<div className="relative z-10">
<motion.div <motion.div
className="bg-gradient-to-br from-rose-500/20 via-pink-500/20 to-orange-500/20 dark:from-rose-500/30 dark:via-pink-500/30 dark:to-orange-500/30 w-14 h-14 rounded-xl flex items-center justify-center mb-4" className="w-16 h-16 rounded-xl overflow-hidden mb-4 shadow-lg"
whileHover={{ scale: 1.1, rotate: 5 }} whileHover={{ scale: 1.1, rotate: 5 }}
transition={{ duration: 0.2 }} transition={{ duration: 0.2 }}
> >
<Icon className="h-7 w-7 text-rose-600 dark:text-rose-400" /> <img
src={service.backgroundImage}
alt={service.title}
className="w-full h-full object-cover"
/>
</motion.div> </motion.div>
<motion.h3 <motion.h3
className="text-xl font-semibold mb-3 text-foreground" className="text-xl font-semibold mb-3 text-foreground"
@ -165,6 +192,7 @@ export function Services() {
> >
{service.description} {service.description}
</motion.p> </motion.p>
</div>
</motion.div> </motion.div>
); );
})} })}

View File

@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{ {
variants: { variants: {
variant: { variant: {

216
components/ui/calendar.tsx Normal file
View File

@ -0,0 +1,216 @@
"use client"
import * as React from "react"
import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from "lucide-react"
import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "@/components/ui/button"
function Calendar({
className,
classNames,
showOutsideDays = true,
captionLayout = "label",
buttonVariant = "ghost",
formatters,
components,
...props
}: React.ComponentProps<typeof DayPicker> & {
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
}) {
const defaultClassNames = getDefaultClassNames()
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn(
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
className
)}
captionLayout={captionLayout}
formatters={{
formatMonthDropdown: (date) =>
date.toLocaleString("default", { month: "short" }),
...formatters,
}}
classNames={{
root: cn("w-fit", defaultClassNames.root),
months: cn(
"flex gap-4 flex-col md:flex-row relative",
defaultClassNames.months
),
month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
nav: cn(
"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
defaultClassNames.nav
),
button_previous: cn(
buttonVariants({ variant: buttonVariant }),
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
defaultClassNames.button_previous
),
button_next: cn(
buttonVariants({ variant: buttonVariant }),
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
defaultClassNames.button_next
),
month_caption: cn(
"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
defaultClassNames.month_caption
),
dropdowns: cn(
"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
defaultClassNames.dropdowns
),
dropdown_root: cn(
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
defaultClassNames.dropdown_root
),
dropdown: cn(
"absolute bg-popover inset-0 opacity-0",
defaultClassNames.dropdown
),
caption_label: cn(
"select-none font-medium",
captionLayout === "label"
? "text-sm"
: "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
defaultClassNames.caption_label
),
table: "w-full border-collapse",
weekdays: cn("flex", defaultClassNames.weekdays),
weekday: cn(
"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
defaultClassNames.weekday
),
week: cn("flex w-full mt-2", defaultClassNames.week),
week_number_header: cn(
"select-none w-(--cell-size)",
defaultClassNames.week_number_header
),
week_number: cn(
"text-[0.8rem] select-none text-muted-foreground",
defaultClassNames.week_number
),
day: cn(
"relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
props.showWeekNumber
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
: "[&:first-child[data-selected=true]_button]:rounded-l-md",
defaultClassNames.day
),
range_start: cn(
"rounded-l-md bg-accent",
defaultClassNames.range_start
),
range_middle: cn("rounded-none", defaultClassNames.range_middle),
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
today: cn(
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
defaultClassNames.today
),
outside: cn(
"text-muted-foreground aria-selected:text-muted-foreground",
defaultClassNames.outside
),
disabled: cn(
"text-muted-foreground opacity-50",
defaultClassNames.disabled
),
hidden: cn("invisible", defaultClassNames.hidden),
...classNames,
}}
components={{
Root: ({ className, rootRef, ...props }) => {
return (
<div
data-slot="calendar"
ref={rootRef}
className={cn(className)}
{...props}
/>
)
},
Chevron: ({ className, orientation, ...props }) => {
if (orientation === "left") {
return (
<ChevronLeftIcon className={cn("size-4", className)} {...props} />
)
}
if (orientation === "right") {
return (
<ChevronRightIcon
className={cn("size-4", className)}
{...props}
/>
)
}
return (
<ChevronDownIcon className={cn("size-4", className)} {...props} />
)
},
DayButton: CalendarDayButton,
WeekNumber: ({ children, ...props }) => {
return (
<td {...props}>
<div className="flex size-(--cell-size) items-center justify-center text-center">
{children}
</div>
</td>
)
},
...components,
}}
{...props}
/>
)
}
function CalendarDayButton({
className,
day,
modifiers,
...props
}: React.ComponentProps<typeof DayButton>) {
const defaultClassNames = getDefaultClassNames()
const ref = React.useRef<HTMLButtonElement>(null)
React.useEffect(() => {
if (modifiers.focused) ref.current?.focus()
}, [modifiers.focused])
return (
<Button
ref={ref}
variant="ghost"
size="icon"
data-day={day.date.toLocaleDateString()}
data-selected-single={
modifiers.selected &&
!modifiers.range_start &&
!modifiers.range_end &&
!modifiers.range_middle
}
data-range-start={modifiers.range_start}
data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle}
className={cn(
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
defaultClassNames.day,
className
)}
{...props}
/>
)
}
export { Calendar, CalendarDayButton }

View File

@ -0,0 +1,257 @@
"use client"
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function DropdownMenu({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}
function DropdownMenuPortal({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
)
}
function DropdownMenuTrigger({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return (
<DropdownMenuPrimitive.Trigger
data-slot="dropdown-menu-trigger"
{...props}
/>
)
}
function DropdownMenuContent({
className,
sideOffset = 4,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
data-slot="dropdown-menu-content"
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground 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 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
)
}
function DropdownMenuGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
)
}
function DropdownMenuItem({
className,
inset,
variant = "default",
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
variant?: "default" | "destructive"
}) {
return (
<DropdownMenuPrimitive.Item
data-slot="dropdown-menu-item"
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
)
}
function DropdownMenuCheckboxItem({
className,
children,
checked,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
return (
<DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
checked={checked}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="size-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
)
}
function DropdownMenuRadioGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return (
<DropdownMenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group"
{...props}
/>
)
}
function DropdownMenuRadioItem({
className,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
return (
<DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CircleIcon className="size-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
)
}
function DropdownMenuLabel({
className,
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.Label
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
)}
{...props}
/>
)
}
function DropdownMenuSeparator({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
return (
<DropdownMenuPrimitive.Separator
data-slot="dropdown-menu-separator"
className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props}
/>
)
}
function DropdownMenuShortcut({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="dropdown-menu-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className
)}
{...props}
/>
)
}
function DropdownMenuSub({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
}
function DropdownMenuSubTrigger({
className,
inset,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.SubTrigger
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger>
)
}
function DropdownMenuSubContent({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
return (
<DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content"
className={cn(
"bg-popover text-popover-foreground 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 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
className
)}
{...props}
/>
)
}
export {
DropdownMenu,
DropdownMenuPortal,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
}

48
components/ui/popover.tsx Normal file
View File

@ -0,0 +1,48 @@
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
function Popover({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
return <PopoverPrimitive.Root data-slot="popover" {...props} />
}
function PopoverTrigger({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
}
function PopoverContent({
className,
align = "center",
sideOffset = 4,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
data-slot="popover-content"
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground 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 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
)
}
function PopoverAnchor({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
}
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }

187
components/ui/select.tsx Normal file
View File

@ -0,0 +1,187 @@
"use client"
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Select({
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
return <SelectPrimitive.Root data-slot="select" {...props} />
}
function SelectGroup({
...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
return <SelectPrimitive.Group data-slot="select-group" {...props} />
}
function SelectValue({
...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
return <SelectPrimitive.Value data-slot="select-value" {...props} />
}
function SelectTrigger({
className,
size = "default",
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default"
}) {
return (
<SelectPrimitive.Trigger
data-slot="select-trigger"
data-size={size}
className={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDownIcon className="size-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
)
}
function SelectContent({
className,
children,
position = "popper",
align = "center",
...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
return (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
data-slot="select-content"
className={cn(
"bg-popover text-popover-foreground 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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
align={align}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
)
}
function SelectLabel({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
return (
<SelectPrimitive.Label
data-slot="select-label"
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
{...props}
/>
)
}
function SelectItem({
className,
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
return (
<SelectPrimitive.Item
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className
)}
{...props}
>
<span className="absolute right-2 flex size-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<CheckIcon className="size-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
)
}
function SelectSeparator({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
return (
<SelectPrimitive.Separator
data-slot="select-separator"
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
{...props}
/>
)
}
function SelectScrollUpButton({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
return (
<SelectPrimitive.ScrollUpButton
data-slot="select-scroll-up-button"
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronUpIcon className="size-4" />
</SelectPrimitive.ScrollUpButton>
)
}
function SelectScrollDownButton({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
return (
<SelectPrimitive.ScrollDownButton
data-slot="select-scroll-down-button"
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronDownIcon className="size-4" />
</SelectPrimitive.ScrollDownButton>
)
}
export {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
}

View File

@ -6,3 +6,4 @@ export function Toaster() {
} }

View File

@ -1,7 +1,14 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
/* config options here */ images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
],
},
}; };
export default nextConfig; export default nextConfig;

View File

@ -12,14 +12,19 @@
"node": ">=20.9.0" "node": ">=20.9.0"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-slot": "^1.2.4",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"date-fns": "^4.1.0",
"framer-motion": "^12.23.24", "framer-motion": "^12.23.24",
"lucide-react": "^0.552.0", "lucide-react": "^0.552.0",
"next": "16.0.1", "next": "16.0.1",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"react": "19.2.0", "react": "19.2.0",
"react-day-picker": "^9.11.1",
"react-dom": "19.2.0", "react-dom": "19.2.0",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.3.1" "tailwind-merge": "^3.3.1"

View File

@ -8,6 +8,15 @@ importers:
.: .:
dependencies: dependencies:
'@radix-ui/react-dropdown-menu':
specifier: ^2.1.16
version: 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-popover':
specifier: ^1.1.15
version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-select':
specifier: ^2.2.6
version: 2.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-slot': '@radix-ui/react-slot':
specifier: ^1.2.4 specifier: ^1.2.4
version: 1.2.4(@types/react@19.2.2)(react@19.2.0) version: 1.2.4(@types/react@19.2.2)(react@19.2.0)
@ -17,6 +26,9 @@ importers:
clsx: clsx:
specifier: ^2.1.1 specifier: ^2.1.1
version: 2.1.1 version: 2.1.1
date-fns:
specifier: ^4.1.0
version: 4.1.0
framer-motion: framer-motion:
specifier: ^12.23.24 specifier: ^12.23.24
version: 12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0) version: 12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
@ -32,6 +44,9 @@ importers:
react: react:
specifier: 19.2.0 specifier: 19.2.0
version: 19.2.0 version: 19.2.0
react-day-picker:
specifier: ^9.11.1
version: 9.11.1(react@19.2.0)
react-dom: react-dom:
specifier: 19.2.0 specifier: 19.2.0
version: 19.2.0(react@19.2.0) version: 19.2.0(react@19.2.0)
@ -143,6 +158,9 @@ packages:
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@date-fns/tz@1.4.1':
resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==}
'@emnapi/core@1.7.0': '@emnapi/core@1.7.0':
resolution: {integrity: sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==} resolution: {integrity: sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==}
@ -190,6 +208,21 @@ packages:
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@floating-ui/core@1.7.3':
resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
'@floating-ui/dom@1.7.4':
resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==}
'@floating-ui/react-dom@2.1.6':
resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@floating-ui/utils@0.2.10':
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
'@humanfs/core@0.19.1': '@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'} engines: {node: '>=18.18.0'}
@ -421,6 +454,38 @@ packages:
resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
engines: {node: '>=12.4.0'} engines: {node: '>=12.4.0'}
'@radix-ui/number@1.1.1':
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
'@radix-ui/primitive@1.1.3':
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
'@radix-ui/react-arrow@1.1.7':
resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==}
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-collection@1.1.7':
resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
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-compose-refs@1.1.2': '@radix-ui/react-compose-refs@1.1.2':
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
peerDependencies: peerDependencies:
@ -430,6 +495,194 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
'@radix-ui/react-context@1.1.2':
resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-direction@1.1.1':
resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-dismissable-layer@1.1.11':
resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
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-dropdown-menu@2.1.16':
resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==}
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-focus-guards@1.1.3':
resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-focus-scope@1.1.7':
resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
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.1':
resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-menu@2.1.16':
resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==}
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-popover@1.1.15':
resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==}
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-popper@1.2.8':
resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==}
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-portal@1.1.9':
resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
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-presence@1.1.5':
resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
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-primitive@2.1.3':
resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
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-roving-focus@1.1.11':
resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==}
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-select@2.2.6':
resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==}
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-slot@1.2.3':
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-slot@1.2.4': '@radix-ui/react-slot@1.2.4':
resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==}
peerDependencies: peerDependencies:
@ -439,6 +692,94 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
'@radix-ui/react-use-callback-ref@1.1.1':
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-controllable-state@1.2.2':
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-effect-event@0.0.2':
resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-escape-keydown@1.1.1':
resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-layout-effect@1.1.1':
resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-previous@1.1.1':
resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-rect@1.1.1':
resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-size@1.1.1':
resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-visually-hidden@1.2.3':
resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
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/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
'@rtsao/scc@1.1.0': '@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
@ -730,6 +1071,10 @@ packages:
argparse@2.0.1: argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
aria-hidden@1.2.6:
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
engines: {node: '>=10'}
aria-query@5.3.2: aria-query@5.3.2:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -875,6 +1220,12 @@ packages:
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
date-fns-jalali@4.1.0-0:
resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==}
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
debug@3.2.7: debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies: peerDependencies:
@ -907,6 +1258,9 @@ packages:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
detect-node-es@1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
doctrine@2.1.0: doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -1169,6 +1523,10 @@ packages:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
get-nonce@1.0.1:
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
engines: {node: '>=6'}
get-proto@1.0.1: get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1694,6 +2052,12 @@ packages:
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
react-day-picker@9.11.1:
resolution: {integrity: sha512-l3ub6o8NlchqIjPKrRFUCkTUEq6KwemQlfv3XZzzwpUeGwmDJ+0u0Upmt38hJyd7D/vn2dQoOoLV/qAp0o3uUw==}
engines: {node: '>=18'}
peerDependencies:
react: '>=16.8.0'
react-dom@19.2.0: react-dom@19.2.0:
resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==}
peerDependencies: peerDependencies:
@ -1702,6 +2066,36 @@ packages:
react-is@16.13.1: react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@types/react':
optional: true
react-remove-scroll@2.7.1:
resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
react-style-singleton@2.2.3:
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
react@19.2.0: react@19.2.0:
resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -1954,6 +2348,26 @@ packages:
uri-js@4.4.1: uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
use-callback-ref@1.3.3:
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
use-sidecar@1.1.3:
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
which-boxed-primitive@1.1.1: which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -2099,6 +2513,8 @@ snapshots:
'@babel/helper-string-parser': 7.27.1 '@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5 '@babel/helper-validator-identifier': 7.28.5
'@date-fns/tz@1.4.1': {}
'@emnapi/core@1.7.0': '@emnapi/core@1.7.0':
dependencies: dependencies:
'@emnapi/wasi-threads': 1.1.0 '@emnapi/wasi-threads': 1.1.0
@ -2161,6 +2577,23 @@ snapshots:
'@eslint/core': 0.17.0 '@eslint/core': 0.17.0
levn: 0.4.1 levn: 0.4.1
'@floating-ui/core@1.7.3':
dependencies:
'@floating-ui/utils': 0.2.10
'@floating-ui/dom@1.7.4':
dependencies:
'@floating-ui/core': 1.7.3
'@floating-ui/utils': 0.2.10
'@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@floating-ui/dom': 1.7.4
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
'@floating-ui/utils@0.2.10': {}
'@humanfs/core@0.19.1': {} '@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.7': '@humanfs/node@0.16.7':
@ -2331,12 +2764,250 @@ snapshots:
'@nolyfill/is-core-module@1.0.39': {} '@nolyfill/is-core-module@1.0.39': {}
'@radix-ui/number@1.1.1': {}
'@radix-ui/primitive@1.1.3': {}
'@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.2.0)': '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.2.0)':
dependencies: dependencies:
react: 19.2.0 react: 19.2.0
optionalDependencies: optionalDependencies:
'@types/react': 19.2.2 '@types/react': 19.2.2
'@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.2.0)':
dependencies:
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-direction@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.2)(react@19.2.0)':
dependencies:
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
aria-hidden: 1.2.6
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
aria-hidden: 1.2.6
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/rect': 1.1.1
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-select@2.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/number': 1.1.1
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
aria-hidden: 1.2.6
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.2.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-slot@1.2.4(@types/react@19.2.2)(react@19.2.0)': '@radix-ui/react-slot@1.2.4(@types/react@19.2.2)(react@19.2.0)':
dependencies: dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
@ -2344,6 +3015,71 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 19.2.2 '@types/react': 19.2.2
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.2.0)':
dependencies:
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.2.0)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.2.0)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-use-previous@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-use-rect@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
'@radix-ui/rect': 1.1.1
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-use-size@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.2
'@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
'@radix-ui/rect@1.1.1': {}
'@rtsao/scc@1.1.0': {} '@rtsao/scc@1.1.0': {}
'@swc/helpers@0.5.15': '@swc/helpers@0.5.15':
@ -2613,6 +3349,10 @@ snapshots:
argparse@2.0.1: {} argparse@2.0.1: {}
aria-hidden@1.2.6:
dependencies:
tslib: 2.8.1
aria-query@5.3.2: {} aria-query@5.3.2: {}
array-buffer-byte-length@1.0.2: array-buffer-byte-length@1.0.2:
@ -2791,6 +3531,10 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
is-data-view: 1.0.2 is-data-view: 1.0.2
date-fns-jalali@4.1.0-0: {}
date-fns@4.1.0: {}
debug@3.2.7: debug@3.2.7:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
@ -2815,6 +3559,8 @@ snapshots:
detect-libc@2.1.2: {} detect-libc@2.1.2: {}
detect-node-es@1.1.0: {}
doctrine@2.1.0: doctrine@2.1.0:
dependencies: dependencies:
esutils: 2.0.3 esutils: 2.0.3
@ -3235,6 +3981,8 @@ snapshots:
hasown: 2.0.2 hasown: 2.0.2
math-intrinsics: 1.1.0 math-intrinsics: 1.1.0
get-nonce@1.0.1: {}
get-proto@1.0.1: get-proto@1.0.1:
dependencies: dependencies:
dunder-proto: 1.0.1 dunder-proto: 1.0.1
@ -3728,6 +4476,13 @@ snapshots:
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
react-day-picker@9.11.1(react@19.2.0):
dependencies:
'@date-fns/tz': 1.4.1
date-fns: 4.1.0
date-fns-jalali: 4.1.0-0
react: 19.2.0
react-dom@19.2.0(react@19.2.0): react-dom@19.2.0(react@19.2.0):
dependencies: dependencies:
react: 19.2.0 react: 19.2.0
@ -3735,6 +4490,33 @@ snapshots:
react-is@16.13.1: {} react-is@16.13.1: {}
react-remove-scroll-bar@2.3.8(@types/react@19.2.2)(react@19.2.0):
dependencies:
react: 19.2.0
react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0)
tslib: 2.8.1
optionalDependencies:
'@types/react': 19.2.2
react-remove-scroll@2.7.1(@types/react@19.2.2)(react@19.2.0):
dependencies:
react: 19.2.0
react-remove-scroll-bar: 2.3.8(@types/react@19.2.2)(react@19.2.0)
react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0)
tslib: 2.8.1
use-callback-ref: 1.3.3(@types/react@19.2.2)(react@19.2.0)
use-sidecar: 1.1.3(@types/react@19.2.2)(react@19.2.0)
optionalDependencies:
'@types/react': 19.2.2
react-style-singleton@2.2.3(@types/react@19.2.2)(react@19.2.0):
dependencies:
get-nonce: 1.0.1
react: 19.2.0
tslib: 2.8.1
optionalDependencies:
'@types/react': 19.2.2
react@19.2.0: {} react@19.2.0: {}
reflect.getprototypeof@1.0.10: reflect.getprototypeof@1.0.10:
@ -4094,6 +4876,21 @@ snapshots:
dependencies: dependencies:
punycode: 2.3.1 punycode: 2.3.1
use-callback-ref@1.3.3(@types/react@19.2.2)(react@19.2.0):
dependencies:
react: 19.2.0
tslib: 2.8.1
optionalDependencies:
'@types/react': 19.2.2
use-sidecar@1.1.3(@types/react@19.2.2)(react@19.2.0):
dependencies:
detect-node-es: 1.1.0
react: 19.2.0
tslib: 2.8.1
optionalDependencies:
'@types/react': 19.2.2
which-boxed-primitive@1.1.1: which-boxed-primitive@1.1.1:
dependencies: dependencies:
is-bigint: 1.1.0 is-bigint: 1.1.0

BIN
public/hshot.jpeg Normal file

Binary file not shown.

BIN
public/large.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
public/mmmfil.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB