32 page experiências admin
Mudanças
Foi criado uma rota nova de reservation-info na page de admin, ela retorna as experiencias e pessoas daquela reserva, também foi ajustado uma pequena mudança no componente ReservationCard, onde é ilustrado a conversa do usuário e admin, adicionado max-full, pois estava com limite de tela. Usei a mesma coisa feita em reserveSummary para o componente de mostrar as pessoas e as experiencias. A api foi implementada um exemplo, pois no back ainda não tem rota, então adicionei alguns dados mockados na classe da rota. Tem que ser adicionado um button de actions na tabela onde mostra todas as reservas, para assim conseguir entrar em uma unica reserva
Como testar
http://localhost:3002/admin/requests/reservation-info
Classe onde está chamando todos os componentes
import { useMemo } from "react";
import { ChevronLeft } from "lucide-react";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/buttons/defaultButton";
import { HiUsers } from "react-icons/hi";
import CanvasCard from "@/components/cards/canvasCard";
import { ReserveSummaryExperienceCard } from "@/components/cards/reserveSummaryExperienceCard";
import { ReserveParticipantInputs } from "@/components/layouts/reserve/ReserveParticipantInputs";
import { Textarea } from "@/components/ui/textarea";
import { ReservationsLayout } from "@/components/display/reservationEvents";
import type {
ReserveParticipant,
ReserveSummaryExperience,
} from "@/types/reserve";
import { Typography } from "@/components/typography/typography";
import { cn } from "@/lib/utils";
import type { ReservationEvent } from "@/components/display/reservationEvents";
export type ReserveInfoProps = {
title?: string;
participants: ReserveParticipant[];
experiences: ReserveSummaryExperience[];
events?: ReservationEvent[];
notes?: string;
onBack?: () => void;
className?: string;
};
export function ReserveInfo({
title,
participants,
experiences,
events,
notes,
onBack,
className,
}: ReserveInfoProps) {
const { t } = useTranslation();
const headerTitle = title ?? t("reservationInfo.title");
const genderCounts = useMemo(() => {
const counts = { men: 0, women: 0, other: 0 };
(participants || []).forEach((p) => {
const g = String((p as any).gender || "").toUpperCase();
if (g === "MALE" || g === "M") counts.men += 1;
else if (g === "FEMALE" || g === "F") counts.women += 1;
else counts.other += 1;
});
return counts;
}, [participants]);
const experiencesToRender = useMemo(() => experiences ?? [], [experiences]);
const eventsToRender = useMemo(() => events ?? [], [events]);
return (
<section className={cn("min-h-screen py-5", className)}>
<div className="mx-auto flex w-full max-w-7xl flex-col gap-8 px-2 sm:px-6 lg:px-8">
<header className="flex items-center gap-6">
<div className="flex items-center gap-6">
<Typography variant="h1" className="text-3xl font-bold text-main-dark-green md:text-4xl">
{headerTitle}
</Typography>
<div className="hidden sm:flex items-center gap-5 text-sm text-foreground/80">
<span className="inline-flex items-center gap-2">
<HiUsers className="h-5 w-5 text-main-dark-green" />
<span className="font-medium">{t("reservationInfo.counts.men")}:</span> {genderCounts.men}
</span>
<span className="inline-flex items-center gap-2">
<span className="font-medium">{t("reservationInfo.counts.women")}:</span> {genderCounts.women}
</span>
<span className="inline-flex items-center gap-2">
<span className="font-medium">{t("reservationInfo.counts.other")}:</span> {genderCounts.other}
</span>
</div>
</div>
</header>
<CanvasCard className="w-full border border-dark-gray/20 bg-white p-6 shadow-sm">
<div className="flex flex-col gap-6">
{participants && participants.length > 0 ? (
<div className="flex flex-col gap-6">
<Typography variant="h3" className="text-2xl font-semibold text-main-dark-green">
{t("reserveSummary.people.title")}
</Typography>
<div className="flex flex-col gap-5">
{participants.map((person, index) => (
<section key={person.id} className="rounded-2xl border border-dark-gray/20 bg-soft-white p-5 shadow-xs">
<header className="mb-4 flex items-center justify-between">
<Typography className="text-sm font-semibold uppercase tracking-[0.12em] text-main-dark-green/70">
{t("reserveSummary.people.personBadge", { index: index + 1 })}
</Typography>
</header>
<ReserveParticipantInputs person={person} readOnly className="mt-1" />
</section>
))}
</div>
{notes ? (
<div className="flex flex-col gap-2">
<Typography className="text-sm font-semibold text-main-dark-green">
{t("reserveSummary.people.notes")}
</Typography>
<Textarea
value={notes}
readOnly
className="min-h-[160px] resize-none border border-dark-gray/30 bg-soft-white/80 text-sm text-foreground"
/>
</div>
) : null}
</div>
) : null}
<div className="flex flex-col gap-3">
<Typography variant="h3" className="text-2xl font-semibold text-main-dark-green">
</Typography>
<div className="grid grid-cols-1 gap-5 md:grid-cols-2">
{experiencesToRender.map((experience) => (
<ReserveSummaryExperienceCard key={`${experience.title}-${experience.startDate}`} {...experience} />
))}
</div>
</div>
</div>
</CanvasCard>
{eventsToRender && eventsToRender.length > 0 ? (
<ReservationsLayout events={eventsToRender} />
) : null}
<div className="flex justify-end">
<Button
variant="ghost"
label={
<span className="flex items-center gap-2">
<ChevronLeft className="h-4 w-4" />
{t("reserveSummary.actions.back")}
</span>
}
onClick={onBack}
className="text-main-dark-green hover:bg-main-dark-green/10"
/>
</div>
</div>
</section>
);
}
Classe onde é criado a rota e os mocks
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { ReserveInfo } from "@/components/layouts/reserve/ReserveInfo";
import type { ReservationEvent } from "@/components/display/reservationEvents";
import type {
ReserveParticipant,
ReserveSummaryExperience,
} from "@/types/reserve";
export const Route = createFileRoute("/admin/requests/reservation-info/")({
component: ReserveInfoPage,
});
const mockPeople: ReserveParticipant[] = [
{
id: "1",
name: "Usuário 1",
phone: "(51) 99999-9999",
birthDate: "2000-10-11",
cpf: "123.456.789-00",
gender: "MALE",
},
{
id: "2",
name: "Usuário 2",
phone: "(51) 99999-9999",
birthDate: "1999-02-20",
cpf: "123.456.789-00",
gender: "FEMALE",
},
{
id: "3",
name: "Usuário 3",
phone: "(51) 99999-9999",
birthDate: "1995-07-03",
cpf: "123.456.789-00",
gender: "MALE",
},
];
const mockExperiences: ReserveSummaryExperience[] = [
{
title: "Experiência X",
startDate: "2025-08-11",
endDate: "2025-08-15",
price: 356.9,
peopleCount: 10,
imageUrl: "/public/mock/landscape-2.webp",
},
{
title: "Experiência Y",
startDate: "2025-08-18",
endDate: "2025-08-20",
price: 420,
peopleCount: 8,
imageUrl: "/public/mock/landscape-4.webp",
},
{
title: "Experiência Z",
startDate: "2025-09-01",
endDate: "2025-09-03",
price: 280,
peopleCount: 6,
imageUrl: "/public/mock/landscape-5.jpg",
},
];
function ReserveInfoPage() {
const navigate = useNavigate();
const mockEvents: ReservationEvent[] = [
{
id: "e1",
user: "Usuário",
status: "Solicitação enviada\nAguardando confirmação",
date: "12/10/2025",
time: "14:00",
avatarUrl: undefined,
},
{
id: "e2",
user: "Você",
status: "Reserva aceita\nInstruções enviadas",
date: "13/10/2025",
time: "09:30",
avatarUrl: undefined,
},
];
return (
<ReserveInfo
participants={mockPeople}
experiences={mockExperiences}
events={mockEvents}
notes="Sem observações"
onBack={() => navigate({ to: "/" })}
/>
);
}
-->
Acceptance Criteria
Não Havia nenhum critério
Screenshots da tela/componente desenvolvido
Observações
Rota não implementada no back e button action da tabela para levar a cada reserva também não existe
🔄 Sincronizado do GitHub
-
🔗 PR original: https://github.com/AGES-Pro-Mata/frontend/pull/131 -
👤 Autor: @JaymeFortes -
📅 Criado: 2025-10-12T20:33:40Z -
🔢 ID GitHub: #131 -
🌿 Branches:32-page-experiências-admin
→dev
-
📊 Estado: open -
🔀 Mergeable: unknown
Sincronizado automaticamente do GitHub para GitLab AGES