diff --git a/apps/web/app/[locale]/(public)/pricing/page.tsx b/apps/web/app/[locale]/(public)/pricing/page.tsx index 841e70a..186b934 100644 --- a/apps/web/app/[locale]/(public)/pricing/page.tsx +++ b/apps/web/app/[locale]/(public)/pricing/page.tsx @@ -40,98 +40,6 @@ const TIER_COLORS: Record = { ENTERPRISE: 'text-amber-600', }; -/** Fallback data when API is unavailable */ -const FALLBACK_PLANS: PlanDto[] = [ - { - id: 'fallback-free', - tier: 'FREE', - name: 'Miễn phí', - priceMonthlyVND: '0', - priceYearlyVND: '0', - maxListings: 3, - maxSavedSearches: 5, - features: { - basicSearch: true, - listingPost: true, - maxPhotos: 5, - analytics: false, - prioritySupport: false, - aiValuation: false, - featuredListing: false, - }, - isActive: true, - }, - { - id: 'fallback-agent', - tier: 'AGENT_PRO', - name: 'Agent Pro', - priceMonthlyVND: '499000', - priceYearlyVND: '4990000', - maxListings: 50, - maxSavedSearches: 30, - features: { - basicSearch: true, - listingPost: true, - maxPhotos: 30, - analytics: true, - prioritySupport: true, - aiValuation: true, - featuredListing: true, - leadManagement: true, - agentProfile: true, - }, - isActive: true, - }, - { - id: 'fallback-investor', - tier: 'INVESTOR', - name: 'Investor', - priceMonthlyVND: '999000', - priceYearlyVND: '9990000', - maxListings: 20, - maxSavedSearches: 100, - features: { - basicSearch: true, - listingPost: true, - maxPhotos: 15, - analytics: true, - prioritySupport: true, - aiValuation: true, - featuredListing: false, - marketReports: true, - priceAlerts: true, - portfolioTracking: true, - }, - isActive: true, - }, - { - id: 'fallback-enterprise', - tier: 'ENTERPRISE', - name: 'Enterprise', - priceMonthlyVND: '4990000', - priceYearlyVND: '49900000', - maxListings: -1, - maxSavedSearches: -1, - features: { - basicSearch: true, - listingPost: true, - maxPhotos: 100, - analytics: true, - prioritySupport: true, - aiValuation: true, - featuredListing: true, - leadManagement: true, - agentProfile: true, - marketReports: true, - priceAlerts: true, - portfolioTracking: true, - apiAccess: true, - whiteLabel: true, - dedicatedSupport: true, - }, - isActive: true, - }, -]; // --------------------------------------------------------------------------- // Helpers @@ -209,7 +117,7 @@ export default function PricingPage() { const [checkoutPlan, setCheckoutPlan] = useState(null); const [checkoutOpen, setCheckoutOpen] = useState(false); - const plans = (plansData ?? (error ? FALLBACK_PLANS : [])) + const plans = (plansData ?? []) .slice() .sort( (a, b) => @@ -316,6 +224,13 @@ export default function PricingPage() {
{t('loading')}
+ ) : error ? ( +
+

+ {t('errorLoadingPlans')} +

+

{t('errorLoadingPlansHint')}

+
) : (
{plans.map((plan) => { diff --git a/apps/web/components/listings/image-gallery.tsx b/apps/web/components/listings/image-gallery.tsx index fb1627b..343f788 100644 --- a/apps/web/components/listings/image-gallery.tsx +++ b/apps/web/components/listings/image-gallery.tsx @@ -2,6 +2,7 @@ import Image from 'next/image'; import * as React from 'react'; +import { useSwipeable } from 'react-swipeable'; import { ImageLightbox } from '@/components/listings/image-lightbox'; import { shimmerBlurDataURL } from '@/lib/image-blur'; import type { PropertyMedia } from '@/lib/listings-api'; @@ -22,6 +23,13 @@ export function ImageGallery({ media, className }: ImageGalleryProps) { setLightboxOpen(true); }, []); + const swipeHandlers = useSwipeable({ + onSwipedLeft: () => setSelectedIndex((i) => (i < images.length - 1 ? i + 1 : 0)), + onSwipedRight: () => setSelectedIndex((i) => (i > 0 ? i - 1 : images.length - 1)), + preventScrollOnSwipe: true, + trackMouse: false, + }); + if (images.length === 0) { return (
{/* Main image */} -
+
1 ? swipeHandlers : {})}>