project-standalo-note-to-app/app/apps/[id]/page.tsx

116 lines
3.6 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { useRouter, useParams } from 'next/navigation';
import Header from '../../components/Header';
import Sidebar from '../../components/Sidebar';
import AppIframeViewer from '../../components/AppIframeViewer';
import type { User, GeneratedApp } from '@/types';
export default function AppDetailPage() {
const [user, setUser] = useState<User | null>(null);
const [app, setApp] = useState<GeneratedApp | null>(null);
const [isLoading, setIsLoading] = useState(true);
const router = useRouter();
const params = useParams();
const id = params?.id as string;
useEffect(() => {
// Fetch current user
fetch('/api/auth/me')
.then(res => {
if (!res.ok) {
router.push('/login');
return null;
}
return res.json();
})
.then(data => {
if (data) setUser(data);
})
.catch(() => router.push('/login'));
// Fetch app
if (id) {
fetch(`/api/apps/${id}`)
.then(res => {
if (!res.ok) throw new Error('App not found');
return res.json();
})
.then(data => {
setApp(data);
})
.catch(() => router.push('/apps'))
.finally(() => setIsLoading(false));
}
}, [id, router]);
const handleLoadComplete = () => {
console.log('App iframe loaded');
};
if (isLoading || !app) {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" />
</div>
);
}
return (
<div className="flex min-h-screen bg-gray-50">
<Sidebar activePath="/apps" />
<div className="flex-1">
<Header user={user} />
<main className="p-6">
<div className="max-w-6xl mx-auto">
<div className="mb-6">
<h1 className="text-3xl font-bold text-gray-900 mb-2">
{app.title}
</h1>
{app.description && (
<p className="text-gray-600">{app.description}</p>
)}
<div className="flex gap-2 mt-2">
{app.appType && (
<span className="text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded">
{app.appType}
</span>
)}
<span className={`text-xs px-2 py-1 rounded ${
app.status === 'completed' ? 'bg-green-100 text-green-800' :
app.status === 'generating' ? 'bg-yellow-100 text-yellow-800' :
'bg-red-100 text-red-800'
}`}>
{app.status}
</span>
</div>
</div>
{app.status === 'completed' && (
<AppIframeViewer
htmlContent={app.htmlContent}
title={app.title}
onLoadComplete={handleLoadComplete}
/>
)}
{app.status === 'generating' && (
<div className="bg-white border rounded-lg p-12 text-center">
<div className="w-12 h-12 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
<p className="text-gray-600">Generating app...</p>
</div>
)}
{app.status === 'failed' && (
<div className="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
<p className="text-red-700">App generation failed</p>
</div>
)}
</div>
</main>
</div>
</div>
);
}