11import { getActiveAnnouncements } from "@/actions/announcements" ;
22import { getPrometheusStatus } from "@/actions/prometheus-status" ;
33import { getUserFirstWatchDate } from "@/actions/users" ;
4- import { PlexSignInButton } from "@/components/auth/plex -sign-in-button " ;
4+ import { ServiceSignInToggle } from "@/components/auth/service -sign-in-toggle " ;
55import { UserDashboard } from "@/components/dashboard/user-dashboard" ;
66import { authOptions } from "@/lib/auth" ;
77import { prisma } from "@/lib/prisma" ;
8+ import type { AuthService } from "@/types/onboarding" ;
89import { getServerSession } from "next-auth" ;
910import { redirect } from "next/navigation" ;
11+ import { z } from "zod" ;
1012
1113export const dynamic = 'force-dynamic'
1214
15+ const OnboardingStatusSchema = z . object ( {
16+ plex : z . boolean ( ) ,
17+ jellyfin : z . boolean ( ) ,
18+ } )
19+
20+ interface OnboardingStatusRecord {
21+ plex : boolean
22+ jellyfin : boolean
23+ }
24+
1325export default async function Home ( ) {
1426 const session = await getServerSession ( authOptions ) ;
15- const [ plexServer , discordIntegration , announcements , overseerr , prometheusStatus ] = await Promise . all ( [
27+ const [ plexServer , jellyfinServer , discordIntegration , announcements , overseerr , prometheusStatus ] = await Promise . all ( [
1628 prisma . plexServer . findFirst ( {
1729 where : { isActive : true } ,
1830 } ) ,
31+ prisma . jellyfinServer . findFirst ( {
32+ where : { isActive : true } ,
33+ } ) ,
1934 prisma . discordIntegration . findUnique ( { where : { id : "discord" } } ) ,
2035 getActiveAnnouncements ( ) ,
2136 prisma . overseerr . findFirst ( { where : { isActive : true } } ) ,
2237 getPrometheusStatus ( ) ,
2338 ] ) ;
24- const serverName = plexServer ?. name || "Plex" ;
39+
40+ // Determine server name based on what's configured
41+ const serverName = plexServer ?. name || jellyfinServer ?. name || "Media Server" ;
2542 const discordEnabled = Boolean ( discordIntegration ?. isEnabled && discordIntegration ?. clientId && discordIntegration ?. clientSecret ) ;
2643 const overseerrUrl = overseerr ?. publicUrl || overseerr ?. url || null ;
2744
2845 // Handle redirect logic for authenticated users
2946 if ( session ?. user ?. id ) {
3047 const userPromise = prisma . user . findUnique ( {
3148 where : { id : session . user . id } ,
32- select : { onboardingCompleted : true , createdAt : true , plexUserId : true , email : true }
49+ select : {
50+ onboardingStatus : true ,
51+ primaryAuthService : true ,
52+ createdAt : true ,
53+ plexUserId : true ,
54+ jellyfinUserId : true ,
55+ email : true ,
56+ }
3357 } ) ;
3458 const discordConnectionPromise = discordEnabled
3559 ? prisma . discordConnection . findUnique ( { where : { userId : session . user . id } } )
3660 : Promise . resolve ( null ) ;
3761
3862 const [ user , discordConnection ] = await Promise . all ( [ userPromise , discordConnectionPromise ] ) ;
3963
40- if ( user && ! user . onboardingCompleted ) {
41- redirect ( "/onboarding" ) ;
64+ // Check service-specific onboarding completion
65+ if ( user ) {
66+ const validation = OnboardingStatusSchema . safeParse ( user . onboardingStatus )
67+ const status : OnboardingStatusRecord = validation . success
68+ ? validation . data
69+ : { plex : false , jellyfin : false } ;
70+ const primaryService : AuthService = ( user . primaryAuthService as AuthService ) || "plex" ;
71+
72+ // Redirect to onboarding if primary service onboarding not complete
73+ if ( ! status [ primaryService ] ) {
74+ redirect ( "/onboarding" ) ;
75+ }
4276 }
4377
4478 // Get first watch date from Tautulli for accurate membership duration
@@ -61,6 +95,11 @@ export default async function Home() {
6195 }
6296 : null ;
6397
98+ // Determine media server URL based on primary auth service
99+ const mediaServerUrl = user ?. primaryAuthService === "jellyfin"
100+ ? jellyfinServer ?. url
101+ : plexServer ?. url ;
102+
64103 return (
65104 < UserDashboard
66105 userId = { session . user . id }
@@ -73,24 +112,41 @@ export default async function Home() {
73112 overseerrUrl = { overseerrUrl }
74113 prometheusStatus = { prometheusStatus }
75114 memberSince = { memberSince }
115+ primaryAuthService = { user ?. primaryAuthService }
116+ mediaServerUrl = { mediaServerUrl }
76117 />
77118 ) ;
78119 }
79120
121+ // Unauthenticated: Show sign-in options
122+ const hasPlex = ! ! plexServer ;
123+ const hasJellyfin = ! ! jellyfinServer ;
124+
125+ if ( ! hasPlex && ! hasJellyfin ) {
126+ return (
127+ < main className = "fixed inset-0 flex flex-col items-center justify-center bg-gradient-to-b from-slate-900 via-slate-800 to-slate-900 px-4" >
128+ < div className = "flex flex-col items-center gap-6 sm:gap-8 text-center" >
129+ < h1 className = "text-4xl sm:text-5xl md:text-6xl font-bold bg-gradient-to-r from-cyan-400 via-purple-400 to-pink-400 bg-clip-text text-transparent" >
130+ Media Server
131+ </ h1 >
132+ < p className = "text-slate-400 text-lg" > No media servers configured. Please contact your administrator.</ p >
133+ </ div >
134+ </ main >
135+ ) ;
136+ }
137+
80138 return (
81139 < main className = "fixed inset-0 flex flex-col items-center justify-center bg-gradient-to-b from-slate-900 via-slate-800 to-slate-900 px-4" >
82- < div className = "flex flex-col items-center gap-6 sm:gap-8" >
140+ < div className = "flex flex-col items-center gap-6 sm:gap-8 w-full max-w-md " >
83141 < h1 className = "text-4xl sm:text-5xl md:text-6xl font-bold bg-gradient-to-r from-cyan-400 via-purple-400 to-pink-400 bg-clip-text text-transparent text-center" >
84142 { serverName }
85143 </ h1 >
86- < PlexSignInButton
87- serverName = { serverName }
88- showWarning = { true }
89- warningDelay = { 3000 }
90- showDisclaimer = { false }
91- buttonText = "Sign in with Plex"
92- loadingText = "Signing in..."
93- buttonClassName = "px-6 sm:px-8 py-3 sm:py-4 flex justify-center items-center gap-3 text-white text-base sm:text-lg font-semibold rounded-xl bg-gradient-to-r from-cyan-600 via-purple-600 to-pink-600 hover:from-cyan-500 hover:via-purple-500 hover:to-pink-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500/50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-lg"
144+
145+ < ServiceSignInToggle
146+ hasPlex = { hasPlex }
147+ hasJellyfin = { hasJellyfin }
148+ plexServerName = { plexServer ?. name }
149+ jellyfinServerName = { jellyfinServer ?. name }
94150 />
95151 </ div >
96152 </ main >
0 commit comments