Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
},
"dependencies": {
"axios": "^1.10.0",
"dependencies": "^0.0.1",
"lucide-react": "^0.534.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
Expand Down
2 changes: 1 addition & 1 deletion src/api/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,4 @@ export const authService = {
login,
register,
getProfile,
};
};
25 changes: 18 additions & 7 deletions src/components/layouts/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ export function NavBar() {
navigate('/login');
};

const handleApplyClick = () => {
if (!isAuthenticated) {
alert("Debes iniciar sesión para poder aplicar.");
navigate('/login');
return;
}
navigate('/professor/apply');
};

return (
<header className="fixed top-0 left-0 right-0 z-50 bg-white/90 backdrop-blur-md border-b border-blue-100 shadow-sm">
<div className="container mx-auto px-4 h-16 flex items-center justify-between">
Expand All @@ -43,13 +52,15 @@ export function NavBar() {
</Link>

<div className="flex items-center space-x-2 md:space-x-4">
<button
className="hidden sm:flex items-center space-x-2 border-green-200 text-green-700 hover:bg-green-50 hover:border-green-300 bg-transparent px-4 py-2 rounded-lg border text-sm font-medium"
onClick={() => alert('Formulario para profesores próximamente!')}
>
<GraduationCap className="w-4 h-4" />
<span className="ml-2">Quiero ser profesor</span>
</button>
{(!user || user.role === 'student') && (
<button
className="hidden sm:flex items-center space-x-2 border-green-200 text-green-700 hover:bg-green-50 hover:border-green-300 bg-transparent px-4 py-2 rounded-lg border text-sm font-medium"
onClick={handleApplyClick}
>
<GraduationCap className="w-4 h-4" />
<span className="ml-2">Quiero ser profesor</span>
</button>
)}

{isLoading ? (
<div className="flex items-center space-x-2">
Expand Down
33 changes: 33 additions & 0 deletions src/components/ui/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type React from 'react';

const Card = ({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={`rounded-lg border bg-card text-card-foreground shadow-sm ${className}`} {...props}>
{children}
</div>
);

const CardHeader = ({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={`flex flex-col space-y-1.5 p-6 ${className}`} {...props}>
{children}
</div>
);

const CardTitle = ({ className, children, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h3 className={`text-2xl font-semibold leading-none tracking-tight ${className}`} {...props}>
{children}
</h3>
);

const CardDescription = ({ className, children, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
<p className={`text-sm text-muted-foreground ${className}`} {...props}>
{children}
</p>
);

const CardContent = ({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={`p-6 pt-0 ${className}`} {...props}>
{children}
</div>
);

export { Card, CardHeader, CardTitle, CardDescription, CardContent };
11 changes: 11 additions & 0 deletions src/components/ui/Label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type React from 'react';

type LabelProps = React.ComponentProps<'label'>;

export const Label = ({ className, ...props }: LabelProps) => {
const baseClasses = "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70";

return (
<label className={`${baseClasses} ${className}`} {...props} />
);
};
11 changes: 11 additions & 0 deletions src/components/ui/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type React from 'react';

type TextareaProps = React.ComponentProps<'textarea'>;

export const Textarea = ({ className, ...props }: TextareaProps) => {
const baseClasses = "flex min-h-[80px] w-full rounded-md border bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50";

return (
<textarea className={`${baseClasses} ${className}`} {...props} />
);
};
173 changes: 173 additions & 0 deletions src/pages/Professor/ProfessorApplication.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import type React from "react";
import { useState } from "react";
import { Link } from "react-router-dom";

import Button from "../../components/ui/Button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../components/ui/Card";
import Input from "../../components/ui/Input";
import { Label } from "../../components/ui/Label";
import { Textarea } from "../../components/ui/TextArea";
import { BookOpen, ArrowLeft, GraduationCap, BookOpenCheck, FileText, Upload } from "lucide-react";

export const ProfessorApplication = () => {
const [formData, setFormData] = useState({
expertise: "",
experienceMotivation: "",
document: null as File | null,
});

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0] || null;
setFormData((prev) => ({
...prev,
document: file,
}));
};

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Teacher application:", formData);
alert("¡Solicitud enviada! Te contactaremos pronto.");
};

return (
<div className="min-h-screen bg-gradient-to-br from-green-50 via-white to-blue-50 flex items-center justify-center p-4">
<div className="absolute top-6 left-6">
<Link to="/" className="flex items-center space-x-2 text-slate-700 hover:text-green-600 transition-colors">
<ArrowLeft className="w-5 h-5" />
<div className="flex items-center space-x-2">
<div className="w-8 h-8 bg-gradient-to-br from-blue-400 to-green-400 rounded-lg flex items-center justify-center">
<BookOpen className="w-5 h-5 text-white" />
</div>
<span className="text-xl font-bold">EduCursos</span>
</div>
</Link>
</div>

<div className="w-full max-w-5xl grid lg:grid-cols-2 gap-8 items-center">
<div className="hidden lg:block">
<div className="relative">
<img
src="/placeholder.svg"
alt="Dos profesores colaborando"
className="w-full h-auto rounded-2xl shadow-2xl"
/>
<div className="absolute -bottom-4 -right-4 w-full h-full bg-gradient-to-br from-green-200 to-blue-200 rounded-2xl -z-10"></div>
</div>
</div>

<Card className="w-full bg-white/90 backdrop-blur-sm border-0 shadow-xl">
<CardHeader className="text-center space-y-2">
<div className="mx-auto w-12 h-12 bg-gradient-to-br from-green-400 to-green-600 rounded-full flex items-center justify-center mb-2">
<GraduationCap className="w-6 h-6 text-white" />
</div>
<CardTitle className="text-2xl font-bold text-slate-800">Únete como Profesor</CardTitle>
<CardDescription className="text-slate-600">
Comparte tu conocimiento y ayuda a otros a aprender
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">

<div className="space-y-2">
<div className="relative">
<Input
id="expertise"
name="expertise"
label="Área de especialización"
icon={<BookOpenCheck className="h-4 w-4" />}
type="text"
placeholder="Ej: Programación, Diseño, Marketing..."
value={formData.expertise}
onChange={handleInputChange}
required
/>
</div>
</div>

<div className="space-y-2">
<Label htmlFor="experienceMotivation" className="text-slate-700">
Experiencia y motivación
</Label>
<div className="relative">
<FileText className="absolute left-3 top-3 h-4 w-4 text-slate-400" />
<Textarea
id="experienceMotivation"
name="experienceMotivation"
placeholder="Cuéntanos sobre tu experiencia profesional..."
value={formData.experienceMotivation}
onChange={handleInputChange}
className="pl-10 pt-10 min-h-[120px] border-slate-200 focus:border-green-400 focus:ring-green-400 resize-none"
required
/>
</div>
</div>

<div className="space-y-2">
<Label htmlFor="document" className="text-slate-700">
Documentos (opcional)
</Label>
<div className="relative">
<div className="flex items-center justify-center w-full">
<label
htmlFor="document"
className="flex flex-col items-center justify-center w-full h-32 border-2 border-slate-200 border-dashed rounded-lg cursor-pointer bg-slate-50 hover:bg-slate-100 hover:border-green-300 transition-colors"
>
<div className="flex flex-col items-center justify-center pt-5 pb-6">
<Upload className="w-8 h-8 mb-2 text-slate-400" />
<p className="mb-2 text-sm text-slate-500">
<span className="font-semibold">Haz clic para subir</span> o arrastra y suelta
</p>
<p className="text-xs text-slate-400">PDF, DOC, DOCX (MAX. 10MB)</p>
{formData.document && (
<p className="text-xs text-green-600 mt-2 font-medium">
Archivo seleccionado: {formData.document.name}
</p>
)}
</div>
<input
id="document"
name="document"
type="file"
className="hidden"
accept=".pdf,.doc,.docx"
onChange={handleFileChange}
/>
</label>
</div>
<p className="text-xs text-slate-500 mt-1">
Puedes subir tu CV, certificados o cualquier documento que consideres relevante
</p>
</div>
</div>

<Button type="submit" className="w-full bg-green-500 hover:bg-green-600 text-white py-2.5">
Enviar solicitud
</Button>

<div className="text-center text-sm text-slate-600 mt-4">
<p>Revisaremos tu solicitud y te contactaremos en un plazo de 2-3 días hábiles.</p>
</div>

<div className="text-center text-sm text-slate-600">
¿Ya tienes una cuenta?{" "}

<Link to="/login" className="text-green-600 hover:text-green-700 hover:underline font-medium">
Inicia sesión aquí
</Link>
</div>
</form>
</CardContent>
</Card>
</div>
</div>
);
};
9 changes: 9 additions & 0 deletions src/router/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import AdminDashboard from '../pages/Admin/AdminDashboard';
import ProfessorDashboard from '../pages/Professor/ProfessorDashboard';
import UnauthorizedPage from '../pages/UnauthorizedPage';
import ProtectedRoute from './ProtectedRoute';
import { ProfessorApplication } from '../pages/Professor/ProfessorApplication'

const AppRouter = () => {
return (
Expand Down Expand Up @@ -45,6 +46,14 @@ const AppRouter = () => {
</ProtectedRoute>
}
/>
<Route
path="/professor/apply"
element={
<ProtectedRoute allowedRoles={['admin', 'student']}>
<ProfessorApplication />
</ProtectedRoute>
}
/>
</Route>
<Route path="/unauthorized" element={<UnauthorizedPage />} />
</Routes>
Expand Down