Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
Merge pull request #112 from igrowker/gab-branch2
Browse files Browse the repository at this point in the history
Dashboard Provider/Tourist
  • Loading branch information
josepacco00 authored Dec 8, 2024
2 parents 51ad4e4 + 7da8906 commit 190730c
Show file tree
Hide file tree
Showing 21 changed files with 1,117 additions and 158 deletions.
2 changes: 1 addition & 1 deletion src/components/ExperienceDetail/InfoExperience.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function InfoExperience({description, capacity, location, createdAt, tags } : {d

export default InfoExperience;

const InfoCard = ({ icon, first, second } : { icon: string, first: string, second: string | number }) => {
export const InfoCard = ({ icon, first, second } : { icon: string, first: string, second: string | number }) => {
return (
<div className="flex items-center w-1/2 gap-4 px-3 py-2 border-[1.5px] border-gray-300 rounded-2xl ">
<img src={icon} alt="iconEmail" className="w-6 h-6" />
Expand Down
11 changes: 7 additions & 4 deletions src/components/ExperienceDetail/ReviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ interface IReview {
userId: string
rating: number
comment: string
createdAt: string
createdAt: string,
userAvatar: string,
userName: string,
}

function ReviewCard({review} : {review: IReview}) {

const { rating, comment, createdAt } = review;
const { rating, comment, createdAt, userName, userAvatar } = review;
console.log(review)

const getRating = (rating: number) => {
switch (rating) {
Expand All @@ -31,9 +34,9 @@ function ReviewCard({review} : {review: IReview}) {
return (
<div className="border-gray-300 border-[1.5px] rounded-xl p-4">
<div className="flex gap-4">
<div className="w-12 h-12 rounded-full bg-primary"></div>
<img className="w-12 h-12 rounded-full" src={userAvatar ?? ""} alt="" />
<div className="flex flex-col">
<h1>Mariana Garcia Rodiguez</h1>
<h1>{userName ?? "Usuario"}</h1>
<p className=""><span className="text-primary">{getRating(rating)?.stars}</span> {formatToShortDate(createdAt)}</p>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/components/ExperienceDetail/ReviewSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ function ReviewSection({ id }: { id: string }) {
const response = await fetch(`${BACK_URL}/reviews/experience/${id}`);
const data = await response.json();
setReviews(data);
console.log(reviews)
} catch (error) {
console.error("Error fetching reviews:", error);
}
Expand Down
283 changes: 283 additions & 0 deletions src/components/ExperiencePanel/BookingInfo/BookingInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import HeaderPanel from "../HeaderPanel";
import imageSafari from "../../../assets/img/Safari.jpg";

//@ts-ignore
import Cookies from "js-cookie";
import { getStatus } from "../../../utils/getDateFormat";
import { useNavigate } from "react-router-dom";
import { useContext } from "react";
import { AuthContext } from "../../../contexts/auth.context";
import reviewServices from "../../../services/review.services";

function BookingInfo() {
const { id } = useParams();
const navigate = useNavigate();
const { user } = useContext(AuthContext);
const userId = user?._id;

const [booking, setBooking] = useState<any>(null);
const [experience, setExperience] = useState<any>(null);
const [loading, setLoading] = useState<boolean>(true);
const [userHasReview, setUserHasReview] = useState<boolean>(false);
const [reviewId, setReviewId] = useState<string | null>(null);
const [showConfirmationModal, setShowConfirmationModal] = useState(false); // Estado para controlar el modal de confirmación
const [successMessage, setSuccessMessage] = useState<string | null>(null); // Mensaje de éxito

function formatDateAndTime(dateString: string) {
const date = new Date(dateString);
const day = String(date.getDate()).padStart(2, "0");
const month = String(date.getMonth() + 1).padStart(2, "0");
const year = date.getFullYear();
const formattedDate = `${day}/${month}/${year}`;
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const formattedTime = `${hours}:${minutes}`;
return { date: formattedDate, time: formattedTime };
}

const { date, time } = formatDateAndTime(booking?.bookingDate || "");
const { text } = getStatus(booking?.status || "");

useEffect(() => {
const fetchBookingData = async () => {
try {
setLoading(true);

const storedBooking = Cookies.get("booking");
if (storedBooking) {
const parsedBooking = JSON.parse(storedBooking);
setBooking(parsedBooking);

const experienceId = parsedBooking.experienceId;

const experienceResponse = await fetch(
`${import.meta.env.VITE_API_URL}/experiences/${experienceId}`
);
const experienceData = await experienceResponse.json();
setExperience(experienceData);

const reviewsResponse = await fetch(
`${import.meta.env.VITE_API_URL}/reviews/experience/${experienceId}`
);
const reviews = await reviewsResponse.json();

// Busca si el usuario ya tiene una reseña
const userReview = reviews.find(
(review: any) => review.userId === userId
);
if (userReview) {
setUserHasReview(true);
setReviewId(userReview.id); // Guarda el ID de la reseña
} else {
setUserHasReview(false);
}
} else {
console.error("No se encontró la reserva en las cookies.");
}
} catch (error) {
console.error("Error fetching booking or experience data:", error);
} finally {
setLoading(false);
}
};

if (id) fetchBookingData();
}, [id, userId]);

const { provider } = booking || {};

const handleDeleteReview = async () => {
if (!reviewId) return;

try {
await reviewServices.delete(reviewId); // Usa el servicio actualizado

setSuccessMessage("Reseña eliminada con éxito."); // Muestra el mensaje de éxito
setUserHasReview(false);
setReviewId(null);
setShowConfirmationModal(false); // Cierra el modal

// Oculta el mensaje después de 3 segundos
setTimeout(() => {
setSuccessMessage(null);
}, 3000);
} catch (error) {
console.error("Error eliminando la reseña:", error);
alert("Hubo un problema al eliminar la reseña.");
}
};

const handleShowConfirmation = () => {
setShowConfirmationModal(true);
};

const handleCancelDelete = () => {
setShowConfirmationModal(false);
};

if (loading) {
return (
<div className="flex flex-col items-center justify-center h-screen gap-2">
<div className="w-6 h-6 border-b-2 border-current rounded-full animate-spin"></div>
</div>
);
}

if (!booking || !experience) {
return <p>No se encontraron datos.</p>;
}

return (
<div>
<HeaderPanel title="Información de Reserva" />
<div className="px-5 my-5">
<img
src={imageSafari}
alt="Imagen de Safari"
className="object-cover w-full h-32 rounded-3xl"
/>
<h1 className="mt-4 mb-4 font-bold text-center">{experience.title}</h1>
<div className="flex flex-col gap-3">
<Info first="Fecha" second={date} />
<Info
first="Lugar"
second={experience.location[0] + ", " + experience.location[1]}
/>
<Info first="Hora" second={time} />
<Info
first="Participantes"
second={`${booking.participants} ${
booking.participants === 1 ? "Persona" : "Personas"
}`}
/>
<Info first="Precio Total" second={`€${booking.totalPrice}`} />
<Info first="Estado" second={text} />
<Info
first="Ubicación"
second={
<ButtonNavigate
latitude={experience.location[2]}
longitude={experience.location[3]}
/>
}
/>
<Info
first="Experiencia"
second={
<button
onClick={() => navigate(`/experience/${experience.id}`)}
className="px-4 py-0.5"
>
Ver información
</button>
}
/>
{booking.status === "CONFIRMED" && (
<Info
first="Reseña"
second={
userHasReview ? (
<button
onClick={handleShowConfirmation} // Muestra el modal de confirmación
className="px-4 py-0.5 bg-tertiary"
>
Eliminar Reseña
</button>
) : (
<button
onClick={() => {
navigate(`/review/${experience.id}`);
Cookies.set(
"experience",
JSON.stringify({ title: experience.title }),
{ expires: 7 }
); // Se guarda por 7 días
}}
className="px-4 py-0.5"
>
Agregar Reseña
</button>
)
}
/>
)}
</div>
<div className="py-4">
<h1 className="py-2 font-bold text-center">Datos del proveedor</h1>
<div className="flex flex-col gap-3">
<Info first="Nombre" second={provider.name} />
<Info first="Correo" second={provider.email} />
<Info first="Telefono" second={provider.phone} />
</div>
</div>
</div>

{/* Modal de confirmación */}
{showConfirmationModal && (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div className="p-6 bg-white w-96 rounded-xl">
<h2 className="text-lg font-bold text-center text-gray-800">
¿Estás seguro?
</h2>
<p className="mb-4 text-center text-gray-700">
¿Quieres eliminar esta reseña?
</p>
<div className="flex justify-center gap-4">
<button
onClick={handleDeleteReview}
className="px-4 py-2 text-white rounded"
>
Eliminar
</button>
<button
onClick={handleCancelDelete}
className="px-4 py-2 text-white rounded bg-tertiary"
>
Cancelar
</button>
</div>
</div>
</div>
)}

{/* Mensaje de éxito */}
{successMessage && (
<div className="fixed bottom-0 p-2 text-white transform -translate-x-1/2 bg-green-500 rounded left-1/2">
{successMessage}
</div>
)}
</div>
);
}

export default BookingInfo;

const Info = ({ first, second }: { first: string; second: string | any }) => {
return (
<div className="flex items-center justify-between pb-1 border-b border-b-gray-200">
<p className="text-sm font-bold">{first}</p>
<p className="text-sm">{second}</p>
</div>
);
};

const ButtonNavigate = ({
latitude,
longitude,
}: {
latitude: string;
longitude: string;
}) => {
const openLocation = () => {
const url = `https://www.google.com/maps?q=${latitude},${longitude}`;
window.open(url, "_blank");
};

return (
<button onClick={openLocation} className="px-4 py-0.5">
Ver ubicación
</button>
);
};
17 changes: 17 additions & 0 deletions src/components/ExperiencePanel/ExperiencePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

import { useContext } from "react"
import { AuthContext } from "../../contexts/auth.context"
import PanelTourist from "./TouristUser/PanelTourist";
// import BookingInfo from "./BookingInfo/BookingInfo";
import PanelProvider from "./ProviderUser/PanelProvider";

function ExperiencePanel() {

const { user } = useContext(AuthContext);
console.log(user)


return user?.role !== 'TOURIST' ? <PanelTourist/> : <PanelProvider/>
}

export default ExperiencePanel;
16 changes: 16 additions & 0 deletions src/components/ExperiencePanel/HeaderPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import iconLeft from '../../assets/icons/icon-arrow-left.svg'
import { useNavigate } from 'react-router-dom';

function HeaderPanel({title} : {title: string}) {
const navigate = useNavigate();

return (
<div className="flex justify-between mt-4">
<img onClick={() => navigate(-1)} src={iconLeft} alt="icon-left" className="w-8 pl-5 cursor-pointer" />
<h1 className="text-lg font-bold">{title}</h1>
<p className="pr-5"></p>
</div>
);
}

export default HeaderPanel;
Loading

0 comments on commit 190730c

Please sign in to comment.