313 lines
13 KiB
TypeScript
313 lines
13 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { useNavigate } from 'react-router';
|
|
import type { Course } from '../types/course';
|
|
import { FiArrowLeft, FiPlus, FiTrash2, FiUploadCloud } from 'react-icons/fi';
|
|
|
|
interface CreateCourseViewProps {
|
|
onCreateCourse: (course: Course) => void;
|
|
onShowToast: (msg: string) => void;
|
|
}
|
|
|
|
export const CreateCourseView: React.FC<CreateCourseViewProps> = ({
|
|
onCreateCourse,
|
|
onShowToast,
|
|
}) => {
|
|
const navigate = useNavigate();
|
|
|
|
const [title, setTitle] = useState('');
|
|
const [subtitle, setSubtitle] = useState('');
|
|
const [category, setCategory] = useState('Desarrollo Web');
|
|
const [level, setLevel] = useState<'Principiante' | 'Intermedio' | 'Avanzado' | 'Todos los niveles'>('Principiante');
|
|
const [price, setPrice] = useState(29.99);
|
|
const [originalPrice, setOriginalPrice] = useState(89.99);
|
|
const [thumbnail, setThumbnail] = useState('https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=800&auto=format&fit=crop&q=80');
|
|
|
|
const [modules, setModules] = useState<{ title: string; lessonsCount: number }[]>([
|
|
{ title: 'Módulo 1: Introducción y Fundamentos', lessonsCount: 4 },
|
|
{ title: 'Módulo 2: Proyecto Práctico Principal', lessonsCount: 6 },
|
|
]);
|
|
|
|
const [newModuleTitle, setNewModuleTitle] = useState('');
|
|
|
|
const handleAddModule = () => {
|
|
if (newModuleTitle.trim()) {
|
|
setModules([...modules, { title: newModuleTitle, lessonsCount: 3 }]);
|
|
setNewModuleTitle('');
|
|
}
|
|
};
|
|
|
|
const handleRemoveModule = (idx: number) => {
|
|
setModules(modules.filter((_, i) => i !== idx));
|
|
};
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!title.trim()) return;
|
|
|
|
const newCourse: Course = {
|
|
id: `course-${Date.now()}`,
|
|
title,
|
|
subtitle: subtitle || 'Aprende paso a paso con proyectos prácticos.',
|
|
category,
|
|
level,
|
|
price: Number(price),
|
|
originalPrice: Number(originalPrice),
|
|
rating: 5.0,
|
|
reviewsCount: 1,
|
|
studentsCount: 1,
|
|
thumbnail,
|
|
duration: '12h 30m',
|
|
lessonsCount: modules.reduce((sum, m) => sum + m.lessonsCount, 0),
|
|
badge: 'Nuevo',
|
|
instructor: {
|
|
id: 'inst-1',
|
|
name: 'Tu Nombre (Instructor)',
|
|
role: 'Instructor Experto',
|
|
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80',
|
|
rating: 5.0,
|
|
totalStudents: 1,
|
|
totalCourses: 1,
|
|
bio: 'Creador de contenido y profesional en tecnología.',
|
|
},
|
|
updatedAt: 'Reciente',
|
|
language: 'Español',
|
|
whatYouWillLearn: [
|
|
'Dominio completo de la tecnología aprendida.',
|
|
'Construcción de proyectos prácticos listos para producción.',
|
|
'Código fuente completo e instructivos.',
|
|
],
|
|
requirements: ['Computadora con conexión a internet.'],
|
|
modules: modules.map((m, idx) => ({
|
|
id: `mod-${idx}`,
|
|
title: m.title,
|
|
lessons: [
|
|
{ id: `l-${idx}-1`, title: 'Lección 1: Conceptos Clave', duration: '15:00', isFreePreview: true },
|
|
{ id: `l-${idx}-2`, title: 'Lección 2: Implementación', duration: '22:30' },
|
|
],
|
|
})),
|
|
reviews: [],
|
|
isPublished: true,
|
|
salesCount: 0,
|
|
totalRevenue: 0,
|
|
};
|
|
|
|
onCreateCourse(newCourse);
|
|
onShowToast('¡Tu curso ha sido publicado exitosamente en el Marketplace!');
|
|
navigate('/seller');
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-slate-950 py-10">
|
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 space-y-8">
|
|
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between border-b border-slate-800 pb-5">
|
|
<div className="space-y-1">
|
|
<button
|
|
onClick={() => navigate('/seller')}
|
|
className="inline-flex items-center gap-2 text-xs font-semibold text-slate-400 hover:text-cyan-400 mb-2 transition"
|
|
>
|
|
<FiArrowLeft /> Regresar a Panel de Instructor
|
|
</button>
|
|
<h1 className="text-2xl sm:text-3xl font-extrabold text-white">
|
|
Publicar Nuevo Curso
|
|
</h1>
|
|
<p className="text-xs text-slate-400">
|
|
Completa los detalles de tu curso para lanzarlo a la comunidad de estudiantes
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Form Container */}
|
|
<form onSubmit={handleSubmit} className="space-y-8">
|
|
|
|
{/* Card 1: Informacion Basica */}
|
|
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-xl">
|
|
<h3 className="text-sm font-bold text-cyan-400 uppercase tracking-wider">
|
|
1. Información Básica
|
|
</h3>
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
|
Título del Curso *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
required
|
|
value={title}
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
placeholder="Ej: Master en React 19 y Next.js 15 desde Cero"
|
|
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
|
Subtítulo Resumen
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={subtitle}
|
|
onChange={(e) => setSubtitle(e.target.value)}
|
|
placeholder="Aprende a construir aplicaciones fullstack de alto rendimiento..."
|
|
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
|
Categoría
|
|
</label>
|
|
<select
|
|
value={category}
|
|
onChange={(e) => setCategory(e.target.value)}
|
|
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
>
|
|
<option value="Desarrollo Web">Desarrollo Web</option>
|
|
<option value="Inteligencia Artificial">Inteligencia Artificial</option>
|
|
<option value="Diseño UI/UX">Diseño UI/UX</option>
|
|
<option value="Ciberseguridad">Ciberseguridad</option>
|
|
<option value="Marketing Digital">Marketing Digital</option>
|
|
<option value="Negocios">Negocios</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
|
Nivel del Curso
|
|
</label>
|
|
<select
|
|
value={level}
|
|
onChange={(e) => setLevel(e.target.value as any)}
|
|
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
>
|
|
<option value="Principiante">Principiante</option>
|
|
<option value="Intermedio">Intermedio</option>
|
|
<option value="Avanzado">Avanzado</option>
|
|
<option value="Todos los niveles">Todos los niveles</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Card 2: Precio e Imagen */}
|
|
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-xl">
|
|
<h3 className="text-sm font-bold text-cyan-400 uppercase tracking-wider">
|
|
2. Precio e Imagen de Portada
|
|
</h3>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
|
Precio de Venta ($ USD)
|
|
</label>
|
|
<input
|
|
type="number"
|
|
step="0.01"
|
|
required
|
|
value={price}
|
|
onChange={(e) => setPrice(Number(e.target.value))}
|
|
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white font-mono focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
|
Precio Original Táchado ($ USD)
|
|
</label>
|
|
<input
|
|
type="number"
|
|
step="0.01"
|
|
required
|
|
value={originalPrice}
|
|
onChange={(e) => setOriginalPrice(Number(e.target.value))}
|
|
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white font-mono focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
|
URL de la Imagen de Portada
|
|
</label>
|
|
<div className="flex gap-2">
|
|
<input
|
|
type="url"
|
|
value={thumbnail}
|
|
onChange={(e) => setThumbnail(e.target.value)}
|
|
className="flex-1 px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
/>
|
|
</div>
|
|
<div className="mt-3 aspect-video max-w-sm rounded-xl overflow-hidden bg-slate-950 border border-slate-800">
|
|
<img src={thumbnail} alt="Vista previa" className="w-full h-full object-cover" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Card 3: Estructura del Temario */}
|
|
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-xl">
|
|
<h3 className="text-sm font-bold text-cyan-400 uppercase tracking-wider">
|
|
3. Módulos y Temario
|
|
</h3>
|
|
|
|
<div className="space-y-3">
|
|
{modules.map((m, idx) => (
|
|
<div key={idx} className="flex items-center justify-between p-4 bg-slate-950 border border-slate-800 rounded-xl text-xs">
|
|
<span className="font-semibold text-white">{m.title}</span>
|
|
<button
|
|
type="button"
|
|
onClick={() => handleRemoveModule(idx)}
|
|
className="p-2 text-slate-500 hover:text-rose-400 transition"
|
|
>
|
|
<FiTrash2 />
|
|
</button>
|
|
</div>
|
|
))}
|
|
|
|
<div className="flex gap-2 pt-2">
|
|
<input
|
|
type="text"
|
|
value={newModuleTitle}
|
|
onChange={(e) => setNewModuleTitle(e.target.value)}
|
|
placeholder="Agregar nuevo título de módulo..."
|
|
className="flex-1 px-4 py-2.5 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={handleAddModule}
|
|
className="px-4 py-2.5 bg-slate-800 hover:bg-slate-700 text-cyan-400 font-semibold rounded-xl text-xs flex items-center gap-1.5 transition"
|
|
>
|
|
<FiPlus /> Módulo
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Submit Actions */}
|
|
<div className="flex items-center justify-end gap-4 pt-4">
|
|
<button
|
|
type="button"
|
|
onClick={() => navigate('/seller')}
|
|
className="px-6 py-3.5 bg-slate-800 hover:bg-slate-700 text-slate-300 font-semibold rounded-xl text-xs transition"
|
|
>
|
|
Cancelar
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
className="px-8 py-3.5 bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-500 hover:to-cyan-400 text-white font-bold rounded-xl text-sm shadow-xl shadow-cyan-500/25 transition-all flex items-center gap-2"
|
|
>
|
|
<FiUploadCloud />
|
|
<span>Publicar Curso en RyCourses</span>
|
|
</button>
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|