mirror of
https://github.com/hanzo-templates/gallery.git
synced 2026-10-30 20:00:04 +00:00
393 lines
18 KiB
TypeScript
393 lines
18 KiB
TypeScript
'use client';
|
|
|
|
import Image from 'next/image';
|
|
import Link from 'next/link';
|
|
import { useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import type { Template } from '../../templates-data';
|
|
|
|
interface TemplatePageClientProps {
|
|
variants: Template[];
|
|
prevTemplate: Template | null;
|
|
nextTemplate: Template | null;
|
|
currentIndex: number;
|
|
totalTemplates: number;
|
|
allTemplates: Template[];
|
|
}
|
|
|
|
export function TemplatePageClient({ variants, prevTemplate, nextTemplate, currentIndex, totalTemplates, allTemplates }: TemplatePageClientProps) {
|
|
const router = useRouter();
|
|
const [selectedVariant, setSelectedVariant] = useState<Template>(variants[0]);
|
|
|
|
// Navigate to random template
|
|
function goToRandomTemplate() {
|
|
const currentSlug = variants[0].slug;
|
|
const otherTemplates = allTemplates.filter(t => t.slug !== currentSlug);
|
|
if (otherTemplates.length > 0) {
|
|
const randomTemplate = otherTemplates[Math.floor(Math.random() * otherTemplates.length)];
|
|
router.push(`/templates/${randomTemplate.slug}`);
|
|
}
|
|
}
|
|
|
|
// Hanzo App URL
|
|
const HANZO_APP_URL = process.env.NEXT_PUBLIC_HANZO_APP_URL || 'https://hanzo.app';
|
|
|
|
// Get GitHub repository URL
|
|
function getRepoUrl() {
|
|
return `https://github.com/hanzo-apps/${selectedVariant.slug}`;
|
|
}
|
|
|
|
// Open GitHub repository
|
|
function openGitHubRepo() {
|
|
window.open(getRepoUrl(), '_blank');
|
|
}
|
|
|
|
// Deploy to Hanzo Cloud
|
|
function deployToCloud() {
|
|
const deployUrl = new URL('/dev', HANZO_APP_URL);
|
|
deployUrl.searchParams.set('template', getRepoUrl());
|
|
deployUrl.searchParams.set('action', 'deploy');
|
|
window.location.href = deployUrl.toString();
|
|
}
|
|
|
|
// Edit in Hanzo
|
|
function editInHanzo() {
|
|
const editUrl = new URL('/dev', HANZO_APP_URL);
|
|
editUrl.searchParams.set('repo', getRepoUrl());
|
|
editUrl.searchParams.set('action', 'edit');
|
|
window.location.href = editUrl.toString();
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-[#0a0a0a] text-white">
|
|
{/* Navigation - Airbnb Style */}
|
|
<nav className="border-b border-neutral-800 bg-black sticky top-0 z-50">
|
|
{/* Top Bar */}
|
|
<div className="border-b border-neutral-800">
|
|
<div className="container mx-auto px-4 py-3 flex items-center justify-between">
|
|
<Link href="/gallery" className="flex items-center gap-2 text-neutral-400 hover:text-white transition-colors text-sm">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
</svg>
|
|
Gallery
|
|
</Link>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<span className="text-xs text-neutral-500 font-medium">
|
|
{currentIndex} / {totalTemplates}
|
|
</span>
|
|
<div className="flex gap-2">
|
|
{prevTemplate ? (
|
|
<Link
|
|
href={`/templates/${prevTemplate.slug}`}
|
|
className="px-3 py-1.5 border border-neutral-700 rounded-full hover:bg-neutral-800 transition-all text-xs font-medium text-neutral-300"
|
|
>
|
|
←
|
|
</Link>
|
|
) : (
|
|
<button
|
|
disabled
|
|
className="px-3 py-1.5 border border-neutral-800 rounded-full text-neutral-700 cursor-not-allowed text-xs"
|
|
>
|
|
←
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={goToRandomTemplate}
|
|
className="px-3 py-1.5 border border-neutral-700 rounded-full hover:bg-neutral-800 transition-all text-xs font-medium text-neutral-300 hover:border-[#fd4444]/50 hover:text-[#fd4444]"
|
|
title="Random Template"
|
|
>
|
|
🎲
|
|
</button>
|
|
{nextTemplate ? (
|
|
<Link
|
|
href={`/templates/${nextTemplate.slug}`}
|
|
className="px-3 py-1.5 border border-neutral-700 rounded-full hover:bg-neutral-800 transition-all text-xs font-medium text-neutral-300"
|
|
>
|
|
→
|
|
</Link>
|
|
) : (
|
|
<button
|
|
disabled
|
|
className="px-3 py-1.5 border border-neutral-800 rounded-full text-neutral-700 cursor-not-allowed text-xs"
|
|
>
|
|
→
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Action Buttons */}
|
|
<div className="container mx-auto px-4 py-2 overflow-x-auto scrollbar-hide">
|
|
<div className="flex gap-2 min-w-max items-center">
|
|
<button
|
|
onClick={deployToCloud}
|
|
className="px-4 py-2 bg-[#fd4444] hover:bg-[#e03e3e] text-white rounded-full transition-all text-xs font-medium whitespace-nowrap"
|
|
>
|
|
🚀 Deploy to Cloud
|
|
</button>
|
|
<button
|
|
onClick={editInHanzo}
|
|
className="px-4 py-2 border border-[#fd4444] text-[#fd4444] hover:bg-[#fd4444]/10 rounded-full transition-all text-xs font-medium whitespace-nowrap"
|
|
>
|
|
✏️ Edit in Hanzo
|
|
</button>
|
|
<button
|
|
onClick={openGitHubRepo}
|
|
className="px-4 py-2 border border-neutral-700 rounded-full hover:bg-neutral-800 transition-all text-xs font-medium text-neutral-300 whitespace-nowrap"
|
|
>
|
|
📦 GitHub
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
navigator.clipboard.writeText(getRepoUrl());
|
|
alert('Repository URL copied!');
|
|
}}
|
|
className="px-4 py-2 border border-neutral-700 rounded-full hover:bg-neutral-800 transition-all text-xs font-medium text-neutral-300 whitespace-nowrap"
|
|
>
|
|
📋 Copy URL
|
|
</button>
|
|
<span className="px-4 py-2 bg-neutral-900 border border-neutral-800 rounded-full text-xs font-medium text-neutral-400 whitespace-nowrap">
|
|
{selectedVariant.framework}
|
|
</span>
|
|
<span className="px-4 py-2 bg-neutral-900 border border-neutral-800 rounded-full text-xs font-medium text-neutral-400 whitespace-nowrap">
|
|
{selectedVariant.category}
|
|
</span>
|
|
<span className={`px-4 py-2 border rounded-full text-xs font-medium whitespace-nowrap ${
|
|
selectedVariant.tier === 1 ? 'border-[#fd4444] bg-[#fd4444]/10 text-[#fd4444]' :
|
|
selectedVariant.tier === 2 ? 'border-neutral-600 bg-neutral-800 text-neutral-300' :
|
|
'border-neutral-700 bg-neutral-900 text-neutral-400'
|
|
}`}>
|
|
Tier {selectedVariant.tier}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Hero */}
|
|
<section className="container mx-auto px-4 py-16">
|
|
<div className="max-w-5xl mx-auto">
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<h1 className="text-5xl md:text-6xl font-medium text-white">
|
|
{selectedVariant.displayName}
|
|
</h1>
|
|
<div className="flex">
|
|
{[...Array(5)].map((_, i) => (
|
|
<span key={i} className={`text-2xl ${i < selectedVariant.rating ? 'text-[#fd4444]' : 'text-neutral-700'}`}>
|
|
★
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<p className="text-xl text-neutral-400 mb-8">
|
|
{selectedVariant.description || `Premium ${selectedVariant.displayName} template with modern design and functionality.`}
|
|
</p>
|
|
|
|
{/* Variant Selector (if multiple variants) */}
|
|
{variants.length > 1 && (
|
|
<div className="mb-8">
|
|
<label className="block text-sm font-medium text-neutral-400 mb-3">
|
|
Choose Framework ({variants.length} variants available)
|
|
</label>
|
|
<div className="flex flex-wrap gap-3">
|
|
{variants.map((variant, i) => (
|
|
<button
|
|
key={i}
|
|
onClick={() => setSelectedVariant(variant)}
|
|
className={`px-6 py-3 rounded-xl font-medium transition-all ${
|
|
selectedVariant.id === variant.id
|
|
? 'bg-[#fd4444] text-white border-2 border-[#fd4444]'
|
|
: 'bg-neutral-900 text-neutral-300 border-2 border-neutral-800 hover:border-[#fd4444]/50 hover:bg-neutral-800'
|
|
}`}
|
|
>
|
|
{variant.framework}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Tech Stack Badges */}
|
|
<div className="flex flex-wrap gap-3 mb-12">
|
|
<span className="px-4 py-2 bg-neutral-900 border border-neutral-800 rounded-xl text-neutral-300 font-medium">
|
|
{selectedVariant.framework}
|
|
</span>
|
|
<span className={`px-4 py-2 rounded-xl font-medium ${
|
|
selectedVariant.tier === 1 ? 'bg-[#fd4444]/10 border border-[#fd4444]/30 text-[#fd4444]' :
|
|
selectedVariant.tier === 2 ? 'bg-neutral-800 border border-neutral-700 text-neutral-300' :
|
|
'bg-neutral-900 border border-neutral-800 text-neutral-400'
|
|
}`}>
|
|
Tier {selectedVariant.tier} - {selectedVariant.tier === 1 ? 'Excellent' : selectedVariant.tier === 2 ? 'Very Good' : 'Good'}
|
|
</span>
|
|
<span className="px-4 py-2 bg-neutral-900 border border-neutral-800 rounded-xl text-neutral-300 font-medium">
|
|
{selectedVariant.components}
|
|
</span>
|
|
<span className="px-4 py-2 bg-neutral-900 border border-neutral-800 rounded-xl text-neutral-400 font-medium">
|
|
{selectedVariant.category}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Screenshot */}
|
|
<div className="relative aspect-video rounded-2xl overflow-hidden border border-neutral-800 mb-16">
|
|
<Image
|
|
src={`/screenshots/${selectedVariant.screenshot}.png`}
|
|
alt={selectedVariant.displayName}
|
|
fill
|
|
unoptimized
|
|
className="object-cover"
|
|
priority
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Features */}
|
|
<section className="container mx-auto px-4 py-16 bg-neutral-900/50">
|
|
<div className="max-w-5xl mx-auto">
|
|
<h2 className="text-3xl font-medium mb-8 text-white">Key Features</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{selectedVariant.features.map((feature, i) => (
|
|
<div key={i} className="bg-neutral-900/50 p-6 rounded-xl border border-neutral-800 hover:border-[#fd4444]/50 transition-all">
|
|
<div className="text-2xl mb-3">✨</div>
|
|
<h3 className="text-lg font-medium text-white">{feature}</h3>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Tech Stack */}
|
|
<section className="container mx-auto px-4 py-16">
|
|
<div className="max-w-5xl mx-auto">
|
|
<h2 className="text-3xl font-medium mb-8 text-white">Technology Stack</h2>
|
|
<div className="bg-neutral-900/50 p-8 rounded-2xl border border-neutral-800">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
<div>
|
|
<h3 className="text-lg font-medium text-[#fd4444] mb-2">Framework</h3>
|
|
<p className="text-neutral-300">{selectedVariant.framework}</p>
|
|
</div>
|
|
<div>
|
|
<h3 className="text-lg font-medium text-[#fd4444] mb-2">Use Case</h3>
|
|
<p className="text-neutral-300">{selectedVariant.useCase}</p>
|
|
</div>
|
|
<div>
|
|
<h3 className="text-lg font-medium text-[#fd4444] mb-2">Setup Difficulty</h3>
|
|
<p className="text-neutral-300">
|
|
{selectedVariant.easeOfSetup}/5 - {selectedVariant.easeOfSetup >= 5 ? 'Very Easy' : selectedVariant.easeOfSetup >= 4 ? 'Easy' : 'Moderate'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Setup */}
|
|
<section className="container mx-auto px-4 py-16 bg-neutral-900/50">
|
|
<div className="max-w-5xl mx-auto">
|
|
<h2 className="text-3xl font-medium mb-8 text-white">Quick Start</h2>
|
|
<div className="bg-black/50 p-8 rounded-2xl border border-neutral-800 font-mono">
|
|
<div className="text-neutral-500 mb-4"># Clone the template</div>
|
|
<div className="text-[#fd4444] mb-6">git clone {getRepoUrl()}</div>
|
|
|
|
<div className="text-neutral-500 mb-4"># Install dependencies</div>
|
|
<div className="text-[#fd4444] mb-6">pnpm install</div>
|
|
|
|
<div className="text-neutral-500 mb-4"># Start development server</div>
|
|
<div className="text-[#fd4444] mb-6">
|
|
{selectedVariant.framework.toLowerCase().includes('html') ? 'gulp' :
|
|
selectedVariant.framework.toLowerCase().includes('react') && !selectedVariant.framework.toLowerCase().includes('next') ? 'pnpm start' :
|
|
'pnpm dev'}
|
|
</div>
|
|
|
|
{selectedVariant.port && (
|
|
<>
|
|
<div className="text-neutral-500 mb-4"># Open in browser</div>
|
|
<div className="text-[#fd4444]">http://localhost:{selectedVariant.port}</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Use Cases */}
|
|
<section className="container mx-auto px-4 py-16">
|
|
<div className="max-w-5xl mx-auto">
|
|
<h2 className="text-3xl font-medium mb-8 text-white">Perfect For</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
<div className="bg-neutral-900/50 p-6 rounded-xl border border-neutral-800">
|
|
<div className="text-3xl mb-3">🚀</div>
|
|
<h3 className="text-lg font-medium text-white mb-2">{selectedVariant.useCase}</h3>
|
|
<p className="text-sm text-neutral-500">Primary use case for this template</p>
|
|
</div>
|
|
<div className="bg-neutral-900/50 p-6 rounded-xl border border-neutral-800">
|
|
<div className="text-3xl mb-3">⚡</div>
|
|
<h3 className="text-lg font-medium text-white mb-2">Fast Development</h3>
|
|
<p className="text-sm text-neutral-500">Pre-built components ready to use</p>
|
|
</div>
|
|
<div className="bg-neutral-900/50 p-6 rounded-xl border border-neutral-800">
|
|
<div className="text-3xl mb-3">🎨</div>
|
|
<h3 className="text-lg font-medium text-white mb-2">Modern Design</h3>
|
|
<p className="text-sm text-neutral-500">Beautiful UI following latest trends</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* CTA */}
|
|
<section className="container mx-auto px-4 py-16">
|
|
<div className="max-w-5xl mx-auto">
|
|
<div className="bg-neutral-900/50 p-12 rounded-2xl border border-neutral-800 text-center relative overflow-hidden">
|
|
{/* Subtle glow */}
|
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[400px] h-[300px] bg-[#fd4444] opacity-10 blur-[100px] rounded-full" />
|
|
|
|
<div className="relative">
|
|
<h2 className="text-3xl font-medium mb-4 text-white">Deploy Instantly</h2>
|
|
<p className="text-lg text-neutral-400 mb-8">
|
|
One-click deployment to Hanzo Cloud
|
|
</p>
|
|
<div className="flex flex-wrap gap-3 justify-center">
|
|
<button
|
|
onClick={deployToCloud}
|
|
className="px-6 py-3 bg-[#fd4444] hover:bg-[#e03e3e] text-white font-medium rounded-full transition-colors flex items-center gap-2"
|
|
>
|
|
🚀 Deploy Now
|
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
|
</svg>
|
|
</button>
|
|
<button
|
|
onClick={editInHanzo}
|
|
className="px-6 py-3 border border-[#fd4444] text-[#fd4444] hover:bg-[#fd4444]/10 font-medium rounded-full transition-colors"
|
|
>
|
|
✏️ Edit in Hanzo
|
|
</button>
|
|
<button
|
|
onClick={openGitHubRepo}
|
|
className="px-6 py-3 bg-neutral-800 hover:bg-neutral-700 border border-neutral-700 text-white font-medium rounded-full transition-colors"
|
|
>
|
|
📦 View on GitHub
|
|
</button>
|
|
<Link
|
|
href="/gallery"
|
|
className="px-6 py-3 bg-transparent border border-neutral-700 hover:border-neutral-600 hover:bg-white/5 text-white font-medium rounded-full transition-colors"
|
|
>
|
|
Browse More
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Footer */}
|
|
<footer className="border-t border-neutral-800 bg-[#0a0a0a] py-8">
|
|
<div className="container mx-auto px-4 text-center text-neutral-600">
|
|
<p>© 2016-2026 Hanzo AI Inc. All rights reserved.</p>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
);
|
|
}
|