se sube vistas
This commit is contained in:
1 parent
509272a643
commit
a8c484c1de
16 files changed
+1994
-256
No files matched your search
@@ -0,0 +1,312 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user