📂 FileMgr
📍
/
home
/
hugpbeavxq
/
colegioelvalle.edu.gt
/
old
✏️ /cursos.html
⬅ Kembali
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Colegio El Valle | E-Learning Guatemala</title> <!-- React & ReactDOM --> <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <!-- Babel for JSX --> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <!-- Tailwind CSS --> <script src="https://cdn.tailwindcss.com"></script> <!-- Google Fonts: Inter --> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <script> tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter', 'sans-serif'], }, colors: { 'valle-green': '#10B981', 'valle-dark': '#064E3B', 'cielo-blue': '#3B82F6', } } } } </script> <style> body { background-color: #F9FAFB; font-family: 'Inter', sans-serif; } .fade-in { animation: fadeIn 0.4s ease-out; } @keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .nav-link { cursor: pointer; position: relative; transition: color 0.3s; } .nav-link::after { content: ''; position: absolute; width: 0; height: 2px; bottom: -4px; left: 0; background-color: #10B981; transition: width 0.3s; } .nav-link:hover::after { width: 100%; } .active-link { color: #10B981; font-weight: 600; } .active-link::after { width: 100%; } </style> </head> <body> <div id="root"></div> <script type="text/babel"> const { useState, useEffect } = React; // --- ICONOS --- const IconBook = () => <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>; const IconEdit = () => <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>; const IconTrash = () => <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>; const INITIAL_COURSES = [ { id: 1, title: "Matemáticas Básicas", level: "Primaria", price: 150, image: "https://images.unsplash.com/photo-1509228468518-180dd4864904?q=80&w=600", instructor: "Lic. Mario Estrada", description: "Fundamentos para niños de primaria.", buttonText: "Ver detalles", buttonUrl: "#" }, { id: 2, title: "Programación Web", level: "Vocacional", price: 450, image: "https://images.unsplash.com/photo-1547658719-da2b51169166?q=80&w=600", instructor: "Ing. Karla Ortiz", description: "HTML, CSS y JavaScript desde cero.", buttonText: "Ver detalles", buttonUrl: "#" } ]; // --- SECCIONES DE CONTENIDO --- const HomeSection = ({ courses }) => ( <div className="fade-in"> <section className="relative bg-valle-dark text-white py-20 px-4 overflow-hidden rounded-b-3xl mb-12"> <div className="container mx-auto text-center relative z-10"> <h1 className="text-5xl font-extrabold mb-6 text-white">Educación Sin Fronteras</h1> <p className="text-xl opacity-90 mb-8 max-w-2xl mx-auto">Llevamos el aula a tu casa con la mejor tecnología educativa de Guatemala.</p> <button className="bg-valle-green hover:bg-green-500 text-white px-8 py-3 rounded-full font-bold transition-all shadow-lg">Explorar Cursos</button> </div> <div className="absolute top-0 right-0 w-64 h-64 bg-valle-green opacity-10 rounded-full -mr-20 -mt-20"></div> </section> <div className="container mx-auto px-4 pb-20"> <h2 className="text-3xl font-bold text-gray-800 mb-8 text-center">Cursos Destacados</h2> <div className="grid md:grid-cols-3 gap-8"> {courses.map(c => ( <div key={c.id} className="bg-white rounded-2xl shadow-sm hover:shadow-xl transition-all border overflow-hidden"> <img src={c.image} className="h-48 w-full object-cover" alt={c.title} /> <div className="p-6"> <span className="text-xs font-bold text-valle-green bg-green-50 px-2 py-1 rounded uppercase">{c.level}</span> <h3 className="text-xl font-bold mt-2 mb-2 text-gray-800">{c.title}</h3> <p className="text-gray-500 text-sm mb-4 line-clamp-2">{c.description}</p> <div className="flex justify-between items-center pt-4 border-t"> <span className="font-bold text-2xl text-valle-dark">Q{c.price}</span> <a href={c.buttonUrl || "#"} target="_blank" rel="noopener noreferrer" className="text-cielo-blue font-semibold hover:underline" > {c.buttonText || "Ver detalles"} </a> </div> </div> </div> ))} </div> </div> </div> ); // --- COMPONENTES ADMIN --- const AdminLogin = ({ onLoginSuccess }) => { const [pass, setPass] = useState(''); const [error, setError] = useState(''); const handleLogin = async (e) => { e.preventDefault(); try { const res = await fetch('config.html'); const text = await res.text(); const doc = new DOMParser().parseFromString(text, 'text/html'); const correct = doc.getElementById('admin-password')?.textContent?.trim(); if (pass === correct) onLoginSuccess(); else setError('Acceso denegado.'); } catch { setError('Error de conexión.'); } }; return ( <div className="fade-in flex justify-center py-24"> <form onSubmit={handleLogin} className="bg-white p-8 rounded-2xl shadow-lg border w-80"> <h2 className="text-xl font-bold mb-4 text-center text-gray-800">Acceso Admin</h2> <input type="password" placeholder="Contraseña" className="w-full border p-2 rounded mb-4 text-gray-800" value={pass} onChange={e=>setPass(e.target.value)} /> {error && <p className="text-red-500 text-xs mb-4 text-center font-bold">{error}</p>} <button className="w-full bg-valle-dark text-white py-2 rounded font-bold">Ingresar</button> </form> </div> ); }; const AdminPanel = ({ courses, setCourses, onLogout }) => { const emptyForm = { id: null, title: '', image: '', description: '', instructor: '', price: '', level: 'Primaria', buttonText: 'Ver detalles', buttonUrl: '' }; const [form, setForm] = useState(emptyForm); const [isEditing, setIsEditing] = useState(false); const handleSave = (e) => { e.preventDefault(); if (isEditing) { setCourses(courses.map(c => c.id === form.id ? { ...form, price: Number(form.price) } : c)); } else { setCourses([...courses, { ...form, id: Date.now(), price: Number(form.price) }]); } resetForm(); }; const handleEditClick = (course) => { setForm(course); setIsEditing(true); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const resetForm = () => { setForm(emptyForm); setIsEditing(false); }; const handleDelete = (id) => { if (window.confirm("¿Seguro que deseas eliminar este curso?")) { setCourses(courses.filter(x => x.id !== id)); if (form.id === id) resetForm(); } }; return ( <div className="fade-in container mx-auto px-4 py-12"> <div className="flex justify-between items-center mb-8"> <h2 className="text-3xl font-bold text-gray-800">Panel de Control</h2> <button onClick={onLogout} className="text-red-500 font-bold hover:underline">Cerrar Sesión</button> </div> <div className="grid lg:grid-cols-3 gap-8"> {/* FORMULARIO */} <form onSubmit={handleSave} className="bg-white p-6 rounded-2xl border space-y-4 shadow-sm h-fit sticky top-24"> <h3 className="font-bold text-xl text-valle-dark mb-4">{isEditing ? 'Editar Curso' : 'Agregar Nuevo Curso'}</h3> <div className="space-y-1"> <label className="text-xs font-bold text-gray-400 uppercase">Título del Curso</label> <input required placeholder="Ej. Matemática Avanzada" className="w-full border p-2 rounded text-gray-800 outline-none focus:ring-1 focus:ring-valle-green" value={form.title} onChange={e=>setForm({...form, title: e.target.value})} /> </div> <div className="space-y-1"> <label className="text-xs font-bold text-gray-400 uppercase">Instructor</label> <input required placeholder="Nombre del docente" className="w-full border p-2 rounded text-gray-800 outline-none focus:ring-1 focus:ring-valle-green" value={form.instructor} onChange={e=>setForm({...form, instructor: e.target.value})} /> </div> <div className="grid grid-cols-2 gap-4"> <div className="space-y-1"> <label className="text-xs font-bold text-gray-400 uppercase">Precio Q</label> <input required type="number" placeholder="0.00" className="w-full border p-2 rounded text-gray-800 outline-none focus:ring-1 focus:ring-valle-green" value={form.price} onChange={e=>setForm({...form, price: e.target.value})} /> </div> <div className="space-y-1"> <label className="text-xs font-bold text-gray-400 uppercase">Nivel</label> <select className="w-full border p-2 rounded text-gray-800 outline-none focus:ring-1 focus:ring-valle-green" value={form.level} onChange={e=>setForm({...form, level: e.target.value})}> <option>Primaria</option> <option>Secundaria</option> <option>Vocacional</option> <option>Diplomado</option> </select> </div> </div> <div className="grid grid-cols-2 gap-4"> <div className="space-y-1"> <label className="text-xs font-bold text-gray-400 uppercase">Texto del Botón</label> <input placeholder="Ver detalles" className="w-full border p-2 rounded text-gray-800 outline-none focus:ring-1 focus:ring-valle-green" value={form.buttonText} onChange={e=>setForm({...form, buttonText: e.target.value})} /> </div> <div className="space-y-1"> <label className="text-xs font-bold text-gray-400 uppercase">Enlace del Botón (URL)</label> <input placeholder="https://..." className="w-full border p-2 rounded text-gray-800 outline-none focus:ring-1 focus:ring-valle-green" value={form.buttonUrl} onChange={e=>setForm({...form, buttonUrl: e.target.value})} /> </div> </div> <div className="space-y-1"> <label className="text-xs font-bold text-gray-400 uppercase">URL Imagen</label> <input required placeholder="https://..." className="w-full border p-2 rounded text-gray-800 outline-none focus:ring-1 focus:ring-valle-green text-sm" value={form.image} onChange={e=>setForm({...form, image: e.target.value})} /> </div> <div className="space-y-1"> <label className="text-xs font-bold text-gray-400 uppercase">Descripción Corta</label> <textarea placeholder="Breve resumen del contenido..." className="w-full border p-2 rounded text-gray-800 outline-none focus:ring-1 focus:ring-valle-green h-24" value={form.description} onChange={e=>setForm({...form, description: e.target.value})} /> </div> <div className="flex gap-2 pt-2"> <button type="submit" className="flex-1 bg-valle-green text-white py-3 rounded-xl font-bold hover:bg-green-600 transition-colors"> {isEditing ? 'Actualizar Curso' : 'Crear Curso'} </button> {isEditing && ( <button type="button" onClick={resetForm} className="bg-gray-200 text-gray-600 px-4 py-3 rounded-xl font-bold"> Cancelar </button> )} </div> </form> {/* LISTADO / TABLA */} <div className="lg:col-span-2 bg-white rounded-2xl border shadow-sm overflow-hidden"> <div className="p-4 border-b bg-gray-50 flex justify-between items-center"> <h3 className="font-bold text-gray-700 uppercase text-sm tracking-widest">Listado de Cursos ({courses.length})</h3> </div> <div className="overflow-x-auto"> <table className="w-full text-left"> <thead className="bg-white border-b text-gray-400 text-xs uppercase font-bold"> <tr> <th className="p-4">Imagen</th> <th className="p-4">Curso</th> <th className="p-4">Detalles</th> <th className="p-4 text-right">Acciones</th> </tr> </thead> <tbody className="divide-y"> {courses.map(c => ( <tr key={c.id} className={`hover:bg-gray-50 transition-colors ${form.id === c.id ? 'bg-green-50' : ''}`}> <td className="p-4"> <img src={c.image} className="w-16 h-10 object-cover rounded-lg border shadow-sm" alt="" /> </td> <td className="p-4"> <div className="font-bold text-gray-800">{c.title}</div> <div className="text-xs text-gray-500">{c.instructor}</div> </td> <td className="p-4"> <div className="text-sm font-semibold text-valle-dark">Q{c.price}</div> <div className="text-xs text-gray-400">{c.level}</div> </td> <td className="p-4 text-right"> <div className="flex justify-end gap-2"> <button onClick={() => handleEditClick(c)} className="p-2 hover:bg-blue-50 text-blue-500 rounded-lg transition-colors" title="Editar" > <IconEdit /> </button> <button onClick={() => handleDelete(c.id)} className="p-2 hover:bg-red-50 text-red-400 rounded-lg transition-colors" title="Eliminar" > <IconTrash /> </button> </div> </td> </tr> ))} </tbody> </table> </div> {courses.length === 0 && ( <div className="p-20 text-center text-gray-400 italic">No hay cursos registrados.</div> )} </div> </div> </div> ); }; // --- APP ROOT --- const App = () => { const [page, setPage] = useState('home'); const [isAuth, setIsAuth] = useState(false); const [courses, setCourses] = useState(() => { const s = localStorage.getItem('valle_v2'); return s ? JSON.parse(s) : INITIAL_COURSES; }); useEffect(() => localStorage.setItem('valle_v2', JSON.stringify(courses)), [courses]); const renderContent = () => { switch(page) { case 'home': return <HomeSection courses={courses} />; case 'admin': return isAuth ? <AdminPanel courses={courses} setCourses={setCourses} onLogout={()=>setIsAuth(false)} /> : <AdminLogin onLoginSuccess={()=>setIsAuth(true)} />; default: return <HomeSection courses={courses} />; } }; return ( <div className="min-h-screen flex flex-col"> <header className="bg-white sticky top-0 z-50 border-b"> <div className="container mx-auto px-4 h-20 flex items-center justify-between"> <div className="flex items-center gap-2 cursor-pointer" onClick={() => setPage('home')}> <div className="text-valle-green"><IconBook /></div> <span className="font-extrabold text-xl tracking-tight text-valle-dark">Colegio El Valle</span> </div> <nav className="flex items-center gap-6 text-sm uppercase tracking-wider font-medium text-gray-500"> <a href="index.html" className="nav-link active-link" > Inicio </a> <button onClick={() => setPage('admin')} className={`px-4 py-2 rounded-lg transition-colors font-bold ${page === 'admin' ? 'bg-valle-dark text-white' : 'bg-gray-100 text-valle-dark hover:bg-gray-200'}`}>Admin</button> </nav> </div> </header> <main className="flex-grow">{renderContent()}</main> <footer className="bg-gray-900 text-white py-12"> <div className="container mx-auto px-4 text-center"> <div className="flex justify-center mb-6 text-valle-green"><IconBook /></div> <p className="opacity-60 mb-4 font-light italic">"Innovando la educación en Guatemala"</p> <div className="text-sm text-gray-400">© 2024 Colegio El Valle. Todos los derechos reservados.</div> </div> </footer> </div> ); }; ReactDOM.createRoot(document.getElementById('root')).render(<App />); </script> </body> </html>
💾 Simpan
Batal
⬅ colegioelvalle.edu.gt
4 item
Nama
Tipe
Ukuran
Diubah
Aksi
🌐
config.html
html
456 B
2026-02-01 23:05
✏️
👁️
🔤
🗑
🌐
cursos.html
html
22.3 KB
2026-02-02 00:18
✏️
👁️
🔤
🗑
🌐
index.html
html
57.2 KB
2026-02-02 00:15
✏️
👁️
🔤
🗑
🌐
old-index.html
html
6.4 KB
2026-01-27 18:21
✏️
👁️
🔤
🗑
✏️ Rename
Nama Baru
Simpan
Batal