359 lines
16 KiB
TypeScript
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>
|
|
);
|
|
}
|