aethex-studio/components/NewProjectModal.tsx

359 lines
16 KiB
TypeScript

"use client";
import React from 'react';
import { X, ChevronRight, Check } from 'lucide-react';
import { Dialog, DialogContent } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Checkbox } from '@/components/ui/checkbox';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import { Progress } from '@/components/ui/progress';
import { useAppStore } from '@/store/app-store';
import { templates, Template } from '../lib/templates';
import { getPlatformIcon } from '@/lib/utils';
export function NewProjectModal() {
const { newProjectModalOpen, setNewProjectModalOpen } = useAppStore();
const [step, setStep] = React.useState(1);
const [selectedTemplate, setSelectedTemplate] = React.useState('');
const [projectName, setProjectName] = React.useState('My Awesome Game');
const [description, setDescription] = React.useState('');
const [platforms, setPlatforms] = React.useState({
roblox: true,
web: true,
mobile: true,
desktop: false,
});
const [features, setFeatures] = React.useState({
nexus: true,
passport: true,
gameforge: true,
analytics: false,
monetization: false,
transmedia: false,
});
const [creating, setCreating] = React.useState(false);
const [progress, setProgress] = React.useState(0);
const handleClose = () => {
setNewProjectModalOpen(false);
setTimeout(() => {
setStep(1);
setSelectedTemplate('');
setCreating(false);
setProgress(0);
}, 300);
};
const handleCreate = () => {
setCreating(true);
let p = 0;
const interval = setInterval(() => {
p += 20;
setProgress(p);
if (p >= 100) {
clearInterval(interval);
setTimeout(handleClose, 1000);
}
}, 600);
};
return (
<Dialog open={newProjectModalOpen} onOpenChange={setNewProjectModalOpen}>
<DialogContent className="max-w-6xl h-[90vh] p-0 bg-surface border-gray-700 overflow-hidden">
{!creating ? (
<>
{/* Step 1: Choose Template */}
{step === 1 && (
<div className="flex flex-col h-full">
<div className="p-6 border-b border-gray-800">
<h2 className="text-2xl font-bold text-white">Choose a Template</h2>
<p className="text-gray-400 mt-1">Start with a pre-built template or create from scratch</p>
</div>
<div className="flex-1 overflow-y-auto p-6">
<div className="grid grid-cols-3 gap-4">
{templates.map((template) => (
<div
key={template.id}
className={`relative bg-background border-2 rounded-lg p-6 cursor-pointer transition-all hover:border-primary/50 ${
selectedTemplate === template.id ? 'border-primary' : 'border-gray-800'
}`}
onClick={() => setSelectedTemplate(template.id)}
>
{template.badge && (
<div className="absolute top-3 right-3 text-xs px-2 py-1 bg-primary/20 text-primary rounded-full">
{template.badge}
</div>
)}
<div className="text-5xl mb-4">{template.icon}</div>
<h3 className="text-lg font-semibold text-white mb-2">{template.name}</h3>
<p className="text-sm text-gray-400 mb-4">{template.description}</p>
<div className="flex flex-wrap gap-2">
{template.platforms.map(platform => (
<span key={platform} className="text-lg">
{getPlatformIcon(platform)}
</span>
))}
</div>
{selectedTemplate === template.id && (
<div className="absolute top-3 left-3 w-6 h-6 bg-primary rounded-full flex items-center justify-center">
<Check className="w-4 h-4 text-white" />
</div>
)}
</div>
))}
</div>
</div>
<div className="p-6 border-t border-gray-800 flex justify-end">
<Button
onClick={() => setStep(2)}
disabled={!selectedTemplate}
className="bg-primary hover:bg-primary-light"
>
Continue
<ChevronRight className="w-4 h-4 ml-2" />
</Button>
</div>
</div>
)}
{/* Step 2: Configure */}
{step === 2 && (
<div className="flex flex-col h-full">
<div className="p-6 border-b border-gray-800">
<h2 className="text-2xl font-bold text-white">Configure Project</h2>
<p className="text-gray-400 mt-1">Set up your project details and features</p>
</div>
<div className="flex-1 overflow-y-auto p-6 space-y-6">
<div>
<Label className="text-white mb-2">Project Name *</Label>
<Input
value={projectName}
onChange={(e) => setProjectName(e.target.value)}
className="bg-background border-gray-700 text-white"
/>
</div>
<div>
<Label className="text-white mb-2">Description</Label>
<Textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
className="bg-background border-gray-700 text-white resize-none"
rows={3}
/>
</div>
<div>
<Label className="text-white mb-3 block">Target Platforms</Label>
<div className="space-y-3">
{[
{ key: 'roblox', label: 'Roblox', icon: '🎮' },
{ key: 'web', label: 'Web', icon: '🌐' },
{ key: 'mobile', label: 'Mobile', icon: '📱' },
{ key: 'desktop', label: 'Desktop', icon: '🖥️', badge: 'Coming Soon' },
].map(({ key, label, icon, badge }) => (
<div key={key} className="flex items-center gap-3">
<Checkbox
checked={platforms[key as keyof typeof platforms]}
onCheckedChange={(checked) =>
setPlatforms(prev => ({ ...prev, [key]: checked }))
}
disabled={badge === 'Coming Soon'}
/>
<span className="text-lg">{icon}</span>
<Label className="text-white flex-1">{label}</Label>
{badge && (
<span className="text-xs px-2 py-0.5 bg-gray-700 text-gray-400 rounded">
{badge}
</span>
)}
</div>
))}
</div>
</div>
<div>
<Label className="text-white mb-3 block">Enable Features</Label>
<div className="space-y-4">
{[
{ key: 'nexus', label: 'Nexus Engine', icon: '⚡', desc: 'Sync game state across all platforms' },
{ key: 'passport', label: 'Passport Authentication', icon: '🔐', desc: 'One account, all platforms' },
{ key: 'gameforge', label: 'GameForge Governance', icon: '🎮', desc: 'Server-authoritative game logic' },
{ key: 'analytics', label: 'Analytics Dashboard', icon: '📊', desc: 'Player metrics and insights' },
{ key: 'monetization', label: 'Monetization', icon: '💰', desc: 'In-app purchases' },
{ key: 'transmedia', label: 'Transmedia Tools', icon: '🌍', desc: 'Story/lore builder' },
].map(({ key, label, icon, desc }) => (
<div key={key} className="flex items-start gap-3">
<Switch
checked={features[key as keyof typeof features]}
onCheckedChange={(checked) =>
setFeatures(prev => ({ ...prev, [key]: checked }))
}
/>
<div className="flex-1">
<div className="flex items-center gap-2">
<span>{icon}</span>
<Label className="text-white">{label}</Label>
</div>
<p className="text-xs text-gray-400 mt-1">{desc}</p>
</div>
</div>
))}
</div>
</div>
</div>
<div className="p-6 border-t border-gray-800 flex justify-between">
<Button
onClick={() => setStep(1)}
variant="outline"
className="border-gray-700"
>
Back
</Button>
<Button
onClick={() => setStep(3)}
className="bg-primary hover:bg-primary-light"
>
Continue
<ChevronRight className="w-4 h-4 ml-2" />
</Button>
</div>
</div>
)}
{/* Step 3: Review & Create */}
{step === 3 && (
<div className="flex flex-col h-full">
<div className="p-6 border-b border-gray-800">
<h2 className="text-2xl font-bold text-white">Review & Create</h2>
<p className="text-gray-400 mt-1">Confirm your project settings</p>
</div>
<div className="flex-1 overflow-y-auto p-6">
<div className="max-w-2xl mx-auto bg-background rounded-lg border border-gray-800 p-6 space-y-4">
<div>
<div className="text-sm text-gray-400 mb-1">Project Name</div>
<div className="text-lg text-white font-semibold">{projectName}</div>
</div>
<div>
<div className="text-sm text-gray-400 mb-1">Template</div>
<div className="text-white">
{templates.find(t => t.id === selectedTemplate)?.name}
</div>
</div>
<div>
<div className="text-sm text-gray-400 mb-2">Platforms</div>
<div className="flex gap-2">
{Object.entries(platforms)
.filter(([_, enabled]) => enabled)
.map(([key]) => (
<span key={key} className="px-3 py-1 bg-primary/20 text-primary rounded-full text-sm flex items-center gap-1">
<span>{getPlatformIcon(key)}</span>
{key.charAt(0).toUpperCase() + key.slice(1)}
</span>
))}
</div>
</div>
<div>
<div className="text-sm text-gray-400 mb-2">Features</div>
<div className="space-y-1">
{Object.entries(features)
.filter(([_, enabled]) => enabled)
.map(([key]) => (
<div key={key} className="flex items-center gap-2 text-sm text-gray-300">
<Check className="w-4 h-4 text-green-400" />
{key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1')}
</div>
))}
</div>
</div>
<div className="pt-4 border-t border-gray-800">
<div className="text-sm text-gray-400">Estimated setup time</div>
<div className="text-2xl text-primary font-semibold">~30 seconds</div>
</div>
</div>
</div>
<div className="p-6 border-t border-gray-800 flex justify-between">
<Button
onClick={() => setStep(2)}
variant="outline"
className="border-gray-700"
>
Back
</Button>
<Button
onClick={handleCreate}
className="bg-gradient-to-r from-primary to-secondary hover:opacity-90 transition-opacity px-8"
>
Create Project
</Button>
</div>
</div>
)}
</>
) : (
/* Creating Progress */
<div className="flex flex-col items-center justify-center h-full p-12">
<div className="w-full max-w-md space-y-6">
<div className="text-center">
<div className="text-6xl mb-4 animate-bounce">🚀</div>
<h2 className="text-2xl font-bold text-white mb-2">Creating Your Project</h2>
<p className="text-gray-400">Please wait while we set everything up...</p>
</div>
<Progress value={progress} className="h-2" />
<div className="space-y-2 text-sm">
{progress >= 20 && (
<div className="flex items-center gap-2 text-green-400">
<Check className="w-4 h-4" />
Setting up Roblox environment...
</div>
)}
{progress >= 40 && (
<div className="flex items-center gap-2 text-green-400">
<Check className="w-4 h-4" />
Initializing web project...
</div>
)}
{progress >= 60 && (
<div className="flex items-center gap-2 text-green-400">
<Check className="w-4 h-4" />
Configuring mobile build...
</div>
)}
{progress >= 80 && (
<div className="flex items-center gap-2 text-green-400">
<Check className="w-4 h-4" />
Installing Nexus Engine...
</div>
)}
{progress >= 100 && (
<div className="flex items-center gap-2 text-green-400 font-semibold">
<Check className="w-4 h-4" />
Project ready!
</div>
)}
</div>
</div>
</div>
)}
</DialogContent>
</Dialog>
);
}