()
| 4 | import { Link } from "react-router-dom"; |
| 5 | |
| 6 | export default function Overview() { |
| 7 | const { subdomains, loading } = useDashboard(); |
| 8 | const { user } = useAuth(); |
| 9 | |
| 10 | const totalCount = subdomains.length; |
| 11 | const activeCount = subdomains.filter(d => d.status === "Active").length; |
| 12 | const expiringCount = subdomains.filter(d => { |
| 13 | if (!d.expiresAt) return false; |
| 14 | const days = Math.ceil((new Date(d.expiresAt) - new Date()) / 864e5); |
| 15 | return days > 0 && days <= 30; |
| 16 | }).length; |
| 17 | const expiredCount = subdomains.filter(d => d.status === "Expired").length; |
| 18 | |
| 19 | // Most recently created domains (up to 3) |
| 20 | const recentDomains = [...subdomains] |
| 21 | .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)) |
| 22 | .slice(0, 3); |
| 23 | |
| 24 | const greeting = () => { |
| 25 | const h = new Date().getHours(); |
| 26 | if (h < 12) return "Good morning"; |
| 27 | if (h < 17) return "Good afternoon"; |
| 28 | return "Good evening"; |
| 29 | }; |
| 30 | |
| 31 | if (loading && subdomains.length === 0) { |
| 32 | return ( |
| 33 | <div className="max-w-5xl animate-pulse space-y-6"> |
| 34 | <div className="h-8 bg-[#E5E3DF] rounded-lg w-48" /> |
| 35 | <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> |
| 36 | {[...Array(4)].map((_, i) => <div key={i} className="h-28 bg-[#E5E3DF] rounded-2xl" />)} |
| 37 | </div> |
| 38 | <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| 39 | {[...Array(4)].map((_, i) => <div key={i} className="h-20 bg-[#E5E3DF] rounded-2xl" />)} |
| 40 | </div> |
| 41 | </div> |
| 42 | ); |
| 43 | } |
| 44 | |
| 45 | return ( |
| 46 | <div className="max-w-5xl space-y-6"> |
| 47 | |
| 48 | {/* ── Greeting header ── */} |
| 49 | <div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-2"> |
| 50 | <div> |
| 51 | <p className="text-xs font-bold uppercase tracking-widest text-[#FF6B35] mb-1">Dashboard</p> |
| 52 | <h1 className="text-2xl md:text-3xl font-extrabold text-[#1A1A1A] dark:text-white leading-tight"> |
| 53 | {greeting()}, {user?.name?.split(" ")[0] || "there"} 👋 |
| 54 | </h1> |
| 55 | <p className="text-sm text-slate-900 dark:text-white mt-1"> |
| 56 | {totalCount === 0 ? "Register your first free domain to get started." : `You have ${totalCount} domain${totalCount > 1 ? "s" : ""} on Stackryze.`} |
| 57 | </p> |
| 58 | </div> |
| 59 | <Link |
| 60 | to="/register" |
| 61 | className="inline-flex items-center gap-2 self-start sm:self-auto px-4 py-2.5 bg-slate-900 text-white dark:bg-white dark:text-slate-900 text-sm font-bold rounded-xl hover:bg-slate-800 dark:hover:bg-slate-200 transition-colors shadow-sm" |
| 62 | > |
| 63 | <Plus className="w-4 h-4" /> |
nothing calls this directly
no test coverage detected