Skip to content
Open
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@heroicons/react": "2.0.18",
"@material-tailwind/react": "2.1.4",
"apexcharts": "3.44.0",
"axios": "^1.7.9",
"prop-types": "15.8.1",
"react": "18.2.0",
"react-apexcharts": "1.4.1",
Expand All @@ -29,4 +30,4 @@
"tailwindcss": "3.3.4",
"vite": "4.5.0"
}
}
}
63 changes: 63 additions & 0 deletions src/data/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import axios from "axios"

const API_BASE_URL = "http://127.0.0.1:8000/api/"

const api = axios.create({
baseURL: API_BASE_URL,
headers: {
"Content-Type": "application/json",
},
})

export const Service = {
get: async (url) => {
try {
const response = await api.get(url)
return response.data
} catch (error) {
console.error("Error en GET:", error)
throw error
}
},

post: async (url, data) => {
try {
const response = await api.post(url, data)
return response.data
} catch (error) {
console.error("Error en POST:", error)
throw error
}
},

patch: async (url, data) => {
try {
const response = await api.patch(url, data)
return response.data
} catch (error) {
console.error("Error en PATCH:", error)
throw error
}
},

put: async (url, data) => {
try {
const response = await api.put(url, data)
return response.data
} catch (error) {
console.error("Error en PUT:", error)
throw error
}
},

delete: async (url) => {
try {
const response = await api.delete(url)
return response.data
} catch (error) {
console.error("Error en DELETE:", error)
throw error
}
},
}

75 changes: 75 additions & 0 deletions src/pages/auth/ForgotPassword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use client"

import { useState } from "react"
import { Card, Input, Button, Typography } from "@material-tailwind/react"
import { EnvelopeIcon } from "@heroicons/react/24/outline"
import { Service } from "@/data/api"

export function ForgotPassword() {
const [email, setEmail] = useState("")
const [notification, setNotification] = useState(null)

const handleSubmit = async (e) => {
e.preventDefault()
try {
await Service.post("/auth/forgot-password", { email })
showNotification("green", "Se ha enviado un código de recuperación a tu correo.")
} catch (error) {
console.error("Error al enviar el correo de recuperación:", error)
showNotification("red", "Error al enviar el correo. Por favor, intenta de nuevo.")
}
}

const showNotification = (type, message) => {
setNotification({ type, message })
setTimeout(() => setNotification(null), 5000)
}

return (
<div className="flex justify-center items-center min-h-screen bg-gray-100">
<Card className="w-full max-w-md mx-4 shadow-xl">
<form onSubmit={handleSubmit} className="p-8">
<div className="text-center mb-8">
<EnvelopeIcon className="h-12 w-12 mx-auto text-blue-500 mb-4" />
<Typography variant="h3" className="font-bold text-2xl mb-2">
Recuperar Contraseña
</Typography>
<Typography variant="paragraph" color="blue-gray" className="text-sm">
Ingresa tu correo electrónico para recibir un código de recuperación.
</Typography>
</div>
<div className="mb-4">
<Input
size="lg"
label="Correo electrónico"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<Button type="submit" className="mt-6" fullWidth>
Enviar Código
</Button>
<Typography variant="small" className="mt-6 flex justify-center">
¿Recordaste tu contraseña?{" "}
<a href="/auth/sign-in" className="ml-1 font-medium text-blue-500 hover:text-blue-700">
Iniciar sesión
</a>
</Typography>
</form>
</Card>
{notification && (
<div
className={`fixed top-4 right-4 p-4 rounded-lg text-white ${
notification.type === "green" ? "bg-green-500" : "bg-red-500"
} transition-opacity duration-500 ${notification ? "opacity-100" : "opacity-0"}`}
>
{notification.message}
</div>
)}
</div>
)
}

export default ForgotPassword

109 changes: 109 additions & 0 deletions src/pages/auth/ResetPassword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"use client"

import { useState } from "react"
import { Card, Input, Button, Typography } from "@material-tailwind/react"
import { LockClosedIcon } from "@heroicons/react/24/outline"
import { Service } from "@/data/api"

export function ResetPassword() {
const [formData, setFormData] = useState({
code: "",
newPassword: "",
confirmPassword: "",
})
const [notification, setNotification] = useState(null)

const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value })
}

const handleSubmit = async (e) => {
e.preventDefault()
if (formData.newPassword !== formData.confirmPassword) {
showNotification("red", "Las contraseñas no coinciden.")
return
}
try {
await Service.post("/auth/reset-password", {
code: formData.code,
newPassword: formData.newPassword,
})
showNotification("green", "Tu contraseña ha sido actualizada exitosamente.")
} catch (error) {
console.error("Error al restablecer la contraseña:", error)
showNotification("red", "Error al restablecer la contraseña. Por favor, intenta de nuevo.")
}
}

const showNotification = (type, message) => {
setNotification({ type, message })
setTimeout(() => setNotification(null), 5000)
}

return (
<div className="flex justify-center items-center min-h-screen bg-gray-100">
<Card className="w-full max-w-md mx-4 shadow-xl">
<form onSubmit={handleSubmit} className="p-8">
<div className="text-center mb-8">
<LockClosedIcon className="h-12 w-12 mx-auto text-blue-500 mb-4" />
<Typography variant="h3" className="font-bold text-2xl mb-2">
Restablecer Contraseña
</Typography>
<Typography variant="paragraph" color="blue-gray" className="text-sm">
Ingresa el código recibido y tu nueva contraseña.
</Typography>
</div>
<div className="mb-4 space-y-4">
<Input
size="lg"
label="Código de recuperación"
name="code"
value={formData.code}
onChange={handleChange}
required
/>
<Input
type="password"
size="lg"
label="Nueva contraseña"
name="newPassword"
value={formData.newPassword}
onChange={handleChange}
required
/>
<Input
type="password"
size="lg"
label="Confirmar nueva contraseña"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
required
/>
</div>
<Button type="submit" className="mt-6" fullWidth>
Restablecer Contraseña
</Button>
<Typography variant="small" className="mt-6 flex justify-center">
¿Recordaste tu contraseña?{" "}
<a href="/auth/sign-in" className="ml-1 font-medium text-blue-500 hover:text-blue-700">
Iniciar sesión
</a>
</Typography>
</form>
</Card>
{notification && (
<div
className={`fixed top-4 right-4 p-4 rounded-lg text-white ${
notification.type === "green" ? "bg-green-500" : "bg-red-500"
} transition-opacity duration-500 ${notification ? "opacity-100" : "opacity-0"}`}
>
{notification.message}
</div>
)}
</div>
)
}

export default ResetPassword

Loading