import "./index.css";
import { v4 as uuidv4 } from "uuid";
import {
useStreamContext,
type UIMessage,
} from "@langchain/langgraph-sdk/react-ui";
import { useEffect, useState } from "react";
import { X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { TripDetails } from "../../../trip-planner/types";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
import { format } from "date-fns";
import { Accommodation } from "agent/types";
import { capitalizeSentence } from "../../../utils/capitalize";
import { Message } from "@langchain/langgraph-sdk";
import { getToolResponse } from "../../utils/get-tool-response";
import { DO_NOT_RENDER_ID_PREFIX } from "@/lib/ensure-tool-responses";
const StarSVG = ({ fill = "white" }: { fill?: string }) => (
);
function AccommodationCard({
accommodation,
}: {
accommodation: Accommodation;
}) {
return (
{accommodation.name}
{accommodation.rating}
ยท
{accommodation.price}
{capitalizeSentence(accommodation.city)}
);
}
function SelectedAccommodation({
accommodation,
onHide,
tripDetails,
onBook,
}: {
accommodation: Accommodation;
onHide: () => void;
tripDetails: TripDetails;
onBook: (accommodation: Accommodation) => void;
}) {
const startDate = new Date(tripDetails.startDate);
const endDate = new Date(tripDetails.endDate);
const totalTripDurationDays = Math.max(
startDate.getDate() - endDate.getDate(),
1,
);
const totalPrice = totalTripDurationDays * accommodation.price;
return (
{accommodation.name}
{accommodation.rating}
{capitalizeSentence(accommodation.city)}
Check-in
{format(startDate, "MMM d, yyyy")}
Check-out
{format(endDate, "MMM d, yyyy")}
Guests
{tripDetails.numberOfGuests}
Total Price
${totalPrice.toLocaleString()}
);
}
function BookedAccommodation({
accommodation,
tripDetails,
}: {
accommodation: Accommodation;
tripDetails: TripDetails;
}) {
const startDate = new Date(tripDetails.startDate);
const endDate = new Date(tripDetails.endDate);
const totalTripDurationDays = Math.max(
startDate.getDate() - endDate.getDate(),
1,
);
const totalPrice = totalTripDurationDays * accommodation.price;
return (
Booked Accommodation
Address:
{accommodation.name}, {capitalizeSentence(accommodation.city)}
Rating:
{accommodation.rating}
Dates:
{format(startDate, "MMM d, yyyy")} -{" "}
{format(endDate, "MMM d, yyyy")}
Guests:
{tripDetails.numberOfGuests}
Total Price:
${totalPrice.toLocaleString()}
);
}
export default function AccommodationsList({
toolCallId,
tripDetails,
accommodations,
}: {
toolCallId: string;
tripDetails: TripDetails;
accommodations: Accommodation[];
}) {
const thread = useStreamContext<
{ messages: Message[]; ui: UIMessage[] },
{ MetaType: { ui: UIMessage | undefined } }
>();
const [selectedAccommodation, setSelectedAccommodation] = useState<
Accommodation | undefined
>();
const [accommodationBooked, setAccommodationBooked] = useState(false);
useEffect(() => {
if (typeof window === "undefined" || accommodationBooked) return;
const toolResponse = getToolResponse(toolCallId, thread);
if (toolResponse) {
setAccommodationBooked(true);
try {
const parsedContent: {
accommodation: Accommodation;
tripDetails: TripDetails;
} = JSON.parse(toolResponse.content as string);
setSelectedAccommodation(parsedContent.accommodation);
} catch {
console.error("Failed to parse tool response content.");
}
}
}, []);
function handleBookAccommodation(accommodation: Accommodation) {
const orderDetails = {
accommodation,
tripDetails,
};
thread.submit({
messages: [
{
type: "tool",
tool_call_id: toolCallId,
id: `${DO_NOT_RENDER_ID_PREFIX}${uuidv4()}`,
name: "trip-planner",
content: JSON.stringify(orderDetails),
},
{
type: "human",
content: `Booked ${accommodation.name} for ${tripDetails.numberOfGuests}.`,
},
],
});
setAccommodationBooked(true);
if (selectedAccommodation?.id !== accommodation.id) {
setSelectedAccommodation(accommodation);
}
}
if (accommodationBooked && selectedAccommodation) {
return (
);
} else if (accommodationBooked) {
return Successfully booked accommodation!
;
}
if (selectedAccommodation) {
return (
setSelectedAccommodation(undefined)}
accommodation={selectedAccommodation}
onBook={handleBookAccommodation}
/>
);
}
return (
{accommodations.map((accommodation) => (
setSelectedAccommodation(accommodation)}
>
))}
);
}