Integrate dark mode support across admin components, including Header, Notifications, SideNav, and Booking pages. Update styles to ensure visual consistency and responsiveness based on the selected theme, enhancing user experience in both light and dark modes.
This commit is contained in:
parent
f4c3fe0c51
commit
35e654cf46
@ -4,11 +4,7 @@ import { useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
import {
|
import {
|
||||||
Inbox,
|
Inbox,
|
||||||
Calendar,
|
Calendar,
|
||||||
@ -19,12 +15,16 @@ import {
|
|||||||
Settings,
|
Settings,
|
||||||
LogOut,
|
LogOut,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [notificationsOpen, setNotificationsOpen] = useState(false);
|
const [notificationsOpen, setNotificationsOpen] = useState(false);
|
||||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
|
|
||||||
// Mock notifications data
|
// Mock notifications data
|
||||||
const notifications = [
|
const notifications = [
|
||||||
@ -49,15 +49,15 @@ export function Header() {
|
|||||||
const unreadCount = notifications.filter((n) => !n.read).length;
|
const unreadCount = notifications.filter((n) => !n.read).length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="bg-white border-b border-gray-200 fixed top-0 left-0 right-0 z-50">
|
<header className={`fixed top-0 left-0 right-0 z-50 ${isDark ? "bg-gray-900 border-gray-800" : "bg-white border-gray-200"} border-b`}>
|
||||||
<div className="px-3 sm:px-4 md:px-6 lg:px-8">
|
<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">
|
<div className="flex items-center justify-between h-14 sm:h-16">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link href="/" className="flex items-center gap-2 sm:gap-3">
|
<Link href="/" 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">
|
<div className={`flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-lg ${isDark ? "bg-gray-800" : "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" />
|
<Heart className={`w-4 h-4 sm:w-6 sm:h-6 ${isDark ? "text-rose-400" : "text-rose-600"}`} fill="currentColor" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base sm:text-lg md:text-xl font-semibold text-gray-900 hidden sm:inline">Attune Heart</span>
|
<span className={`text-base sm:text-lg md:text-xl font-semibold hidden sm:inline ${isDark ? "text-white" : "text-gray-900"}`}>Attune Heart</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Navigation Links */}
|
{/* Navigation Links */}
|
||||||
@ -67,7 +67,9 @@ export function Header() {
|
|||||||
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 ${
|
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 === "/admin/dashboard"
|
pathname === "/admin/dashboard"
|
||||||
? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
|
? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
|
||||||
: "text-gray-600 hover:bg-gray-100"
|
: isDark
|
||||||
|
? "text-gray-300 hover:bg-gray-800"
|
||||||
|
: "text-gray-600 hover:bg-gray-100"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<LayoutGrid className="w-4 h-4 sm:w-5 sm:h-5" />
|
<LayoutGrid className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@ -78,7 +80,9 @@ export function Header() {
|
|||||||
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 ${
|
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 === "/admin/booking"
|
pathname === "/admin/booking"
|
||||||
? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
|
? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
|
||||||
: "text-gray-600 hover:bg-gray-100"
|
: isDark
|
||||||
|
? "text-gray-300 hover:bg-gray-800"
|
||||||
|
: "text-gray-600 hover:bg-gray-100"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Calendar className="w-4 h-4 sm:w-5 sm:h-5" />
|
<Calendar className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
@ -88,23 +92,24 @@ export function Header() {
|
|||||||
|
|
||||||
{/* Right Side Actions */}
|
{/* Right Side Actions */}
|
||||||
<div className="flex items-center gap-1.5 sm:gap-2 md:gap-3">
|
<div className="flex items-center gap-1.5 sm:gap-2 md:gap-3">
|
||||||
|
<ThemeToggle />
|
||||||
<Popover open={notificationsOpen} onOpenChange={setNotificationsOpen}>
|
<Popover open={notificationsOpen} onOpenChange={setNotificationsOpen}>
|
||||||
<PopoverTrigger asChild>
|
<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">
|
<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" />
|
<Inbox className={`w-5 h-5 sm:w-6 sm:h-6 md:w-8 md:h-8 ${isDark ? "text-gray-300" : "text-gray-600"}`} />
|
||||||
{unreadCount > 0 && (
|
{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>
|
<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>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-[calc(100vw-2rem)] sm:w-80 md:w-96 p-0 bg-white shadow-xl border border-gray-200" align="end">
|
<PopoverContent className={`w-[calc(100vw-2rem)] sm:w-80 md:w-96 p-0 shadow-xl border ${isDark ? "bg-gray-900 border-gray-800" : "bg-white border-gray-200"}`} align="end">
|
||||||
{/* Thumbtack Design at Top Right */}
|
{/* Thumbtack Design at Top Right */}
|
||||||
<div className="relative">
|
<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-2 right-8 w-4 h-4 rotate-45 ${isDark ? "bg-gray-900 border-l border-t border-gray-800" : "bg-white border-l border-t border-gray-200"}`}></div>
|
||||||
<div className="absolute -top-1 right-8 w-2 h-2 bg-white translate-x-1/2"></div>
|
<div className={`absolute -top-1 right-8 w-2 h-2 translate-x-1/2 ${isDark ? "bg-gray-900" : "bg-white"}`}></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between p-4 border-b">
|
<div className={`flex items-center justify-between p-4 border-b ${isDark ? "border-gray-800" : ""}`}>
|
||||||
<h3 className="font-semibold text-gray-900">Notifications</h3>
|
<h3 className={`font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>Notifications</h3>
|
||||||
{unreadCount > 0 && (
|
{unreadCount > 0 && (
|
||||||
<span className="px-2 py-1 text-xs font-medium bg-rose-100 text-rose-700 rounded-full">
|
<span className="px-2 py-1 text-xs font-medium bg-rose-100 text-rose-700 rounded-full">
|
||||||
{unreadCount} new
|
{unreadCount} new
|
||||||
@ -113,24 +118,28 @@ export function Header() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="max-h-96 overflow-y-auto">
|
<div className="max-h-96 overflow-y-auto">
|
||||||
{notifications.length === 0 ? (
|
{notifications.length === 0 ? (
|
||||||
<div className="p-8 text-center text-gray-500">
|
<div className={`p-8 text-center ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
<Bell className="w-12 h-12 mx-auto mb-2 text-gray-300" />
|
<Bell className={`w-12 h-12 mx-auto mb-2 ${isDark ? "text-gray-600" : "text-gray-300"}`} />
|
||||||
<p className="text-sm">No notifications</p>
|
<p className="text-sm">No notifications</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="divide-y">
|
<div className={`divide-y ${isDark ? "divide-gray-800" : ""}`}>
|
||||||
{notifications.map((notification) => {
|
{notifications.map((notification) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
className={`p-4 hover:bg-gray-50 transition-colors cursor-pointer ${
|
className={`p-4 transition-colors cursor-pointer ${
|
||||||
!notification.read ? "bg-rose-50/50" : ""
|
!notification.read
|
||||||
|
? isDark
|
||||||
|
? "bg-rose-500/10"
|
||||||
|
: "bg-rose-50/50"
|
||||||
|
: ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-start justify-between gap-2">
|
||||||
<p
|
<p
|
||||||
className={`text-sm font-medium text-gray-900 ${
|
className={`text-sm font-medium ${isDark ? "text-white" : "text-gray-900"} ${
|
||||||
!notification.read ? "font-semibold" : ""
|
!notification.read ? "font-semibold" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -140,10 +149,10 @@ export function Header() {
|
|||||||
<span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span>
|
<span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 mt-1">
|
<p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-600"}`}>
|
||||||
{notification.message}
|
{notification.message}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-400 mt-1">
|
<p className={`text-xs mt-1 ${isDark ? "text-gray-500" : "text-gray-400"}`}>
|
||||||
{notification.time}
|
{notification.time}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -153,11 +162,11 @@ export function Header() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 border-t bg-gray-50">
|
<div className={`p-3 border-t ${isDark ? "border-gray-800 bg-gray-900/80" : "bg-gray-50"}`}>
|
||||||
<Link
|
<Link
|
||||||
href="/admin/notifications"
|
href="/admin/notifications"
|
||||||
onClick={() => setNotificationsOpen(false)}
|
onClick={() => setNotificationsOpen(false)}
|
||||||
className="block w-full text-center text-sm font-medium text-rose-600 hover:text-rose-700 hover:underline transition-colors"
|
className={`block w-full text-center text-sm font-medium hover:underline transition-colors ${isDark ? "text-rose-300 hover:text-rose-200" : "text-rose-600 hover:text-rose-700"}`}
|
||||||
>
|
>
|
||||||
View all notifications
|
View all notifications
|
||||||
</Link>
|
</Link>
|
||||||
@ -166,15 +175,23 @@ export function Header() {
|
|||||||
</Popover>
|
</Popover>
|
||||||
<Popover open={userMenuOpen} onOpenChange={setUserMenuOpen}>
|
<Popover open={userMenuOpen} onOpenChange={setUserMenuOpen}>
|
||||||
<PopoverTrigger asChild>
|
<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">
|
<Button
|
||||||
<UserCog className="w-4 h-4 sm:w-5 sm:h-5 text-rose-600" />
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className={`rounded-full cursor-pointer w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10 ${
|
||||||
|
isDark
|
||||||
|
? "bg-gray-800 hover:bg-gray-700"
|
||||||
|
: "bg-linear-to-r from-rose-100 to-pink-100 hover:from-rose-200 hover:to-pink-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<UserCog className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? "text-rose-300" : "text-rose-600"}`} />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-56 sm:w-64 p-0 bg-white shadow-xl border border-gray-200" align="end">
|
<PopoverContent className={`w-56 sm:w-64 p-0 shadow-xl border ${isDark ? "bg-gray-900 border-gray-800" : "bg-white border-gray-200"}`} align="end">
|
||||||
{/* Thumbtack Design at Top Right */}
|
{/* Thumbtack Design at Top Right */}
|
||||||
<div className="relative">
|
<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-2 right-8 w-4 h-4 rotate-45 ${isDark ? "bg-gray-900 border-l border-t border-gray-800" : "bg-white border-l border-t border-gray-200"}`}></div>
|
||||||
<div className="absolute -top-1 right-8 w-2 h-2 bg-white translate-x-1/2"></div>
|
<div className={`absolute -top-1 right-8 w-2 h-2 translate-x-1/2 ${isDark ? "bg-gray-900" : "bg-white"}`}></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-2">
|
<div className="py-2">
|
||||||
<Button
|
<Button
|
||||||
@ -183,10 +200,12 @@ export function Header() {
|
|||||||
setUserMenuOpen(false);
|
setUserMenuOpen(false);
|
||||||
router.push("/admin/settings");
|
router.push("/admin/settings");
|
||||||
}}
|
}}
|
||||||
className="w-full flex items-center gap-3 px-4 py-3 justify-start hover:bg-gray-50 transition-colors cursor-pointer"
|
className={`w-full flex items-center gap-3 px-4 py-3 justify-start transition-colors cursor-pointer ${
|
||||||
|
isDark ? "hover:bg-gray-800" : "hover:bg-gray-50"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<Settings className="w-5 h-5 text-gray-600" />
|
<Settings className={`w-5 h-5 ${isDark ? "text-gray-300" : "text-gray-600"}`} />
|
||||||
<span className="text-sm font-medium text-gray-900">Settings</span>
|
<span className={`text-sm font-medium ${isDark ? "text-white" : "text-gray-900"}`}>Settings</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@ -194,7 +213,9 @@ export function Header() {
|
|||||||
setUserMenuOpen(false);
|
setUserMenuOpen(false);
|
||||||
router.push("/");
|
router.push("/");
|
||||||
}}
|
}}
|
||||||
className="w-full flex items-center gap-3 px-4 py-3 justify-start hover:bg-gray-50 transition-colors cursor-pointer"
|
className={`w-full flex items-center gap-3 px-4 py-3 justify-start transition-colors cursor-pointer ${
|
||||||
|
isDark ? "hover:bg-gray-800" : "hover:bg-gray-50"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<LogOut className="w-5 h-5 text-red-500" />
|
<LogOut className="w-5 h-5 text-red-500" />
|
||||||
<span className="text-sm font-medium text-red-500">Logout</span>
|
<span className="text-sm font-medium text-red-500">Logout</span>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
Clock,
|
Clock,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export interface Notification {
|
export interface Notification {
|
||||||
id: string;
|
id: string;
|
||||||
@ -36,30 +37,32 @@ export function Notifications({
|
|||||||
onMarkAllAsRead,
|
onMarkAllAsRead,
|
||||||
}: NotificationsProps) {
|
}: NotificationsProps) {
|
||||||
const unreadCount = notifications.filter((n) => !n.read).length;
|
const unreadCount = notifications.filter((n) => !n.read).length;
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
|
|
||||||
const getIcon = (type: Notification["type"]) => {
|
const getIcon = (type: Notification["type"]) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "success":
|
case "success":
|
||||||
return <CheckCircle className="w-5 h-5 text-green-600" />;
|
return <CheckCircle className={`w-5 h-5 ${isDark ? "text-green-300" : "text-green-600"}`} />;
|
||||||
case "warning":
|
case "warning":
|
||||||
return <AlertCircle className="w-5 h-5 text-orange-600" />;
|
return <AlertCircle className={`w-5 h-5 ${isDark ? "text-orange-300" : "text-orange-600"}`} />;
|
||||||
case "info":
|
case "info":
|
||||||
return <Info className="w-5 h-5 text-blue-600" />;
|
return <Info className={`w-5 h-5 ${isDark ? "text-blue-300" : "text-blue-600"}`} />;
|
||||||
case "appointment":
|
case "appointment":
|
||||||
return <Calendar className="w-5 h-5 text-rose-600" />;
|
return <Calendar className={`w-5 h-5 ${isDark ? "text-rose-300" : "text-rose-600"}`} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBgColor = (type: Notification["type"]) => {
|
const getBgColor = (type: Notification["type"]) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "success":
|
case "success":
|
||||||
return "bg-[#4A90A4]/10 border-[#4A90A4]/30";
|
return isDark ? "bg-green-500/10 border-green-500/30" : "bg-[#4A90A4]/10 border-[#4A90A4]/30";
|
||||||
case "warning":
|
case "warning":
|
||||||
return "bg-rose-100 border-rose-300";
|
return isDark ? "bg-rose-500/10 border-rose-400/40" : "bg-rose-100 border-rose-300";
|
||||||
case "info":
|
case "info":
|
||||||
return "bg-pink-50 border-pink-200";
|
return isDark ? "bg-pink-500/10 border-pink-400/40" : "bg-pink-50 border-pink-200";
|
||||||
case "appointment":
|
case "appointment":
|
||||||
return "bg-gradient-to-br from-rose-50 to-pink-50 border-rose-300";
|
return isDark ? "bg-rose-500/10 border-rose-400/40" : "bg-gradient-to-br from-rose-50 to-pink-50 border-rose-300";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,8 +71,8 @@ export function Notifications({
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Bell className="w-6 h-6 text-gray-900" />
|
<Bell className={`w-6 h-6 ${isDark ? "text-white" : "text-gray-900"}`} />
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Notifications</h2>
|
<h2 className={`text-2xl font-bold ${isDark ? "text-white" : "text-gray-900"}`}>Notifications</h2>
|
||||||
{unreadCount > 0 && (
|
{unreadCount > 0 && (
|
||||||
<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">
|
<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}
|
||||||
@ -77,7 +80,12 @@ export function Notifications({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{unreadCount > 0 && onMarkAllAsRead && (
|
{unreadCount > 0 && onMarkAllAsRead && (
|
||||||
<Button variant="outline" size="sm" onClick={onMarkAllAsRead}>
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={onMarkAllAsRead}
|
||||||
|
className={isDark ? "border-gray-700 text-gray-200 hover:bg-gray-800" : ""}
|
||||||
|
>
|
||||||
Mark all as read
|
Mark all as read
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@ -87,8 +95,8 @@ export function Notifications({
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{notifications.length === 0 ? (
|
{notifications.length === 0 ? (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<Bell className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
<Bell className={`w-12 h-12 mx-auto mb-4 ${isDark ? "text-gray-600" : "text-gray-400"}`} />
|
||||||
<p className="text-gray-600">No notifications</p>
|
<p className={isDark ? "text-gray-400" : "text-gray-600"}>No notifications</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
notifications.map((notification) => (
|
notifications.map((notification) => (
|
||||||
@ -97,7 +105,7 @@ export function Notifications({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"p-4 rounded-lg border-2 transition-all",
|
"p-4 rounded-lg border-2 transition-all",
|
||||||
getBgColor(notification.type),
|
getBgColor(notification.type),
|
||||||
!notification.read && "ring-2 ring-offset-2 ring-rose-300"
|
!notification.read && (isDark ? "ring-2 ring-offset-2 ring-rose-400 ring-offset-gray-900" : "ring-2 ring-offset-2 ring-rose-300")
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
@ -108,13 +116,15 @@ export function Notifications({
|
|||||||
<h3
|
<h3
|
||||||
className={cn(
|
className={cn(
|
||||||
"font-semibold mb-1",
|
"font-semibold mb-1",
|
||||||
!notification.read ? "text-gray-900" : "text-gray-700"
|
!notification.read
|
||||||
|
? isDark ? "text-white" : "text-gray-900"
|
||||||
|
: isDark ? "text-gray-300" : "text-gray-700"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{notification.title}
|
{notification.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mb-2">{notification.message}</p>
|
<p className={`text-sm mb-2 ${isDark ? "text-gray-400" : "text-gray-600"}`}>{notification.message}</p>
|
||||||
<div className="flex items-center gap-2 text-xs text-gray-500">
|
<div className={`flex items-center gap-2 text-xs ${isDark ? "text-gray-500" : "text-gray-500"}`}>
|
||||||
<Clock className="w-3 h-3" />
|
<Clock className="w-3 h-3" />
|
||||||
{notification.time}
|
{notification.time}
|
||||||
</div>
|
</div>
|
||||||
@ -125,7 +135,7 @@ export function Notifications({
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
onClick={() => onMarkAsRead(notification.id)}
|
onClick={() => onMarkAsRead(notification.id)}
|
||||||
className="h-7 w-7"
|
className={`h-7 w-7 ${isDark ? "text-gray-300 hover:bg-gray-800" : ""}`}
|
||||||
>
|
>
|
||||||
<CheckCircle className="w-4 h-4" />
|
<CheckCircle className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
@ -135,7 +145,7 @@ export function Notifications({
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
onClick={() => onDismiss(notification.id)}
|
onClick={() => onDismiss(notification.id)}
|
||||||
className="h-7 w-7"
|
className={`h-7 w-7 ${isDark ? "text-gray-300 hover:bg-gray-800" : ""}`}
|
||||||
>
|
>
|
||||||
<X className="w-4 h-4" />
|
<X className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
X,
|
X,
|
||||||
Heart,
|
Heart,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ label: "Dashboard", icon: LayoutGrid, href: "/admin/dashboard" },
|
{ label: "Dashboard", icon: LayoutGrid, href: "/admin/dashboard" },
|
||||||
@ -23,6 +24,8 @@ export default function SideNav() {
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
|
|
||||||
const getActiveIndex = () => {
|
const getActiveIndex = () => {
|
||||||
return navItems.findIndex((item) => pathname?.includes(item.href)) ?? -1;
|
return navItems.findIndex((item) => pathname?.includes(item.href)) ?? -1;
|
||||||
@ -43,12 +46,12 @@ export default function SideNav() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* 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 z-30 fixed top-0 left-0 right-0 ${isDark ? "bg-gray-900 border-gray-800" : "bg-white border-gray-200"}`}>
|
||||||
<Link href="/" className="flex items-center gap-3">
|
<Link href="/" className="flex items-center gap-3">
|
||||||
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-linear-to-r from-rose-100 to-pink-100">
|
<div className={`flex items-center justify-center w-8 h-8 rounded-lg ${isDark ? "bg-gray-800" : "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 ${isDark ? "text-rose-300" : "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 ${isDark ? "text-white" : "text-gray-900"}`}>Attune Heart Therapy</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@ -70,21 +73,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-[200px] min-w-[160px] md:translate-x-0 md:w-[200px] md:min-w-[200px] md:max-w-[200px] ${
|
className={`fixed top-0 left-0 z-50 h-screen 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] ${isDark ? "bg-gray-900 border-r border-gray-800" : "bg-white border-r border-gray-200"} ${
|
||||||
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="shrink-0 px-3 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">
|
||||||
<Link href="/" className="flex items-center gap-2 mb-1 ml-2 md:ml-3">
|
<Link href="/" className="flex items-center gap-2 mb-1 ml-2 md:ml-3">
|
||||||
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-linear-to-r from-rose-100 to-pink-100">
|
<div className={`flex items-center justify-center w-8 h-8 rounded-lg ${isDark ? "bg-gray-800" : "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 ${isDark ? "text-rose-300" : "text-rose-600"}`} fill="currentColor" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-semibold text-gray-900">Attune Heart</span>
|
<span className={`text-sm font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>Attune Heart</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr className="shrink-0 -mt-10 mb-4 mx-3 border-gray-200 md:block hidden" />
|
<hr className={`shrink-0 -mt-10 mb-4 mx-3 md:block hidden ${isDark ? "border-gray-800" : "border-gray-200"}`} />
|
||||||
|
|
||||||
{/* 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">
|
||||||
@ -106,7 +109,9 @@ export default function SideNav() {
|
|||||||
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 ${
|
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-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 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-rose-50 hover:text-rose-600 rounded-lg"
|
: isDark
|
||||||
|
? "bg-transparent text-gray-300 hover:bg-gray-800 hover:text-rose-300 rounded-lg"
|
||||||
|
: "bg-transparent text-gray-600 hover:bg-rose-50 hover:text-rose-600 rounded-lg"
|
||||||
}`}
|
}`}
|
||||||
style={isActive ? { height: 40 } : {}}
|
style={isActive ? { height: 40 } : {}}
|
||||||
>
|
>
|
||||||
@ -116,7 +121,9 @@ export default function SideNav() {
|
|||||||
className={
|
className={
|
||||||
isActive
|
isActive
|
||||||
? "text-white"
|
? "text-white"
|
||||||
: "text-gray-700 group-hover:text-rose-600"
|
: isDark
|
||||||
|
? "text-gray-400 group-hover:text-rose-300"
|
||||||
|
: "text-gray-700 group-hover:text-rose-600"
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
@ -131,7 +138,7 @@ export default function SideNav() {
|
|||||||
})}
|
})}
|
||||||
|
|
||||||
{/* Bottom Actions */}
|
{/* Bottom Actions */}
|
||||||
<div className="mt-auto pt-4 pb-4 border-t border-gray-200">
|
<div className={`mt-auto pt-4 pb-4 border-t ${isDark ? "border-gray-800" : "border-gray-200"}`}>
|
||||||
<div className="relative flex items-center w-full">
|
<div className="relative flex items-center w-full">
|
||||||
{pathname === "/admin/settings" && (
|
{pathname === "/admin/settings" && (
|
||||||
<span
|
<span
|
||||||
@ -145,7 +152,9 @@ export default function SideNav() {
|
|||||||
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 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 rounded-lg ${
|
||||||
pathname === "/admin/settings"
|
pathname === "/admin/settings"
|
||||||
? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm"
|
? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm"
|
||||||
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900"
|
: isDark
|
||||||
|
? "text-gray-300 hover:bg-gray-800 hover:text-rose-300"
|
||||||
|
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900"
|
||||||
}`}
|
}`}
|
||||||
style={pathname === "/admin/settings" ? { height: 40 } : {}}
|
style={pathname === "/admin/settings" ? { height: 40 } : {}}
|
||||||
>
|
>
|
||||||
@ -155,7 +164,9 @@ export default function SideNav() {
|
|||||||
className={
|
className={
|
||||||
pathname === "/admin/settings"
|
pathname === "/admin/settings"
|
||||||
? "text-white"
|
? "text-white"
|
||||||
: "text-gray-700 group-hover:text-gray-900"
|
: isDark
|
||||||
|
? "text-gray-400 group-hover:text-rose-300"
|
||||||
|
: "text-gray-700 group-hover:text-gray-900"
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
|
<span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
|
||||||
@ -169,9 +180,13 @@ export default function SideNav() {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
router.push("/");
|
router.push("/");
|
||||||
}}
|
}}
|
||||||
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"
|
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 rounded-lg ${
|
||||||
|
isDark
|
||||||
|
? "text-gray-300 hover:bg-gray-800 hover:text-rose-300"
|
||||||
|
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<LogOut size={18} strokeWidth={1.5} className="text-gray-700 group-hover:text-gray-900" />
|
<LogOut size={18} strokeWidth={1.5} className={isDark ? "text-gray-400 group-hover:text-rose-300" : "text-gray-700 group-hover:text-gray-900"} />
|
||||||
<span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
|
<span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
|
||||||
Logout
|
Logout
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
FileText,
|
FileText,
|
||||||
MoreVertical,
|
MoreVertical,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
ID: number;
|
ID: number;
|
||||||
@ -54,6 +55,8 @@ export default function Booking() {
|
|||||||
const [bookings, setBookings] = useState<Booking[]>([]);
|
const [bookings, setBookings] = useState<Booking[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Simulate API call
|
// Simulate API call
|
||||||
@ -127,7 +130,22 @@ export default function Booking() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
switch (status.toLowerCase()) {
|
const normalized = status.toLowerCase();
|
||||||
|
if (isDark) {
|
||||||
|
switch (normalized) {
|
||||||
|
case "scheduled":
|
||||||
|
return "bg-blue-500/20 text-blue-200";
|
||||||
|
case "completed":
|
||||||
|
return "bg-green-500/20 text-green-200";
|
||||||
|
case "cancelled":
|
||||||
|
return "bg-red-500/20 text-red-200";
|
||||||
|
case "pending":
|
||||||
|
return "bg-yellow-500/20 text-yellow-200";
|
||||||
|
default:
|
||||||
|
return "bg-gray-700 text-gray-200";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (normalized) {
|
||||||
case "scheduled":
|
case "scheduled":
|
||||||
return "bg-blue-100 text-blue-700";
|
return "bg-blue-100 text-blue-700";
|
||||||
case "completed":
|
case "completed":
|
||||||
@ -142,7 +160,20 @@ export default function Booking() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getPaymentStatusColor = (status: string) => {
|
const getPaymentStatusColor = (status: string) => {
|
||||||
switch (status.toLowerCase()) {
|
const normalized = status.toLowerCase();
|
||||||
|
if (isDark) {
|
||||||
|
switch (normalized) {
|
||||||
|
case "paid":
|
||||||
|
return "bg-green-500/20 text-green-200";
|
||||||
|
case "pending":
|
||||||
|
return "bg-yellow-500/20 text-yellow-200";
|
||||||
|
case "failed":
|
||||||
|
return "bg-red-500/20 text-red-200";
|
||||||
|
default:
|
||||||
|
return "bg-gray-700 text-gray-200";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (normalized) {
|
||||||
case "paid":
|
case "paid":
|
||||||
return "bg-green-100 text-green-700";
|
return "bg-green-100 text-green-700";
|
||||||
case "pending":
|
case "pending":
|
||||||
@ -166,102 +197,104 @@ export default function Booking() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className={`min-h-screen ${isDark ? "bg-gray-900" : "bg-gray-50"}`}>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="p-3 sm:p-4 md:p-6 lg:p-8">
|
<main className="p-3 sm:p-4 md:p-6 lg:p-8">
|
||||||
{/* Page Header */}
|
{/* 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="mb-4 sm:mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl sm:text-2xl font-semibold text-gray-900 mb-1">
|
<h1 className={`text-xl sm:text-2xl font-semibold mb-1 ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
Bookings
|
Bookings
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-xs sm:text-sm text-gray-500">
|
<p className={`text-xs sm:text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Manage and view all appointment bookings
|
Manage and view all appointment bookings
|
||||||
</p>
|
</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">
|
<button className={`w-full sm:w-auto px-3 sm:px-4 py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${
|
||||||
|
isDark ? "bg-rose-500 text-white hover:bg-rose-600" : "bg-gray-900 text-white hover:bg-gray-800"
|
||||||
|
}`}>
|
||||||
+ New Booking
|
+ New Booking
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-12">
|
<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 className={`animate-spin rounded-full h-8 w-8 border-b-2 ${isDark ? "border-gray-600" : "border-gray-400"}`}></div>
|
||||||
</div>
|
</div>
|
||||||
) : filteredBookings.length === 0 ? (
|
) : filteredBookings.length === 0 ? (
|
||||||
<div className="bg-white rounded-lg border border-gray-200 p-12 text-center">
|
<div className={`rounded-lg border p-12 text-center ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
||||||
<Calendar className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
<Calendar className={`w-12 h-12 mx-auto mb-4 ${isDark ? "text-gray-500" : "text-gray-400"}`} />
|
||||||
<p className="text-gray-600 font-medium mb-1">No bookings found</p>
|
<p className={`font-medium mb-1 ${isDark ? "text-gray-200" : "text-gray-600"}`}>No bookings found</p>
|
||||||
<p className="text-sm text-gray-500">
|
<p className={`text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
{searchTerm
|
{searchTerm
|
||||||
? "Try adjusting your search terms"
|
? "Try adjusting your search terms"
|
||||||
: "Create a new booking to get started"}
|
: "Create a new booking to get started"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
|
<div className={`rounded-lg border overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="w-full">
|
<table className="w-full">
|
||||||
<thead className="bg-gray-50 border-b border-gray-200">
|
<thead className={`${isDark ? "bg-gray-800 border-b border-gray-700" : "bg-gray-50 border-b border-gray-200"}`}>
|
||||||
<tr>
|
<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">
|
<th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Patient
|
Patient
|
||||||
</th>
|
</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">
|
<th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider hidden md:table-cell ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Date & Time
|
Date & Time
|
||||||
</th>
|
</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">
|
<th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider hidden lg:table-cell ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Duration
|
Duration
|
||||||
</th>
|
</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">
|
<th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Status
|
Status
|
||||||
</th>
|
</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">
|
<th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider hidden lg:table-cell ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Payment
|
Payment
|
||||||
</th>
|
</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">
|
<th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider hidden xl:table-cell ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Amount
|
Amount
|
||||||
</th>
|
</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">
|
<th className={`px-3 sm:px-4 md:px-6 py-3 text-right text-xs font-medium uppercase tracking-wider ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Actions
|
Actions
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className={`${isDark ? "bg-gray-800 divide-gray-700" : "bg-white divide-gray-200"}`}>
|
||||||
{filteredBookings.map((booking) => (
|
{filteredBookings.map((booking) => (
|
||||||
<tr
|
<tr
|
||||||
key={booking.ID}
|
key={booking.ID}
|
||||||
className="hover:bg-gray-50 transition-colors"
|
className={`transition-colors ${isDark ? "hover:bg-gray-700" : "hover:bg-gray-50"}`}
|
||||||
>
|
>
|
||||||
<td className="px-3 sm:px-4 md:px-6 py-4">
|
<td className="px-3 sm:px-4 md:px-6 py-4">
|
||||||
<div className="flex items-center">
|
<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">
|
<div className={`shrink-0 h-8 w-8 sm:h-10 sm:w-10 rounded-full flex items-center justify-center ${isDark ? "bg-gray-700" : "bg-gray-100"}`}>
|
||||||
<User className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" />
|
<User className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? "text-gray-200" : "text-gray-600"}`} />
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2 sm:ml-4 min-w-0">
|
<div className="ml-2 sm:ml-4 min-w-0">
|
||||||
<div className="text-xs sm:text-sm font-medium text-gray-900 truncate">
|
<div className={`text-xs sm:text-sm font-medium truncate ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
{booking.user.first_name} {booking.user.last_name}
|
{booking.user.first_name} {booking.user.last_name}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs sm:text-sm text-gray-500 truncate hidden sm:block">
|
<div className={`text-xs sm:text-sm truncate hidden sm:block ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
{booking.user.email}
|
{booking.user.email}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500 sm:hidden mt-0.5">
|
<div className={`text-xs sm:hidden mt-0.5 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
{formatDate(booking.scheduled_at)}
|
{formatDate(booking.scheduled_at)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap hidden md:table-cell">
|
<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">
|
<div className={`text-xs sm:text-sm ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
{formatDate(booking.scheduled_at)}
|
{formatDate(booking.scheduled_at)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs sm:text-sm text-gray-500 flex items-center gap-1">
|
<div className={`text-xs sm:text-sm flex items-center gap-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
<Clock className="w-3 h-3" />
|
<Clock className="w-3 h-3" />
|
||||||
{formatTime(booking.scheduled_at)}
|
{formatTime(booking.scheduled_at)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</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">
|
<td className={`px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-xs sm:text-sm hidden lg:table-cell ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
{booking.duration} min
|
{booking.duration} min
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap">
|
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap">
|
||||||
@ -282,7 +315,7 @@ export default function Booking() {
|
|||||||
{booking.payment_status}
|
{booking.payment_status}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</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">
|
<td className={`px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-xs sm:text-sm font-medium hidden xl:table-cell ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
${booking.amount}
|
${booking.amount}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
@ -292,7 +325,7 @@ export default function Booking() {
|
|||||||
href={booking.jitsi_room_url}
|
href={booking.jitsi_room_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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"
|
className={`p-1.5 sm:p-2 rounded-lg transition-colors ${isDark ? "text-gray-300 hover:text-white hover:bg-gray-700" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`}
|
||||||
title="Join Meeting"
|
title="Join Meeting"
|
||||||
>
|
>
|
||||||
<Video className="w-4 h-4" />
|
<Video className="w-4 h-4" />
|
||||||
@ -300,13 +333,13 @@ export default function Booking() {
|
|||||||
)}
|
)}
|
||||||
{booking.notes && (
|
{booking.notes && (
|
||||||
<button
|
<button
|
||||||
className="p-1.5 sm:p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"
|
className={`p-1.5 sm:p-2 rounded-lg transition-colors ${isDark ? "text-gray-300 hover:text-white hover:bg-gray-700" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`}
|
||||||
title="View Notes"
|
title="View Notes"
|
||||||
>
|
>
|
||||||
<FileText className="w-4 h-4" />
|
<FileText className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button className="p-1.5 sm:p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors">
|
<button className={`p-1.5 sm:p-2 rounded-lg transition-colors ${isDark ? "text-gray-300 hover:text-white hover:bg-gray-700" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`}>
|
||||||
<MoreVertical className="w-4 h-4" />
|
<MoreVertical className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
ArrowUpRight,
|
ArrowUpRight,
|
||||||
ArrowDownRight,
|
ArrowDownRight,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
interface DashboardStats {
|
interface DashboardStats {
|
||||||
total_users: number;
|
total_users: number;
|
||||||
@ -35,6 +36,8 @@ export default function Dashboard() {
|
|||||||
const [stats, setStats] = useState<DashboardStats | null>(null);
|
const [stats, setStats] = useState<DashboardStats | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [timePeriod, setTimePeriod] = useState<string>("last_month");
|
const [timePeriod, setTimePeriod] = useState<string>("last_month");
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Simulate API call
|
// Simulate API call
|
||||||
@ -122,36 +125,43 @@ export default function Dashboard() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const getTrendClasses = (trendUp: boolean) => {
|
||||||
|
if (isDark) {
|
||||||
|
return trendUp ? "bg-green-500/20 text-green-200" : "bg-red-500/20 text-red-200";
|
||||||
|
}
|
||||||
|
return trendUp ? "bg-green-50 text-green-700" : "bg-red-50 text-red-700";
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className={`min-h-screen ${isDark ? "bg-gray-900" : "bg-gray-50"}`}>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="p-4 sm:p-6 lg:p-8 space-y-6">
|
<main className="p-4 sm:p-6 lg:p-8 space-y-6">
|
||||||
{/* Welcome Section */}
|
{/* Welcome Section */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold text-gray-900 mb-1">
|
<h1 className={`text-2xl font-semibold mb-1 ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
Welcome Back! Hammond
|
Welcome Back! Hammond
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-gray-500">
|
<p className={`text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Here's an overview of your practice today
|
Here's an overview of your practice today
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Select value={timePeriod} onValueChange={setTimePeriod}>
|
<Select value={timePeriod} onValueChange={setTimePeriod}>
|
||||||
<SelectTrigger className="w-[180px] cursor-pointer">
|
<SelectTrigger className={`w-full sm:w-[200px] cursor-pointer ${isDark ? "bg-gray-800 border-gray-700 text-gray-100" : "bg-white border-gray-200 text-gray-900"}`}>
|
||||||
<SelectValue placeholder="Select period" />
|
<SelectValue placeholder="Select period" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-white cursor-pointer">
|
<SelectContent className={`${isDark ? "bg-gray-800 border-gray-700 text-gray-100" : "bg-white border-gray-200 text-gray-900"} cursor-pointer`}>
|
||||||
<SelectItem value="last_week">Last Week</SelectItem>
|
<SelectItem className={isDark ? "focus:bg-gray-700" : ""} value="last_week">Last Week</SelectItem>
|
||||||
<SelectItem value="last_month">Last Month</SelectItem>
|
<SelectItem className={isDark ? "focus:bg-gray-700" : ""} value="last_month">Last Month</SelectItem>
|
||||||
<SelectItem value="last_year">Last Year</SelectItem>
|
<SelectItem className={isDark ? "focus:bg-gray-700" : ""} value="last_year">Last Year</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-12">
|
<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 className={`animate-spin rounded-full h-8 w-8 border-b-2 ${isDark ? "border-gray-600" : "border-gray-400"}`}></div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@ -162,17 +172,13 @@ export default function Dashboard() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="bg-white rounded-lg border border-gray-200 p-4 sm:p-5 md:p-6 hover:shadow-md transition-shadow"
|
className={`rounded-lg border p-4 sm:p-5 md:p-6 hover:shadow-md transition-shadow ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between mb-3 sm:mb-4">
|
<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">
|
<div className={`p-2 sm:p-2.5 rounded-lg ${isDark ? "bg-gray-700" : "bg-gray-50"}`}>
|
||||||
<Icon className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" />
|
<Icon className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? "text-gray-200" : "text-gray-600"}`} />
|
||||||
</div>
|
</div>
|
||||||
<div className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${
|
<div className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${getTrendClasses(card.trendUp)}`}>
|
||||||
card.trendUp
|
|
||||||
? "bg-green-50 text-green-700"
|
|
||||||
: "bg-red-50 text-red-700"
|
|
||||||
}`}>
|
|
||||||
{card.trendUp ? (
|
{card.trendUp ? (
|
||||||
<ArrowUpRight className="w-3 h-3" />
|
<ArrowUpRight className="w-3 h-3" />
|
||||||
) : (
|
) : (
|
||||||
@ -183,13 +189,13 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xs font-medium text-rose-600 mb-1 sm:mb-2 uppercase tracking-wider">
|
<h3 className={`text-xs font-medium mb-1 sm:mb-2 uppercase tracking-wider ${isDark ? "text-rose-300" : "text-rose-600"}`}>
|
||||||
{card.title}
|
{card.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xl sm:text-2xl font-bold text-gray-900 mb-1">
|
<p className={`text-xl sm:text-2xl font-bold mb-1 ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
{card.value}
|
{card.value}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500">
|
<p className={`text-xs ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
vs last month
|
vs last month
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Bell } from "lucide-react";
|
import { Bell } from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
interface Notification {
|
interface Notification {
|
||||||
id: string;
|
id: string;
|
||||||
@ -51,16 +52,18 @@ export default function NotificationsPage() {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const unreadCount = notifications.filter((n) => !n.read).length;
|
const unreadCount = notifications.filter((n) => !n.read).length;
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className={`min-h-screen ${isDark ? "bg-gray-900" : "bg-gray-50"}`}>
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="p-3 sm:p-4 md:p-6 lg:p-8">
|
<main className="p-3 sm:p-4 md:p-6 lg:p-8">
|
||||||
{/* Page Header */}
|
{/* 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="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">
|
<div className="flex items-center gap-3">
|
||||||
<Bell className="w-5 h-5 sm:w-6 sm:h-6 text-gray-900" />
|
<Bell className={`w-5 h-5 sm:w-6 sm:h-6 ${isDark ? "text-white" : "text-gray-900"}`} />
|
||||||
<h1 className="text-xl sm:text-2xl font-semibold text-gray-900">
|
<h1 className={`text-xl sm:text-2xl font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
Notifications
|
Notifications
|
||||||
</h1>
|
</h1>
|
||||||
{unreadCount > 0 && (
|
{unreadCount > 0 && (
|
||||||
@ -72,26 +75,30 @@ export default function NotificationsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Notifications List */}
|
{/* Notifications List */}
|
||||||
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
|
<div className={`rounded-lg border overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
||||||
{notifications.length === 0 ? (
|
{notifications.length === 0 ? (
|
||||||
<div className="p-8 sm:p-12 text-center text-gray-500">
|
<div className={`p-8 sm:p-12 text-center ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
<Bell className="w-12 h-12 mx-auto mb-2 text-gray-300" />
|
<Bell className={`w-12 h-12 mx-auto mb-2 ${isDark ? "text-gray-600" : "text-gray-300"}`} />
|
||||||
<p className="text-sm">No notifications</p>
|
<p className="text-sm">No notifications</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="divide-y">
|
<div className={`divide-y ${isDark ? "divide-gray-700" : ""}`}>
|
||||||
{notifications.map((notification) => {
|
{notifications.map((notification) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
className={`p-4 sm:p-6 hover:bg-gray-50 transition-colors cursor-pointer ${
|
className={`p-4 sm:p-6 transition-colors cursor-pointer ${
|
||||||
!notification.read ? "bg-rose-50/50" : ""
|
!notification.read
|
||||||
|
? isDark
|
||||||
|
? "bg-rose-500/10"
|
||||||
|
: "bg-rose-50/50"
|
||||||
|
: ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-start justify-between gap-2">
|
||||||
<p
|
<p
|
||||||
className={`text-sm sm:text-base font-medium text-gray-900 ${
|
className={`text-sm sm:text-base font-medium ${isDark ? "text-white" : "text-gray-900"} ${
|
||||||
!notification.read ? "font-semibold" : ""
|
!notification.read ? "font-semibold" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -101,10 +108,10 @@ export default function NotificationsPage() {
|
|||||||
<span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span>
|
<span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 mt-1">
|
<p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-600"}`}>
|
||||||
{notification.message}
|
{notification.message}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-400 mt-1">
|
<p className={`text-xs mt-1 ${isDark ? "text-gray-500" : "text-gray-400"}`}>
|
||||||
{notification.time}
|
{notification.time}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
EyeOff,
|
EyeOff,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export default function AdminSettingsPage() {
|
export default function AdminSettingsPage() {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -33,6 +34,8 @@ export default function AdminSettingsPage() {
|
|||||||
new: false,
|
new: false,
|
||||||
confirm: false,
|
confirm: false,
|
||||||
});
|
});
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
|
|
||||||
const handleInputChange = (field: string, value: string) => {
|
const handleInputChange = (field: string, value: string) => {
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
@ -88,22 +91,22 @@ export default function AdminSettingsPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className={`min-h-screen ${isDark ? "bg-gray-900" : "bg-gray-50"}`}>
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="p-4 sm:p-6 lg:p-8 space-y-6">
|
<main className="p-4 sm:p-6 lg:p-8 space-y-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Link href="/admin/dashboard">
|
<Link href="/admin/dashboard">
|
||||||
<Button variant="ghost" size="icon" className="hover:bg-gray-100">
|
<Button variant="ghost" size="icon" className={isDark ? "hover:bg-gray-800 text-gray-300" : "hover:bg-gray-100"}>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold text-gray-900 mb-1">
|
<h1 className={`text-2xl font-semibold mb-1 ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
Settings
|
Settings
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-gray-600">
|
<p className={`text-sm ${isDark ? "text-gray-400" : "text-gray-600"}`}>
|
||||||
Manage your account settings and practice information
|
Manage your account settings and practice information
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -111,7 +114,7 @@ export default function AdminSettingsPage() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
className="w-full sm:w-auto bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
||||||
@ -125,60 +128,60 @@ export default function AdminSettingsPage() {
|
|||||||
<div className="max-w-4xl mx-auto">
|
<div className="max-w-4xl mx-auto">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Profile Information */}
|
{/* Profile Information */}
|
||||||
<Card className="bg-white border-gray-200">
|
<Card className={isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<User className="w-5 h-5 text-gray-600" />
|
<User className={`w-5 h-5 ${isDark ? "text-gray-300" : "text-gray-600"}`} />
|
||||||
<CardTitle className="text-gray-900">Profile Information</CardTitle>
|
<CardTitle className={isDark ? "text-white" : "text-gray-900"}>Profile Information</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription className="text-gray-600">
|
<CardDescription className={isDark ? "text-gray-400" : "text-gray-600"}>
|
||||||
Update your personal information and contact details
|
Update your personal information and contact details
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? "text-gray-300" : "text-gray-700"}`}>
|
||||||
Full Name
|
Full Name
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<User className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<User className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? "text-gray-500" : "text-gray-400"}`} />
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.fullName}
|
value={formData.fullName}
|
||||||
onChange={(e) => handleInputChange("fullName", e.target.value)}
|
onChange={(e) => handleInputChange("fullName", e.target.value)}
|
||||||
className="pl-10"
|
className={`pl-10 ${isDark ? "bg-gray-700 border-gray-600 text-white placeholder:text-gray-400" : ""}`}
|
||||||
placeholder="Enter your full name"
|
placeholder="Enter your full name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? "text-gray-300" : "text-gray-700"}`}>
|
||||||
Email Address
|
Email Address
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Mail className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Mail className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? "text-gray-500" : "text-gray-400"}`} />
|
||||||
<Input
|
<Input
|
||||||
type="email"
|
type="email"
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={(e) => handleInputChange("email", e.target.value)}
|
onChange={(e) => handleInputChange("email", e.target.value)}
|
||||||
className="pl-10"
|
className={`pl-10 ${isDark ? "bg-gray-700 border-gray-600 text-white placeholder:text-gray-400" : ""}`}
|
||||||
placeholder="Enter your email"
|
placeholder="Enter your email"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? "text-gray-300" : "text-gray-700"}`}>
|
||||||
Phone Number
|
Phone Number
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Phone className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Phone className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? "text-gray-500" : "text-gray-400"}`} />
|
||||||
<Input
|
<Input
|
||||||
type="tel"
|
type="tel"
|
||||||
value={formData.phone}
|
value={formData.phone}
|
||||||
onChange={(e) => handleInputChange("phone", e.target.value)}
|
onChange={(e) => handleInputChange("phone", e.target.value)}
|
||||||
className="pl-10"
|
className={`pl-10 ${isDark ? "bg-gray-700 border-gray-600 text-white placeholder:text-gray-400" : ""}`}
|
||||||
placeholder="Enter your phone number"
|
placeholder="Enter your phone number"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -187,34 +190,34 @@ export default function AdminSettingsPage() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Change Password */}
|
{/* Change Password */}
|
||||||
<Card className="bg-white border-gray-200">
|
<Card className={isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Lock className="w-5 h-5 text-gray-600" />
|
<Lock className={`w-5 h-5 ${isDark ? "text-gray-300" : "text-gray-600"}`} />
|
||||||
<CardTitle className="text-gray-900">Change Password</CardTitle>
|
<CardTitle className={isDark ? "text-white" : "text-gray-900"}>Change Password</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription className="text-gray-600">
|
<CardDescription className={isDark ? "text-gray-400" : "text-gray-600"}>
|
||||||
Update your password to keep your account secure
|
Update your password to keep your account secure
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? "text-gray-300" : "text-gray-700"}`}>
|
||||||
Current Password
|
Current Password
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Lock className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? "text-gray-500" : "text-gray-400"}`} />
|
||||||
<Input
|
<Input
|
||||||
type={showPasswords.current ? "text" : "password"}
|
type={showPasswords.current ? "text" : "password"}
|
||||||
value={passwordData.currentPassword}
|
value={passwordData.currentPassword}
|
||||||
onChange={(e) => handlePasswordChange("currentPassword", e.target.value)}
|
onChange={(e) => handlePasswordChange("currentPassword", e.target.value)}
|
||||||
className="pl-10 pr-10"
|
className={`pl-10 pr-10 ${isDark ? "bg-gray-700 border-gray-600 text-white placeholder:text-gray-400" : ""}`}
|
||||||
placeholder="Enter your current password"
|
placeholder="Enter your current password"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => togglePasswordVisibility("current")}
|
onClick={() => togglePasswordVisibility("current")}
|
||||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
className={`absolute right-3 top-1/2 transform -translate-y-1/2 ${isDark ? "text-gray-400 hover:text-gray-300" : "text-gray-400 hover:text-gray-600"}`}
|
||||||
>
|
>
|
||||||
{showPasswords.current ? (
|
{showPasswords.current ? (
|
||||||
<EyeOff className="w-4 h-4" />
|
<EyeOff className="w-4 h-4" />
|
||||||
@ -226,22 +229,22 @@ export default function AdminSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? "text-gray-300" : "text-gray-700"}`}>
|
||||||
New Password
|
New Password
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Lock className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? "text-gray-500" : "text-gray-400"}`} />
|
||||||
<Input
|
<Input
|
||||||
type={showPasswords.new ? "text" : "password"}
|
type={showPasswords.new ? "text" : "password"}
|
||||||
value={passwordData.newPassword}
|
value={passwordData.newPassword}
|
||||||
onChange={(e) => handlePasswordChange("newPassword", e.target.value)}
|
onChange={(e) => handlePasswordChange("newPassword", e.target.value)}
|
||||||
className="pl-10 pr-10"
|
className={`pl-10 pr-10 ${isDark ? "bg-gray-700 border-gray-600 text-white placeholder:text-gray-400" : ""}`}
|
||||||
placeholder="Enter your new password"
|
placeholder="Enter your new password"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => togglePasswordVisibility("new")}
|
onClick={() => togglePasswordVisibility("new")}
|
||||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
className={`absolute right-3 top-1/2 transform -translate-y-1/2 ${isDark ? "text-gray-400 hover:text-gray-300" : "text-gray-400 hover:text-gray-600"}`}
|
||||||
>
|
>
|
||||||
{showPasswords.new ? (
|
{showPasswords.new ? (
|
||||||
<EyeOff className="w-4 h-4" />
|
<EyeOff className="w-4 h-4" />
|
||||||
@ -250,28 +253,28 @@ export default function AdminSettingsPage() {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500">
|
<p className={`text-xs ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
Password must be at least 8 characters long
|
Password must be at least 8 characters long
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? "text-gray-300" : "text-gray-700"}`}>
|
||||||
Confirm New Password
|
Confirm New Password
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Lock className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? "text-gray-500" : "text-gray-400"}`} />
|
||||||
<Input
|
<Input
|
||||||
type={showPasswords.confirm ? "text" : "password"}
|
type={showPasswords.confirm ? "text" : "password"}
|
||||||
value={passwordData.confirmPassword}
|
value={passwordData.confirmPassword}
|
||||||
onChange={(e) => handlePasswordChange("confirmPassword", e.target.value)}
|
onChange={(e) => handlePasswordChange("confirmPassword", e.target.value)}
|
||||||
className="pl-10 pr-10"
|
className={`pl-10 pr-10 ${isDark ? "bg-gray-700 border-gray-600 text-white placeholder:text-gray-400" : ""}`}
|
||||||
placeholder="Confirm your new password"
|
placeholder="Confirm your new password"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => togglePasswordVisibility("confirm")}
|
onClick={() => togglePasswordVisibility("confirm")}
|
||||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
className={`absolute right-3 top-1/2 transform -translate-y-1/2 ${isDark ? "text-gray-400 hover:text-gray-300" : "text-gray-400 hover:text-gray-600"}`}
|
||||||
>
|
>
|
||||||
{showPasswords.confirm ? (
|
{showPasswords.confirm ? (
|
||||||
<EyeOff className="w-4 h-4" />
|
<EyeOff className="w-4 h-4" />
|
||||||
|
|||||||
@ -98,7 +98,7 @@ export default function SettingsPage() {
|
|||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="container mx-auto px-4 sm:px-6 lg:px-8 space-y-6 pt-20 sm:pt-24 pb-8">
|
<main className="container mx-auto px-4 sm:px-6 lg:px-8 space-y-6 pt-20 sm:pt-24 pb-8">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Link href="/user/dashboard">
|
<Link href="/user/dashboard">
|
||||||
<Button variant="ghost" size="icon" className={isDark ? 'hover:bg-gray-800 text-gray-300' : 'hover:bg-gray-100'}>
|
<Button variant="ghost" size="icon" className={isDark ? 'hover:bg-gray-800 text-gray-300' : 'hover:bg-gray-100'}>
|
||||||
@ -117,7 +117,7 @@ export default function SettingsPage() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
className="w-full sm:w-auto bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user