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