feat: Upgrade to Next.js 16.1 & React 19.2, migrate to Supabase SSR with async client handling
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { checkIsAdmin, getGlobalApiStats } from '@/services/track-api-usage';
|
||||
import { BarChart3, TrendingUp, Users, Calendar, AlertCircle } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default async function AdminPage() {
|
||||
const supabase = createServerComponentClient({ cookies });
|
||||
const supabase = await createClient();
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
|
||||
console.log('[Admin Page] User:', user?.id, user?.email);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { checkIsAdmin } from '@/services/track-api-usage';
|
||||
import { getAllPlans } from '@/services/subscription-service';
|
||||
@@ -9,7 +8,7 @@ import { ChevronLeft, Package } from 'lucide-react';
|
||||
import PlanManagementClient from '@/components/PlanManagementClient';
|
||||
|
||||
export default async function AdminPlansPage() {
|
||||
const supabase = createServerComponentClient({ cookies });
|
||||
const supabase = await createClient();
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
|
||||
if (!user) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import Link from 'next/link';
|
||||
import { ChevronLeft, Tag as TagIcon, Plus, Search, Trash2, Shield, User, Filter, Download } from 'lucide-react';
|
||||
import { Tag, TagCategory, getTagsByCategory } from '@/services/tags';
|
||||
@@ -9,7 +9,7 @@ import { useI18n } from '@/i18n/I18nContext';
|
||||
|
||||
export default function AdminTagsPage() {
|
||||
const { t } = useI18n();
|
||||
const supabase = createClientComponentClient();
|
||||
const supabase = createClient();
|
||||
const [tags, setTags] = useState<Tag[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { checkIsAdmin } from '@/services/track-api-usage';
|
||||
import { getAllUsersWithCredits } from '@/services/admin-credit-service';
|
||||
@@ -10,7 +9,7 @@ import { ChevronLeft, Users, Coins, TrendingUp, TrendingDown } from 'lucide-reac
|
||||
import UserManagementClient from '@/components/UserManagementClient';
|
||||
|
||||
export default async function AdminUsersPage() {
|
||||
const supabase = createServerComponentClient({ cookies });
|
||||
const supabase = await createClient();
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
|
||||
if (!user) {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export async function GET() {
|
||||
const supabase = createServerComponentClient({ cookies });
|
||||
const supabase = await createClient();
|
||||
|
||||
// Get current user
|
||||
const { data: { user }, error: userError } = await supabase.auth.getUser();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
import sharp from 'sharp';
|
||||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const supabase = createRouteHandlerClient({ cookies });
|
||||
const supabase = await createClient();
|
||||
|
||||
// Check session
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
export const dynamic = 'force-dynamic';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export async function GET(request: Request) {
|
||||
@@ -7,7 +7,7 @@ export async function GET(request: Request) {
|
||||
const code = requestUrl.searchParams.get('code');
|
||||
|
||||
if (code) {
|
||||
const supabase = createRouteHandlerClient({ cookies });
|
||||
const supabase = await createClient();
|
||||
await supabase.auth.exchangeCodeForSession(code);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { notFound } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { ChevronLeft, Calendar, Award, Droplets, MapPin, Tag, ExternalLink, Package, PlusCircle, Info } from 'lucide-react';
|
||||
@@ -10,13 +9,12 @@ import DeleteBottleButton from '@/components/DeleteBottleButton';
|
||||
import EditBottleForm from '@/components/EditBottleForm';
|
||||
import { validateSession } from '@/services/validate-session';
|
||||
|
||||
export default async function BottlePage({
|
||||
params,
|
||||
searchParams
|
||||
}: {
|
||||
params: { id: string },
|
||||
searchParams: { session_id?: string }
|
||||
export default async function BottlePage(props: {
|
||||
params: Promise<{ id: string }>,
|
||||
searchParams: Promise<{ session_id?: string }>
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const searchParams = await props.searchParams;
|
||||
let sessionId = searchParams.session_id;
|
||||
|
||||
// Validate Session Age (12 hour limit)
|
||||
@@ -26,7 +24,7 @@ export default async function BottlePage({
|
||||
sessionId = undefined;
|
||||
}
|
||||
}
|
||||
const supabase = createServerComponentClient({ cookies });
|
||||
const supabase = await createClient();
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
|
||||
const { data: bottle } = await supabase
|
||||
|
||||
36
src/app/global-error.tsx
Normal file
36
src/app/global-error.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import { RefreshCcw } from 'lucide-react';
|
||||
|
||||
export default function GlobalError({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
return (
|
||||
<html lang="de">
|
||||
<body>
|
||||
<div className="flex min-h-screen flex-col items-center justify-center p-6 bg-zinc-50 dark:bg-black text-center">
|
||||
<div className="bg-white dark:bg-zinc-900 p-8 rounded-3xl border border-zinc-200 dark:border-zinc-800 shadow-xl max-w-md w-full space-y-6">
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-2xl font-black text-zinc-900 dark:text-white">Kritischer Fehler</h2>
|
||||
<p className="text-zinc-500 text-sm">
|
||||
Ein schwerwiegender Fehler ist aufgetreten. Bitte versuche die Seite neu zu laden.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => reset()}
|
||||
className="w-full py-4 bg-amber-600 hover:bg-amber-700 text-white rounded-xl font-bold flex items-center justify-center gap-2 transition-all"
|
||||
>
|
||||
<RefreshCcw size={18} />
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
12
src/app/loading.tsx
Normal file
12
src/app/loading.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center p-6 bg-zinc-50 dark:bg-black text-center">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<Loader2 size={40} className="animate-spin text-amber-600" />
|
||||
<p className="text-zinc-500 font-medium animate-pulse">Whisky Vault wird geladen...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
29
src/app/not-found.tsx
Normal file
29
src/app/not-found.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import Link from 'next/link';
|
||||
import { Home, MoveLeft } from 'lucide-react';
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center p-6 bg-zinc-50 dark:bg-black text-center">
|
||||
<div className="bg-white dark:bg-zinc-900 p-8 rounded-3xl border border-zinc-200 dark:border-zinc-800 shadow-xl max-w-md w-full space-y-6">
|
||||
<div className="text-8xl font-black text-amber-600/20">404</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-2xl font-black text-zinc-900 dark:text-white">Seite nicht gefunden</h2>
|
||||
<p className="text-zinc-500 text-sm">
|
||||
Die gesuchte Seite existiert leider nicht oder wurde verschoben.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="pt-4">
|
||||
<Link
|
||||
href="/"
|
||||
className="w-full py-4 bg-amber-600 hover:bg-amber-700 text-white rounded-xl font-bold flex items-center justify-center gap-2 transition-all shadow-lg shadow-amber-600/20"
|
||||
>
|
||||
<Home size={18} />
|
||||
Zurück zum Vault
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import CameraCapture from "@/components/CameraCapture";
|
||||
import BottleGrid from "@/components/BottleGrid";
|
||||
import AuthForm from "@/components/AuthForm";
|
||||
@@ -13,7 +13,7 @@ import LanguageSwitcher from "@/components/LanguageSwitcher";
|
||||
import { useI18n } from "@/i18n/I18nContext";
|
||||
|
||||
export default function Home() {
|
||||
const supabase = createClientComponentClient();
|
||||
const supabase = createClient();
|
||||
const [bottles, setBottles] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [user, setUser] = useState<any>(null);
|
||||
@@ -64,7 +64,7 @@ export default function Home() {
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
// Listen for auth changes
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange((event: string, session: any) => {
|
||||
console.log('[Auth] State change event:', event, {
|
||||
hasSession: !!session,
|
||||
userId: session?.user?.id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import { ChevronLeft, Users, Calendar, GlassWater, Plus, Trash2, Loader2, Sparkles, ChevronRight, Play, Square } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import AvatarStack from '@/components/AvatarStack';
|
||||
@@ -43,7 +43,7 @@ export default function SessionDetailPage() {
|
||||
const { t } = useI18n();
|
||||
const { id } = useParams();
|
||||
const router = useRouter();
|
||||
const supabase = createClientComponentClient();
|
||||
const supabase = createClient();
|
||||
const [session, setSession] = useState<Session | null>(null);
|
||||
const [participants, setParticipants] = useState<Participant[]>([]);
|
||||
const [tastings, setTastings] = useState<SessionTasting[]>([]);
|
||||
|
||||
Reference in New Issue
Block a user