eny.space Landingpage
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(dashboard): add PDS status overview and adjust background

+159 -80
+74 -70
app/components/hero/hero-background.tsx
··· 4 4 import Image from "next/image"; 5 5 import { motion, useScroll, useTransform } from "framer-motion"; 6 6 7 - export function HeroBackground() { 7 + export function HeroBackground({ showPlanets = true }: { showPlanets?: boolean }) { 8 8 const { scrollYProgress } = useScroll(); 9 9 10 10 // Parallax ratios (approximate 10%, 30%, 80%) ··· 110 110 </div> 111 111 </motion.div> 112 112 113 - {/* LAYER 3 – Foreground planet (Jupiter image) */} 114 - <motion.div 115 - className="pointer-events-none fixed inset-0 -z-[1]" 116 - style={{ 117 - y: planetY, 118 - scale: planetScale, 119 - x: planetX, 120 - rotate: planetRotate, 121 - }} 122 - aria-hidden 123 - > 124 - <div className="absolute bottom-[-22%] right-[2%] h-[360px] w-[360px] overflow-hidden rounded-full opacity-80"> 125 - <Image 126 - src="/jupiter2.png" 127 - alt="Gas giant planet" 128 - fill 129 - priority 130 - className="object-cover object-center" 131 - /> 132 - </div> 133 - </motion.div> 113 + {showPlanets && ( 114 + <> 115 + {/* LAYER 3 – Foreground planet (Jupiter image) */} 116 + <motion.div 117 + className="pointer-events-none fixed inset-0 -z-[1]" 118 + style={{ 119 + y: planetY, 120 + scale: planetScale, 121 + x: planetX, 122 + rotate: planetRotate, 123 + }} 124 + aria-hidden 125 + > 126 + <div className="absolute bottom-[-22%] right-[2%] h-[360px] w-[360px] overflow-hidden rounded-full opacity-80"> 127 + <Image 128 + src="/jupiter2.png" 129 + alt="Gas giant planet" 130 + fill 131 + priority 132 + className="object-cover object-center" 133 + /> 134 + </div> 135 + </motion.div> 134 136 135 - {/* LAYER 3b – Secondary planets (Earth, Mars, Venus, Neptune images) */} 136 - <motion.div 137 - className="pointer-events-none fixed inset-0 -z-[1]" 138 - style={{ 139 - y: planetY, 140 - scale: planetScale, 141 - x: planetX, 142 - rotate: planetRotate, 143 - }} 144 - aria-hidden 145 - > 146 - <div className="absolute bottom-[10%] left-[6%] h-[220px] w-[220px] overflow-hidden rounded-full"> 147 - <Image 148 - src="/earth.png" 149 - alt="Blue planet" 150 - fill 151 - priority={false} 152 - className="object-cover object-center" 153 - /> 154 - </div> 155 - <div className="absolute top-[54%] right-[30%] h-[120px] w-[120px] overflow-hidden rounded-full opacity-80"> 156 - <Image 157 - src="/mars.png" 158 - alt="Red planet" 159 - fill 160 - priority={false} 161 - className="object-cover object-center" 162 - /> 163 - </div> 164 - <div className="absolute top-[50%] right-[20%] h-[70px] w-[100px] overflow-hidden rounded-full opacity-80"> 165 - <Image 166 - src="/venus.png" 167 - alt="Yellow planet" 168 - fill 169 - priority={false} 170 - className="object-cover object-center" 171 - /> 172 - </div> 173 - <div className="absolute top-[30%] left-[35%] h-[60px] w-[60px] overflow-hidden rounded-full opacity-80"> 174 - <Image 175 - src="/neptune.png" 176 - alt="Blue planet" 177 - fill 178 - priority={false} 179 - className="object-cover object-center" 180 - /> 181 - </div> 182 - </motion.div> 137 + {/* LAYER 3b – Secondary planets (Earth, Mars, Venus, Neptune images) */} 138 + <motion.div 139 + className="pointer-events-none fixed inset-0 -z-[1]" 140 + style={{ 141 + y: planetY, 142 + scale: planetScale, 143 + x: planetX, 144 + rotate: planetRotate, 145 + }} 146 + aria-hidden 147 + > 148 + <div className="absolute bottom-[10%] left-[6%] h-[220px] w-[220px] overflow-hidden rounded-full"> 149 + <Image 150 + src="/earth.png" 151 + alt="Blue planet" 152 + fill 153 + priority={false} 154 + className="object-cover object-center" 155 + /> 156 + </div> 157 + <div className="absolute top-[54%] right-[30%] h-[120px] w-[120px] overflow-hidden rounded-full opacity-80"> 158 + <Image 159 + src="/mars.png" 160 + alt="Red planet" 161 + fill 162 + priority={false} 163 + className="object-cover object-center" 164 + /> 165 + </div> 166 + <div className="absolute top-[50%] right-[20%] h-[70px] w-[100px] overflow-hidden rounded-full opacity-80"> 167 + <Image 168 + src="/venus.png" 169 + alt="Yellow planet" 170 + fill 171 + priority={false} 172 + className="object-cover object-center" 173 + /> 174 + </div> 175 + <div className="absolute top-[30%] left-[35%] h-[60px] w-[60px] overflow-hidden rounded-full opacity-80"> 176 + <Image 177 + src="/neptune.png" 178 + alt="Blue planet" 179 + fill 180 + priority={false} 181 + className="object-cover object-center" 182 + /> 183 + </div> 184 + </motion.div> 185 + </> 186 + )} 183 187 </> 184 188 ); 185 189 }
+12
app/components/site-background.tsx
··· 1 + "use client"; 2 + 3 + import { usePathname } from "next/navigation"; 4 + import { HeroBackground } from "@/components/hero"; 5 + 6 + export function SiteBackground() { 7 + const pathname = usePathname(); 8 + const isLanding = pathname === "/"; 9 + 10 + return <HeroBackground showPlanets={isLanding} />; 11 + } 12 +
+69 -8
app/dashboard/page.tsx
··· 8 8 CardTitle, 9 9 CardDescription, 10 10 } from "@/actions/components/ui/card"; 11 + import { Button } from "@/actions/components/ui/button"; 11 12 import DashboardClient from "./dashboard-client"; 12 13 13 14 export default async function DashboardPage() { ··· 22 23 23 24 const { subscribed, subscription } = await getSubscriptionStatus(); 24 25 26 + // Simple stubbed PDS status derived from subscription state 27 + const pdsStatus = subscribed ? "active" : "provisioning"; 28 + const pdsHostname = 29 + user.email?.split("@")[0]?.toLowerCase().replace(/[^a-z0-9-]/g, "-") + 30 + ".eny.space" || "pending.eny.space"; 31 + const pdsDashboardUrl = `https://${pdsHostname}`; 32 + 25 33 return ( 26 34 <main className="flex flex-col gap-6 px-4 py-6"> 27 35 <Card> 28 36 <CardHeader> 29 - <CardTitle>Dashboard</CardTitle> 30 - <CardDescription>Welcome back, {user.email}.</CardDescription> 37 + <CardTitle>My PDS</CardTitle> 38 + <CardDescription> 39 + Authenticated as {user.email}. This is your Personal Data Server 40 + overview. 41 + </CardDescription> 31 42 </CardHeader> 32 - <CardContent> 33 - <DashboardClient 34 - subscribed={subscribed} 35 - subscription={subscription} 36 - priceId={process.env.NEXT_PUBLIC_STRIPE_PRICE_ID || ""} 37 - /> 43 + <CardContent className="space-y-4"> 44 + <div className="grid gap-4 md:grid-cols-2"> 45 + <div className="space-y-2"> 46 + <p className="text-sm font-medium text-muted-foreground"> 47 + Status 48 + </p> 49 + <p className="text-base font-semibold capitalize">{pdsStatus}</p> 50 + </div> 51 + <div className="space-y-2"> 52 + <p className="text-sm font-medium text-muted-foreground"> 53 + URL / Hostname 54 + </p> 55 + <a 56 + href={pdsDashboardUrl} 57 + target="_blank" 58 + rel="noreferrer" 59 + className="text-base font-semibold text-primary underline underline-offset-2" 60 + > 61 + {pdsHostname} 62 + </a> 63 + </div> 64 + </div> 65 + 66 + <div className="mt-4 space-y-2 rounded-md border bg-muted/40 p-4"> 67 + <p className="text-sm font-medium text-muted-foreground"> 68 + Usage summary 69 + </p> 70 + <p className="text-sm text-muted-foreground"> 71 + Usage analytics for your PDS (storage, bandwidth, user count, and 72 + more) will appear here. 73 + </p> 74 + </div> 75 + 76 + <div className="mt-4 flex flex-wrap gap-3"> 77 + <Button asChild variant="outline"> 78 + <a href="/dashboard/manage">Manage</a> 79 + </Button> 80 + <Button asChild variant="default"> 81 + <a href={pdsDashboardUrl} target="_blank" rel="noreferrer"> 82 + Open dashboard 83 + </a> 84 + </Button> 85 + </div> 86 + 87 + <hr className="my-6" /> 88 + 89 + <section className="space-y-2"> 90 + <h2 className="text-sm font-semibold uppercase tracking-wide text-muted-foreground"> 91 + Billing & Subscription 92 + </h2> 93 + <DashboardClient 94 + subscribed={subscribed} 95 + subscription={subscription} 96 + priceId={process.env.NEXT_PUBLIC_STRIPE_PRICE_ID || ""} 97 + /> 98 + </section> 38 99 </CardContent> 39 100 </Card> 40 101 </main>
+4 -2
app/layout.tsx
··· 4 4 import { createClient } from "@/lib/supabase/server"; 5 5 import { SiteHeader } from "@/components/site-header"; 6 6 import { Footer } from "@/components/footer"; 7 + import { SiteBackground } from "@/components/site-background"; 7 8 8 9 import "./globals.css"; 9 10 import { Doto, Fira_Mono } from "next/font/google"; ··· 42 43 43 44 return ( 44 45 <html lang="en" className={`${firaMono.variable} ${doto.variable}`}> 45 - <body> 46 + <body className="min-h-screen flex flex-col"> 47 + <SiteBackground /> 46 48 <SiteHeader user={user} /> 47 - {children} 49 + <main className="flex-1">{children}</main> 48 50 <Footer /> 49 51 <SpeedInsights /> 50 52 <Analytics />