Chuyển tới nội dung chính

Code Examples

Các ví dụ code chi tiết để FE tham khảo.

Listings

Lấy danh sách Listings

'use client';

import { useGetListingsApiV1ListingsGet } from '@/generated/api/listings/listings';

export default function ListingList() {
const { data, isLoading, error, refetch } = useGetListingsApiV1ListingsGet({
page: 1,
limit: 20,
});

if (isLoading) return <Skeleton />;
if (error) return <ErrorDisplay error={error} />;

return (
<div>
<button onClick={() => refetch()}>Refresh</button>
<div className="listing-grid">
{data?.items?.map((listing) => (
<ListingCard key={listing.id} listing={listing} />
))}
</div>
</div>
);
}

Chi tiết một Listing

'use client';

import { useGetListingApiV1ListingsListingIdGet } from '@/generated/api/listings/listings';

interface Props {
listingId: string;
}

export default function ListingDetail({ listingId }: Props) {
const { data: listing, isLoading } = useGetListingApiV1ListingsListingIdGet({
listingId,
});

if (isLoading) return <Skeleton />;

return (
<div>
<h1>{listing?.name}</h1>
<p>{listing?.description}</p>
<ListingGallery images={listing?.images} />
<PricingInfo pricing={listing?.pricing} />
<AmenitiesList amenities={listing?.amenities} />
</div>
);
}

Tạo Listing mới

'use client';

import { useCreateListingApiV1ListingsPost } from '@/generated/api/listings/listings';
import { useRouter } from 'next/navigation';

export function CreateListingForm() {
const router = useRouter();
const createListing = useCreateListingApiV1ListingsPost();

const handleSubmit = async (formData: ListingFormData) => {
try {
const result = await createListing.mutateAsync({
data: {
name: formData.name,
type: formData.type,
location: formData.location,
capacity: formData.capacity,
// ... other fields
},
});

router.push(`/listings/${result.id}`);
} catch (error) {
console.error('Create failed:', error);
}
};

return <ListingForm onSubmit={handleSubmit} />;
}

Preview Listing trước khi tạo

'use client';

import { usePreviewListingApiV1ListingsPreviewPost } from '@/generated/api/listings/listings';

function PreviewBeforeSubmit({ formData }: { formData: ListingFormData }) {
const { data: preview, isLoading } = usePreviewListingApiV1ListingsPreviewPost({
data: formData,
});

if (isLoading) return <Loading />;

return (
<div>
{preview?.errors && preview.errors.length > 0 && (
<ValidationErrors errors={preview.errors} />
)}
{preview?.warnings && preview.warnings.length > 0 && (
<ValidationWarnings warnings={preview.warnings} />
)}
<EstimatedPricing pricing={preview?.estimated_pricing} />
</div>
);
}

Bookings

Lấy danh sách Bookings

'use client';

import { useGetMyBookingsApiV1BookingsMyGet } from '@/generated/api/bookings/bookings';

export function MyBookings() {
const { data, isLoading } = useGetMyBookingsApiV1BookingsMyGet({
page: 1,
limit: 20,
});

return (
<div>
{data?.items?.map((booking) => (
<BookingCard
key={booking.id}
booking={booking}
status={booking.state}
/>
))}
</div>
);
}

Chi tiết Booking

'use client';

import { useGetBookingApiV1BookingsBookingIdGet } from '@/generated/api/bookings/bookings';

interface Props {
bookingId: string;
}

export function BookingDetail({ bookingId }: Props) {
const { data: booking, isLoading } = useGetBookingApiV1BookingsBookingIdGet({
bookingId,
});

if (isLoading) return <Skeleton />;

return (
<div>
<BookingHeader booking={booking} />
<GuestInfo guest={booking?.guest_info} />
<StayDetails
checkIn={booking?.check_in}
checkOut={booking?.check_out}
guests={booking?.guests}
/>
<PaymentInfo payment={booking?.payment} />
<BookingTimeline status={booking?.state} />
</div>
);
}

Hủy Booking

'use client';

import { useCancelBookingApiV1BookingsBookingIdCancelPost } from '@/generated/api/bookings/bookings';

function CancelBookingButton({ bookingId }: { bookingId: string }) {
const cancelBooking = useCancelBookingApiV1BookingsBookingIdCancelPost();

const handleCancel = async (reason: string) => {
try {
await cancelBooking.mutateAsync({
bookingId,
data: { reason },
});
// Show success, redirect
} catch (error) {
// Handle error
}
};

return (
<button onClick={() => handleCancel('User requested')}>
Cancel Booking
</button>
);
}

Availability

Lấy Availability Calendar

'use client';

import { useGetListingAvailabilityApiV1ListingAvailabilityListingsListingIdGet } from '@/generated/api/availability/availability';

interface Props {
listingId: string;
month: string; // Format: '2025-03'
}

function AvailabilityCalendar({ listingId, month }: Props) {
const [year, monthNum] = month.split('-').map(Number);

const { data: availability } = useGetListingAvailabilityApiV1ListingAvailabilityListingsListingIdGet({
listingId,
year,
month: monthNum,
});

return (
<Calendar>
{availability?.days?.map((day) => (
<DayCell
key={day.date}
date={day.date}
status={day.status}
// AVAILABLE, BLOCKED, BOOKED, SOFT_LOCK
/>
))}
</Calendar>
);
}

Block dates

'use client';

import { useBlockListingAvailabilityApiV1ListingAvailabilityListingsListingIdBlocksPost } from '@/generated/api/availability/availability';

function BlockDatesForm({ listingId }: { listingId: string }) {
const blockDates = useBlockListingAvailabilityApiV1ListingAvailabilityListingsListingIdBlocksPost();

const handleBlock = async (startDate: string, endDate: string, reason: string) => {
await blockDates.mutateAsync({
listingId,
data: {
start_date: startDate,
end_date: endDate,
reason,
},
});
};

return <BlockDatesForm onSubmit={handleBlock} />;
}

Cart

Lấy Cart

'use client';

import { useGetCartApiV1CartGet } from '@/generated/api/cart/cart';

export function CartSummary() {
const { data: cart, isLoading } = useGetCartApiV1CartGet();

if (isLoading) return <CartSkeleton />;

return (
<div>
<h2>Giỏ hàng ({cart?.items?.length} items)</h2>
{cart?.items?.map((item) => (
<CartItem key={item.index} item={item} />
))}
<TotalDisplay total={cart?.total} />
</div>
);
}

Thêm vào Cart

'use client';

import { useAddCartItemApiV1CartItemsPost } from '@/generated/api/cart/cart';

function AddToCartButton({ listingId, checkIn, checkOut, guests }: AddToCartParams) {
const addToCart = useAddCartItemApiV1CartItemsPost();

const handleAdd = async () => {
await addToCart.mutateAsync({
data: {
listing_id: listingId,
check_in: checkIn,
check_out: checkOut,
guests,
},
});
};

return <button onClick={handleAdd}>Thêm vào giỏ</button>;
}

Checkout

Preview Booking

'use client';

import { usePreviewBookingApiV1CheckoutPreviewPost } from '@/generated/api/checkout/checkout';

function CheckoutPreview({ cartItems }: { cartItems: CartItem[] }) {
const { data: preview, isLoading } = usePreviewBookingApiV1CheckoutPreviewPost({
data: {
items: cartItems,
},
});

if (isLoading) return <Loading />;

return (
<div>
<PriceBreakdown
subtotal={preview?.subtotal}
fees={preview?.fees}
discount={preview?.discount}
total={preview?.total}
/>
<CancellationPolicy policy={preview?.cancellation_policy} />
</div>
);
}

Confirm Booking (Thanh toán)

'use client';

import { useConfirmBookingApiV1CheckoutConfirmPost } from '@/generated/api/checkout/checkout';
import { useRouter } from 'next/navigation';

function CheckoutButton({ cartItems }: { cartItems: CartItem[] }) {
const router = useRouter();
const confirmBooking = useConfirmBookingApiV1CheckoutConfirmPost();

const handleCheckout = async () => {
try {
const result = await confirmBooking.mutateAsync({
data: {
items: cartItems,
payment_method_id: 'payment_method_id_here',
},
});

// Redirect to booking confirmation
router.push(`/bookings/${result.booking_id}/confirmation`);
} catch (error) {
// Handle payment failure
}
};

return <button onClick={handleCheckout}>Xác nhận đặt phòng</button>;
}

Channels (Chat)

Lấy danh sách Channels

'use client';

import { useGetChannelsApiV1ChannelsGet } from '@/generated/api/channels/channels';

export function ChannelList() {
const { data, isLoading } = useGetChannelsApiV1ChannelsGet({
page: 1,
limit: 20,
});

return (
<div>
{data?.items?.map((channel) => (
<ChannelItem key={channel.id} channel={channel} />
))}
</div>
);
}

Gửi tin nhắn

'use client';

import { useSendChannelMessageApiV1ChannelsChannelIdMessagesPost } from '@/generated/api/channels/channels';

function MessageInput({ channelId }: { channelId: string }) {
const sendMessage = useSendChannelMessageApiV1ChannelsChannelIdMessagesPost();
const [message, setMessage] = useState('');

const handleSend = async () => {
if (!message.trim()) return;

await sendMessage.mutateAsync({
channelId,
data: {
content: message,
// type: 'text' | 'image' | 'file'
},
});

setMessage('');
};

return (
<div>
<input value={message} onChange={(e) => setMessage(e.target.value)} />
<button onClick={handleSend}>Gửi</button>
</div>
);
}

Upload Files

'use client';

import { useGetPublicUrlApiV1UploadsPublicUrlGet } from '@/generated/api/uploads/uploads';

function ImageUploader() {
const { data: uploadUrl } = useGetPublicUrlApiV1UploadsPublicUrlGet({
file_name: 'image.jpg',
content_type: 'image/jpeg',
});

const handleUpload = async (file: File) => {
// 1. Get pre-signed URL
const { upload_url, public_url } = uploadUrl;

// 2. Upload to S3/Cloudflare R2
await fetch(upload_url, {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type },
});

// 3. Return public URL
return public_url;
};

return <FileInput onUpload={handleUpload} />;
}