@burdenoff/microfe-kadaikodi 2026.710.1 → 2026.716.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/kadaikodi/pages/HomePage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/HomePage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminDashboard.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminFinancialPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminFinancialPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminOrdersPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminOrdersPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/CartManagementPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/CartManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/InvestmentListingPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/MerchantOnboardingPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/MerchantOnboardingPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/BookingDetailsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/BookingDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CartDetailPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/CartDetailPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CheckoutPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/FavoritesPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/FavoritesPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/OrderTrackingPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/OrderTrackingPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/ReviewsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/ReviewsPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/shared.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/shared.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js +3 -3
- package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestorPortfolioPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestorPortfolioPage.js.map +1 -1
- package/dist/kadaikodi/pages/worker/TaskDetailsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/worker/TaskDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerDashboard.d.ts.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerSchedulePage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerSchedulePage.js.map +1 -1
- package/dist/services/createSandboxAssistantTransport.d.ts +8 -27
- package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -1
- package/dist/services/createSandboxAssistantTransport.js +7 -10
- package/dist/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/stories/EmptyState.stories.d.ts +10 -0
- package/dist/stories/EmptyState.stories.d.ts.map +1 -0
- package/dist/stories/MetricsCard.stories.d.ts +10 -0
- package/dist/stories/MetricsCard.stories.d.ts.map +1 -0
- package/dist/stories/StatusBadge.stories.d.ts +12 -0
- package/dist/stories/StatusBadge.stories.d.ts.map +1 -0
- package/package.json +15 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BookingDetailsPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/customer/BookingDetailsPage.tsx"],"sourcesContent":["/**\n * Booking Details Page\n * @module kadaikodi/pages/customer\n *\n * Detailed view of a service booking with service name, provider info,\n * scheduled date/time, booking status timeline, location info,\n * price breakdown, Contact Support button, Rate Service button (when completed),\n * Cart Information sidebar, cancel button.\n * Uses route param :bookingId from useParams().\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n MapPin,\n Star,\n CheckCircle2,\n XCircle,\n ShoppingBag,\n CalendarCheck,\n Headphones,\n Loader2,\n X,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { Timeline, type TimelineItem } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useOrder, useCreateReview, useUpdateOrderStatus } from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatDate } from '../../utils/formatters';\n\nconst SUPPORT_EMAIL = 'support@kadaikodi.com';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface BookingTimelineEvent {\n status: string;\n label: string;\n timestamp: string | null;\n completed: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function BookingDetailsPage() {\n const { bookingId } = useParams<{ bookingId: string }>();\n const { navigateTo, currentUser } = useKadaikodi();\n const { data: order, isLoading } = useOrder(bookingId);\n const createReview = useCreateReview();\n const cancelOrder = useUpdateOrderStatus();\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n const [showRateModal, setShowRateModal] = useState(false);\n const [rating, setRating] = useState(5);\n const [reviewText, setReviewText] = useState('');\n\n const showStatus = (msg: string) => {\n setStatusMessage(msg);\n setTimeout(() => setStatusMessage(null), 3000);\n };\n\n // Build the timeline from the order's real recorded events; fall back to a\n // single \"order placed\" milestone derived from the order date.\n const timeline = useMemo<BookingTimelineEvent[]>(() => {\n if (!order) return [];\n if (order.timeline.length > 0) {\n return order.timeline.map((e) => ({\n status: e.status,\n label: e.description || e.status,\n timestamp: e.timestamp,\n completed: true,\n }));\n }\n return [\n { status: 'placed', label: 'Order placed', timestamp: order.orderDate, completed: true },\n ];\n }, [order]);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Booking Details\">\n <div className=\"flex items-center justify-center py-20\">\n <div className=\"h-12 w-12 animate-spin rounded-full border-b-2 border-foreground\" />\n </div>\n </PageLayout>\n );\n }\n\n if (!order) {\n return (\n <PageLayout title=\"Booking Not Found\">\n <EmptyState\n icon={<CalendarCheck className=\"h-12 w-12\" />}\n title=\"Booking not found\"\n description={`No booking found with ID \"${bookingId}\".`}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/dashboard')}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Go to Dashboard\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const isCancelled = order.status === 'cancelled';\n const isCompleted = order.status === 'delivered';\n const canCancel = !isCancelled && !isCompleted;\n const addr = order.deliveryAddress;\n const hasAddress = Boolean(addr.street || addr.area || addr.city);\n\n const alreadyRated = order.rating != null && order.rating > 0;\n\n const handleSubmitRating = () => {\n if (!currentUser?.id) {\n showStatus('Please sign in to rate this service.');\n return;\n }\n createReview.mutate(\n {\n providerId: order.cartId,\n orderId: order.id,\n customerId: currentUser.id,\n rating,\n review: reviewText.trim() || undefined,\n },\n {\n onSuccess: () => {\n setShowRateModal(false);\n setReviewText('');\n showStatus('Thanks! Your rating has been submitted.');\n },\n onError: () => showStatus('Could not submit your rating. Please try again.'),\n }\n );\n };\n\n const handleCancelBooking = () => {\n if (!window.confirm('Cancel this booking? This cannot be undone.')) return;\n cancelOrder.mutate(\n { id: order.id, status: 'CANCELLED' },\n {\n onSuccess: () => showStatus('Your booking has been cancelled.'),\n onError: () => showStatus('Could not cancel the booking. Please try again.'),\n }\n );\n };\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/dashboard')}\n className=\"rounded-md p-1 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n <span>Booking #{order.id}</span>\n </div>\n }\n actions={<StatusBadge status={order.status} />}\n >\n <PagePurpose className=\"mb-4\">\n The full picture of one service booking — who's providing it, what was ordered, what it\n costs, and the actions you can take such as contacting support or cancelling.\n </PagePurpose>\n\n {/* Status message */}\n {statusMessage && (\n <div className=\"mb-4 rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle p-3 flex items-center gap-2\">\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text shrink-0\" />\n <p className=\"text-sm text-status-success-text\">{statusMessage}</p>\n </div>\n )}\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Left: Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Service Info — the one emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <h2 className=\"text-lg font-semibold text-text-primary\">{order.cartName}</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Order placed on {formatDate(order.orderDate)}\n </p>\n\n {order.items.length > 0 && (\n <div className=\"mt-4 space-y-1.5\">\n {order.items.map((item, index) => (\n <div key={item.id || index} className=\"flex justify-between text-sm\">\n <span className=\"text-text-primary\">\n {item.quantity}x {item.name}\n </span>\n <span className=\"text-text-secondary\">\n {formatCurrency(item.price * item.quantity, { minimumFractionDigits: 0 })}\n </span>\n </div>\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Provider Info */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <h2 className=\"text-base font-semibold text-text-primary mb-4\">Service Provider</h2>\n <div className=\"flex items-center gap-4\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n <ShoppingBag className=\"h-6 w-6\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-semibold text-text-primary\">{order.cartName}</p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/cart/${order.cartId}`)}\n className=\"rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n View Cart\n </button>\n </div>\n </div>\n\n {/* Booking Status Timeline */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <h2 className=\"text-base font-semibold text-text-primary mb-5\">Booking Timeline</h2>\n <Timeline\n items={timeline.map(\n (event, index): TimelineItem => ({\n id: `${event.status}-${index}`,\n title: event.label,\n tone:\n event.status === 'cancelled'\n ? 'danger'\n : event.completed\n ? 'success'\n : 'muted',\n time: event.timestamp ? formatDate(event.timestamp) : 'Pending',\n })\n )}\n />\n </div>\n\n {/* Action Buttons: Contact Support + Rate Service */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <a\n href={`mailto:${SUPPORT_EMAIL}?subject=${encodeURIComponent(\n `Support request — Booking #${order.id}`\n )}`}\n className=\"flex-1 flex items-center justify-center gap-2 rounded-lg border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n <Headphones className=\"h-4 w-4\" />\n Contact Support\n </a>\n\n {isCompleted && (\n <button\n type=\"button\"\n disabled={alreadyRated}\n onClick={() => setShowRateModal(true)}\n className={cn(\n 'flex-1 flex items-center justify-center gap-2 rounded-lg px-4 py-3 text-sm font-medium transition-opacity',\n alreadyRated\n ? 'bg-bg-sunken text-text-secondary cursor-not-allowed'\n : 'bg-foreground text-background hover:opacity-90'\n )}\n >\n <Star className=\"h-4 w-4\" />\n {alreadyRated ? 'Rated' : 'Rate Service'}\n </button>\n )}\n </div>\n </div>\n\n {/* Right: Sidebar */}\n <div className=\"space-y-6\">\n {/* Cart Information */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3\">Cart Information</h3>\n <div className=\"flex items-center gap-3 mb-4\">\n <div className=\"flex h-14 w-14 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-text-secondary\">\n <ShoppingBag className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-text-primary\">{order.cartName}</p>\n </div>\n </div>\n\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/cart/${order.cartId}`)}\n className=\"w-full rounded-lg border border-border-subtle px-4 py-2.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n View Cart Menu\n </button>\n </div>\n\n {/* Location Info */}\n {hasAddress && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3\">Service Location</h3>\n <div className=\"flex items-start gap-3\">\n <MapPin className=\"mt-0.5 h-5 w-5 shrink-0 text-text-secondary\" />\n <div>\n {addr.street && <p className=\"text-sm text-text-primary\">{addr.street}</p>}\n <p className=\"text-sm text-text-secondary\">\n {[addr.area, addr.city].filter(Boolean).join(', ')}\n {addr.pincode ? ` - ${addr.pincode}` : ''}\n </p>\n {addr.landmark && (\n <p className=\"text-xs text-text-secondary mt-1\">Landmark: {addr.landmark}</p>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* Price Breakdown */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3\">Price Breakdown</h3>\n <div className=\"space-y-2.5 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Subtotal</span>\n <span className=\"text-text-primary\">\n {formatCurrency(order.subtotal, { minimumFractionDigits: 0 })}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Delivery Fee</span>\n <span className=\"text-text-primary\">\n {order.deliveryFee === 0\n ? 'FREE'\n : formatCurrency(order.deliveryFee, { minimumFractionDigits: 0 })}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Tax</span>\n <span className=\"text-text-primary\">\n {formatCurrency(order.taxes, { minimumFractionDigits: 0 })}\n </span>\n </div>\n {order.discount != null && order.discount > 0 && (\n <div className=\"flex justify-between\">\n <span className=\"text-status-success-text\">Discount</span>\n <span className=\"text-status-success-text\">\n -{formatCurrency(order.discount, { minimumFractionDigits: 0 })}\n </span>\n </div>\n )}\n <div className=\"flex justify-between border-t border-border-subtle pt-2.5\">\n <span className=\"font-semibold text-text-primary\">Total</span>\n <span className=\"font-bold text-text-primary\">\n {formatCurrency(order.total, { minimumFractionDigits: 0 })}\n </span>\n </div>\n </div>\n </div>\n\n {/* Booking Info */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3\">Booking Info</h3>\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <CalendarCheck className=\"h-4 w-4 text-text-secondary\" />\n <div>\n <p className=\"text-xs text-text-secondary\">Booked On</p>\n <p className=\"text-sm text-text-primary\">{formatDate(order.orderDate)}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <CheckCircle2 className=\"h-4 w-4 text-text-secondary\" />\n <div>\n <p className=\"text-xs text-text-secondary\">Payment</p>\n <p className=\"text-sm text-text-primary capitalize\">\n {order.paymentMethod} · {order.paymentStatus}\n </p>\n </div>\n </div>\n </div>\n </div>\n\n {/* Cancel Button */}\n {canCancel && (\n <button\n type=\"button\"\n onClick={handleCancelBooking}\n disabled={cancelOrder.isPending}\n className={cn(\n 'w-full inline-flex items-center justify-center gap-2 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm font-medium text-status-error-text transition-colors',\n cancelOrder.isPending\n ? 'opacity-60 cursor-not-allowed'\n : 'hover:bg-status-error-bg-subtle'\n )}\n >\n {cancelOrder.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <XCircle className=\"h-4 w-4\" />\n )}\n Cancel Booking\n </button>\n )}\n </div>\n </div>\n\n {/* Rate Service Modal */}\n {showRateModal && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"w-full max-w-md rounded-lg border border-border-subtle bg-bg-surface shadow-lg\">\n <div className=\"p-5\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Rate this service</h3>\n <button\n type=\"button\"\n onClick={() => setShowRateModal(false)}\n className=\"rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Your rating\n </label>\n <div className=\"flex items-center gap-1\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n type=\"button\"\n onClick={() => setRating(star)}\n className=\"p-0.5\"\n aria-label={`${star} star${star > 1 ? 's' : ''}`}\n >\n <Star\n className={cn(\n 'h-7 w-7',\n star <= rating\n ? 'fill-status-warning-text text-status-warning-text'\n : 'fill-none text-text-secondary/40'\n )}\n />\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"mb-5\">\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Review <span className=\"text-text-secondary font-normal\">(optional)</span>\n </label>\n <textarea\n rows={4}\n value={reviewText}\n onChange={(e) => setReviewText(e.target.value)}\n placeholder=\"Share your experience with this service...\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleSubmitRating}\n disabled={createReview.isPending}\n className={cn(\n 'flex-1 inline-flex items-center justify-center gap-2 rounded-lg bg-foreground px-4 py-2.5 text-sm font-medium text-background transition-opacity',\n createReview.isPending ? 'opacity-60 cursor-not-allowed' : 'hover:opacity-90'\n )}\n >\n {createReview.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n Submit Rating\n </button>\n <button\n type=\"button\"\n onClick={() => setShowRateModal(false)}\n className=\"rounded-lg border border-border-subtle px-4 py-2.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BookingDetailsPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/customer/BookingDetailsPage.tsx"],"sourcesContent":["/**\n * Booking Details Page\n * @module kadaikodi/pages/customer\n *\n * Detailed view of a service booking with service name, provider info,\n * scheduled date/time, booking status timeline, location info,\n * price breakdown, Contact Support button, Rate Service button (when completed),\n * Cart Information sidebar, cancel button.\n * Uses route param :bookingId from useParams().\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n MapPin,\n Star,\n CheckCircle2,\n XCircle,\n ShoppingBag,\n CalendarCheck,\n Headphones,\n Loader2,\n X,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { Timeline, type TimelineItem } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useOrder, useCreateReview, useUpdateOrderStatus } from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatDate } from '../../utils/formatters';\n\nconst SUPPORT_EMAIL = 'support@kadaikodi.com';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface BookingTimelineEvent {\n status: string;\n label: string;\n timestamp: string | null;\n completed: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function BookingDetailsPage() {\n const { bookingId } = useParams<{ bookingId: string }>();\n const { navigateTo, currentUser } = useKadaikodi();\n const { data: order, isLoading } = useOrder(bookingId);\n const createReview = useCreateReview();\n const cancelOrder = useUpdateOrderStatus();\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n const [showRateModal, setShowRateModal] = useState(false);\n const [rating, setRating] = useState(5);\n const [reviewText, setReviewText] = useState('');\n\n const showStatus = (msg: string) => {\n setStatusMessage(msg);\n setTimeout(() => setStatusMessage(null), 3000);\n };\n\n // Build the timeline from the order's real recorded events; fall back to a\n // single \"order placed\" milestone derived from the order date.\n const timeline = useMemo<BookingTimelineEvent[]>(() => {\n if (!order) return [];\n if (order.timeline.length > 0) {\n return order.timeline.map((e) => ({\n status: e.status,\n label: e.description || e.status,\n timestamp: e.timestamp,\n completed: true,\n }));\n }\n return [\n { status: 'placed', label: 'Order placed', timestamp: order.orderDate, completed: true },\n ];\n }, [order]);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Booking Details\">\n <div className=\"flex items-center justify-center py-20\">\n <div className=\"h-12 w-12 animate-spin rounded-full border-b-2 border-foreground\" />\n </div>\n </PageLayout>\n );\n }\n\n if (!order) {\n return (\n <PageLayout title=\"Booking Not Found\">\n <EmptyState\n icon={<CalendarCheck className=\"h-12 w-12\" />}\n title=\"Booking not found\"\n description={`No booking found with ID \"${bookingId}\".`}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/dashboard')}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Go to Dashboard\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const isCancelled = order.status === 'cancelled';\n const isCompleted = order.status === 'delivered';\n const canCancel = !isCancelled && !isCompleted;\n const addr = order.deliveryAddress;\n const hasAddress = Boolean(addr.street || addr.area || addr.city);\n\n const alreadyRated = order.rating != null && order.rating > 0;\n\n const handleSubmitRating = () => {\n if (!currentUser?.id) {\n showStatus('Please sign in to rate this service.');\n return;\n }\n createReview.mutate(\n {\n providerId: order.cartId,\n orderId: order.id,\n customerId: currentUser.id,\n rating,\n review: reviewText.trim() || undefined,\n },\n {\n onSuccess: () => {\n setShowRateModal(false);\n setReviewText('');\n showStatus('Thanks! Your rating has been submitted.');\n },\n onError: () => showStatus('Could not submit your rating. Please try again.'),\n }\n );\n };\n\n const handleCancelBooking = () => {\n if (!window.confirm('Cancel this booking? This cannot be undone.')) return;\n cancelOrder.mutate(\n { id: order.id, status: 'CANCELLED' },\n {\n onSuccess: () => showStatus('Your booking has been cancelled.'),\n onError: () => showStatus('Could not cancel the booking. Please try again.'),\n }\n );\n };\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/dashboard')}\n className=\"rounded-md p-1 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n <span>Booking #{order.id}</span>\n </div>\n }\n actions={<StatusBadge status={order.status} />}\n >\n <PagePurpose className=\"mb-4\">\n The full picture of one service booking — who's providing it, what was ordered, what it\n costs, and the actions you can take such as contacting support or cancelling.\n </PagePurpose>\n\n {/* Status message */}\n {statusMessage && (\n <div className=\"mb-4 rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle p-3 flex items-center gap-2\">\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text shrink-0\" />\n <p className=\"text-sm text-status-success-text\">{statusMessage}</p>\n </div>\n )}\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Left: Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Service Info — the one emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <h2 className=\"text-lg font-semibold text-text-primary\">{order.cartName}</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Order placed on {formatDate(order.orderDate)}\n </p>\n\n {order.items.length > 0 && (\n <div className=\"mt-4 space-y-1.5\">\n {order.items.map((item, index) => (\n <div key={item.id || index} className=\"flex justify-between text-sm\">\n <span className=\"text-text-primary\">\n {item.quantity}x {item.name}\n </span>\n <span className=\"text-text-secondary\">\n {formatCurrency(item.price * item.quantity, { minimumFractionDigits: 0 })}\n </span>\n </div>\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Provider Info */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <h2 className=\"text-base font-semibold text-text-primary mb-4\">Service Provider</h2>\n <div className=\"flex items-center gap-4\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n <ShoppingBag className=\"h-6 w-6\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-semibold text-text-primary\">{order.cartName}</p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/cart/${order.cartId}`)}\n className=\"rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n View Cart\n </button>\n </div>\n </div>\n\n {/* Booking Status Timeline */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <h2 className=\"text-base font-semibold text-text-primary mb-5\">Booking Timeline</h2>\n <Timeline\n items={timeline.map(\n (event, index): TimelineItem => ({\n id: `${event.status}-${index}`,\n title: event.label,\n tone:\n event.status === 'cancelled' ? 'danger' : event.completed ? 'success' : 'muted',\n time: event.timestamp ? formatDate(event.timestamp) : 'Pending',\n })\n )}\n />\n </div>\n\n {/* Action Buttons: Contact Support + Rate Service */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <a\n href={`mailto:${SUPPORT_EMAIL}?subject=${encodeURIComponent(\n `Support request — Booking #${order.id}`\n )}`}\n className=\"flex-1 flex items-center justify-center gap-2 rounded-lg border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n <Headphones className=\"h-4 w-4\" />\n Contact Support\n </a>\n\n {isCompleted && (\n <button\n type=\"button\"\n disabled={alreadyRated}\n onClick={() => setShowRateModal(true)}\n className={cn(\n 'flex-1 flex items-center justify-center gap-2 rounded-lg px-4 py-3 text-sm font-medium transition-opacity',\n alreadyRated\n ? 'bg-bg-sunken text-text-secondary cursor-not-allowed'\n : 'bg-foreground text-background hover:opacity-90'\n )}\n >\n <Star className=\"h-4 w-4\" />\n {alreadyRated ? 'Rated' : 'Rate Service'}\n </button>\n )}\n </div>\n </div>\n\n {/* Right: Sidebar */}\n <div className=\"space-y-6\">\n {/* Cart Information */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3\">Cart Information</h3>\n <div className=\"flex items-center gap-3 mb-4\">\n <div className=\"flex h-14 w-14 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-text-secondary\">\n <ShoppingBag className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-text-primary\">{order.cartName}</p>\n </div>\n </div>\n\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/cart/${order.cartId}`)}\n className=\"w-full rounded-lg border border-border-subtle px-4 py-2.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n View Cart Menu\n </button>\n </div>\n\n {/* Location Info */}\n {hasAddress && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3\">Service Location</h3>\n <div className=\"flex items-start gap-3\">\n <MapPin className=\"mt-0.5 h-5 w-5 shrink-0 text-text-secondary\" />\n <div>\n {addr.street && <p className=\"text-sm text-text-primary\">{addr.street}</p>}\n <p className=\"text-sm text-text-secondary\">\n {[addr.area, addr.city].filter(Boolean).join(', ')}\n {addr.pincode ? ` - ${addr.pincode}` : ''}\n </p>\n {addr.landmark && (\n <p className=\"text-xs text-text-secondary mt-1\">Landmark: {addr.landmark}</p>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* Price Breakdown */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3\">Price Breakdown</h3>\n <div className=\"space-y-2.5 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Subtotal</span>\n <span className=\"text-text-primary\">\n {formatCurrency(order.subtotal, { minimumFractionDigits: 0 })}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Delivery Fee</span>\n <span className=\"text-text-primary\">\n {order.deliveryFee === 0\n ? 'FREE'\n : formatCurrency(order.deliveryFee, { minimumFractionDigits: 0 })}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Tax</span>\n <span className=\"text-text-primary\">\n {formatCurrency(order.taxes, { minimumFractionDigits: 0 })}\n </span>\n </div>\n {order.discount != null && order.discount > 0 && (\n <div className=\"flex justify-between\">\n <span className=\"text-status-success-text\">Discount</span>\n <span className=\"text-status-success-text\">\n -{formatCurrency(order.discount, { minimumFractionDigits: 0 })}\n </span>\n </div>\n )}\n <div className=\"flex justify-between border-t border-border-subtle pt-2.5\">\n <span className=\"font-semibold text-text-primary\">Total</span>\n <span className=\"font-bold text-text-primary\">\n {formatCurrency(order.total, { minimumFractionDigits: 0 })}\n </span>\n </div>\n </div>\n </div>\n\n {/* Booking Info */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3\">Booking Info</h3>\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <CalendarCheck className=\"h-4 w-4 text-text-secondary\" />\n <div>\n <p className=\"text-xs text-text-secondary\">Booked On</p>\n <p className=\"text-sm text-text-primary\">{formatDate(order.orderDate)}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <CheckCircle2 className=\"h-4 w-4 text-text-secondary\" />\n <div>\n <p className=\"text-xs text-text-secondary\">Payment</p>\n <p className=\"text-sm text-text-primary capitalize\">\n {order.paymentMethod} · {order.paymentStatus}\n </p>\n </div>\n </div>\n </div>\n </div>\n\n {/* Cancel Button */}\n {canCancel && (\n <button\n type=\"button\"\n onClick={handleCancelBooking}\n disabled={cancelOrder.isPending}\n className={cn(\n 'w-full inline-flex items-center justify-center gap-2 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm font-medium text-status-error-text transition-colors',\n cancelOrder.isPending\n ? 'opacity-60 cursor-not-allowed'\n : 'hover:bg-status-error-bg-subtle'\n )}\n >\n {cancelOrder.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <XCircle className=\"h-4 w-4\" />\n )}\n Cancel Booking\n </button>\n )}\n </div>\n </div>\n\n {/* Rate Service Modal */}\n {showRateModal && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"w-full max-w-md rounded-lg border border-border-subtle bg-bg-surface shadow-lg\">\n <div className=\"p-5\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Rate this service</h3>\n <button\n type=\"button\"\n onClick={() => setShowRateModal(false)}\n className=\"rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Your rating\n </label>\n <div className=\"flex items-center gap-1\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n type=\"button\"\n onClick={() => setRating(star)}\n className=\"p-0.5\"\n aria-label={`${star} star${star > 1 ? 's' : ''}`}\n >\n <Star\n className={cn(\n 'h-7 w-7',\n star <= rating\n ? 'fill-status-warning-text text-status-warning-text'\n : 'fill-none text-text-secondary/40'\n )}\n />\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"mb-5\">\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Review <span className=\"text-text-secondary font-normal\">(optional)</span>\n </label>\n <textarea\n rows={4}\n value={reviewText}\n onChange={(e) => setReviewText(e.target.value)}\n placeholder=\"Share your experience with this service...\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleSubmitRating}\n disabled={createReview.isPending}\n className={cn(\n 'flex-1 inline-flex items-center justify-center gap-2 rounded-lg bg-foreground px-4 py-2.5 text-sm font-medium text-background transition-opacity',\n createReview.isPending ? 'opacity-60 cursor-not-allowed' : 'hover:opacity-90'\n )}\n >\n {createReview.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n Submit Rating\n </button>\n <button\n type=\"button\"\n onClick={() => setShowRateModal(false)}\n className=\"rounded-lg border border-border-subtle px-4 py-2.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CartDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/customer/CartDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"CartDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/customer/CartDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAuEH,wBAAgB,cAAc,gCA2yB7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CartDetailPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/customer/CartDetailPage.tsx"],"sourcesContent":["/**\n * Cart Detail Page\n * @module kadaikodi/pages/customer\n *\n * Full detail view with shopping cart sidebar (Your Order with +/- controls,\n * remove, total, Proceed to Checkout), product customization modal,\n * category-grouped product listing, search, category filter,\n * investment opportunity card, scheduled stops, direct service card,\n * veg/vegan icons, availability badges, calories, original price\n * strikethrough, and cart closed warning banner.\n */\n\nimport { useState, useMemo, useEffect } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Star,\n MapPin,\n Clock,\n CheckCircle2,\n ShoppingCart,\n Search,\n Leaf,\n Minus,\n Plus,\n X,\n TrendingUp,\n Home,\n Calendar,\n Flame,\n Heart,\n MessageSquare,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { RatingStars, SegmentedTabs } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useProviders,\n useOfferings,\n useInvestmentOpportunities,\n useReviews,\n} from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatDistance, formatDuration, formatRelativeTime } from '../../utils/formatters';\nimport type { CartMenuItem, InvestmentOpportunity } from '../../types';\nimport { ProviderImage, useFavorites } from './shared';\n\ntype DetailTab = 'menu' | 'reviews' | 'info';\n\ninterface CartItem {\n id: string;\n productId: string;\n name: string;\n price: number;\n quantity: number;\n specialInstructions?: string;\n}\n\ninterface CustomizationState {\n product: CartMenuItem;\n quantity: number;\n specialInstructions: string;\n}\n\nconst DAY_LABELS: Record<string, string> = {\n monday: 'Mon',\n tuesday: 'Tue',\n wednesday: 'Wed',\n thursday: 'Thu',\n friday: 'Fri',\n saturday: 'Sat',\n sunday: 'Sun',\n};\n\nexport function CartDetailPage() {\n const { cartId } = useParams<{ cartId: string }>();\n const { navigateTo } = useKadaikodi();\n\n const { data: providers = [], isLoading: isLoadingProviders } = useProviders();\n const { data: offerings = [], isLoading: isLoadingOfferings } = useOfferings(cartId);\n const { data: investments = [], isLoading: isLoadingInvestments } = useInvestmentOpportunities();\n\n const { data: reviews = [] } = useReviews(cartId ?? '');\n const favorites = useFavorites();\n\n const cart = useMemo(() => providers.find((p) => p.id === cartId), [providers, cartId]);\n const investment = useMemo(\n () => investments.find((i) => i.cartId === cartId),\n [investments, cartId]\n );\n\n const [activeTab, setActiveTab] = useState<DetailTab>('menu');\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedCategory, setSelectedCategory] = useState('');\n const [cartItems, setCartItems] = useState<CartItem[]>([]);\n const [showCustomization, setShowCustomization] = useState(false);\n const [customization, setCustomization] = useState<CustomizationState | null>(null);\n\n const menuItems = useMemo(\n () =>\n offerings.map((o) => ({\n id: o.id,\n name: o.name,\n price: o.price,\n description: o.description ?? '',\n image: o.media?.[0] ?? '',\n category: o.category,\n isVeg: true,\n duration: o.durationMinutes ?? undefined,\n serviceType: (o.serviceType ?? undefined) as CartMenuItem['serviceType'],\n })),\n [offerings]\n );\n\n const menuCategories = useMemo(() => {\n const cats = [...new Set(menuItems.map((item) => item.category))];\n return cats;\n }, [menuItems]);\n\n const filteredMenu = useMemo(() => {\n let items = menuItems;\n\n if (selectedCategory) {\n items = items.filter((item) => item.category === selectedCategory);\n }\n\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n items = items.filter(\n (item) => item.name.toLowerCase().includes(q) || item.description.toLowerCase().includes(q)\n );\n }\n\n return items;\n }, [menuItems, selectedCategory, searchQuery]);\n\n const isLoading = isLoadingProviders || isLoadingOfferings || isLoadingInvestments;\n\n const groupedMenu = useMemo(() => {\n const groups: Record<string, CartMenuItem[]> = {};\n filteredMenu.forEach((item) => {\n if (!groups[item.category]) groups[item.category] = [];\n groups[item.category].push(item);\n });\n return groups;\n }, [filteredMenu]);\n\n // Cart operations\n const addToCart = (product: CartMenuItem, qty: number = 1, instructions?: string) => {\n setCartItems((prev) => {\n const existing = prev.find((item) => item.productId === product.id);\n if (existing) {\n return prev.map((item) =>\n item.productId === product.id ? { ...item, quantity: item.quantity + qty } : item\n );\n }\n return [\n ...prev,\n {\n id: `cart-${Date.now()}`,\n productId: product.id,\n name: product.name,\n price: product.price,\n quantity: qty,\n specialInstructions: instructions,\n },\n ];\n });\n };\n\n const updateQuantity = (productId: string, delta: number) => {\n setCartItems((prev) =>\n prev\n .map((item) =>\n item.productId === productId\n ? { ...item, quantity: Math.max(0, item.quantity + delta) }\n : item\n )\n .filter((item) => item.quantity > 0)\n );\n };\n\n const removeFromCart = (productId: string) => {\n setCartItems((prev) => prev.filter((item) => item.productId !== productId));\n };\n\n const getItemQuantity = (productId: string) => {\n return cartItems.find((item) => item.productId === productId)?.quantity ?? 0;\n };\n\n const cartTotal = cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);\n const cartCount = cartItems.reduce((sum, item) => sum + item.quantity, 0);\n\n const handleAddToCart = (product: CartMenuItem) => {\n setCustomization({ product, quantity: 1, specialInstructions: '' });\n setShowCustomization(true);\n };\n\n const confirmAddToCart = () => {\n if (!customization) return;\n addToCart(customization.product, customization.quantity, customization.specialInstructions);\n setShowCustomization(false);\n setCustomization(null);\n alert(`${customization.product.name} added to cart`);\n };\n\n if (!cart) {\n return (\n <PageLayout title=\"Cart Not Found\">\n <EmptyState\n icon={<ShoppingCart className=\"h-12 w-12\" />}\n title=\"Cart not found\"\n description={`No cart found with ID \"${cartId}\".`}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/discover')}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Browse Carts\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/discover')}\n className=\"rounded-md p-1 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n <span className=\"truncate\">{cart.name}</span>\n {cart.isVerified && <CheckCircle2 className=\"h-5 w-5 text-text-primary shrink-0\" />}\n </div>\n }\n description={cart.category}\n >\n <PagePurpose className=\"mb-4\">\n Everything about this cart in one place — browse its menu, check ratings, location and\n opening hours, then add items to your order. When you're ready, head to checkout to confirm\n and pay.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"kadaikodi-cart-detail\"\n className=\"mb-4\"\n steps={[\n {\n id: 'menu',\n label: 'Browse the menu and add items',\n description: 'Tap items below to build your order.',\n },\n {\n id: 'reviews',\n label: 'Read what others say',\n description: 'Check ratings and reviews before you order.',\n onClick: () => navigateTo('/customer/reviews'),\n },\n {\n id: 'checkout',\n label: 'Proceed to checkout',\n description: 'Review your order, choose payment and confirm.',\n onClick: () => navigateTo('/customer/checkout'),\n },\n ]}\n />\n\n {/* Hero Image — the one emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"relative h-48 overflow-hidden p-0\">\n <ProviderImage src={cart.image} alt={cart.name} seed={cart.name} />\n <button\n type=\"button\"\n onClick={() => favorites.toggle(cart.id)}\n aria-pressed={favorites.has(cart.id)}\n aria-label={favorites.has(cart.id) ? 'Remove from favorites' : 'Save to favorites'}\n className=\"absolute top-3 right-3 flex h-9 w-9 items-center justify-center rounded-full bg-bg-surface/80 backdrop-blur-sm text-text-secondary hover:text-status-error-text transition-colors\"\n >\n <Heart\n className={cn(\n 'h-4.5 w-4.5',\n favorites.has(cart.id) && 'fill-status-error-text text-status-error-text'\n )}\n />\n </button>\n <div className=\"absolute bottom-0 inset-x-0 bg-gradient-to-t from-black/60 to-transparent p-4\">\n <div className=\"flex items-center gap-3\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n cart.isOpen\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-error-bg-subtle text-status-error-text'\n )}\n >\n {cart.isOpen ? 'Open Now' : 'Closed'}\n </span>\n <div className=\"flex items-center gap-1.5 text-white text-sm\">\n <RatingStars value={cart.rating} size={14} />\n <span className=\"font-medium\">{cart.rating}</span>\n <span className=\"text-white/70\">({cart.totalRatings} reviews)</span>\n </div>\n </div>\n </div>\n </GlassCard>\n\n {/* Cart Closed Warning */}\n {!cart.isOpen && (\n <div className=\"mt-4 rounded-lg border border-status-warning-text/30 bg-status-warning-bg-subtle p-3\">\n <p className=\"text-sm text-status-warning-text\">\n <strong>This cart is currently closed.</strong> You can browse the offerings but orders\n are not being accepted.\n </p>\n </div>\n )}\n\n {/* Menu / Reviews / Info tabs */}\n <div className=\"mt-5\">\n <SegmentedTabs\n aria-label=\"Cart sections\"\n variant=\"underline\"\n tabs={[\n { id: 'menu', label: 'Menu' },\n { id: 'reviews', label: 'Reviews', count: reviews.length || undefined },\n { id: 'info', label: 'Info' },\n ]}\n value={activeTab}\n onChange={(id) => setActiveTab(id as DetailTab)}\n />\n </div>\n\n {/* ── Info tab ─────────────────────────────────────────── */}\n {activeTab === 'info' && (\n <>\n <div className=\"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\">\n <div className=\"flex items-center gap-2 text-text-secondary text-xs mb-1\">\n <MapPin className=\"h-3.5 w-3.5\" />\n <span className=\"font-medium uppercase tracking-wider\">Location</span>\n </div>\n <p className=\"text-sm text-text-primary\">{cart.location.address}</p>\n {cart.distance != null && (\n <p className=\"text-xs text-text-secondary\">{formatDistance(cart.distance)} away</p>\n )}\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\">\n <div className=\"flex items-center gap-2 text-text-secondary text-xs mb-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n <span className=\"font-medium uppercase tracking-wider\">Estimated Time</span>\n </div>\n <p className=\"text-sm text-text-primary\">\n {cart.estimatedTime != null ? formatDuration(cart.estimatedTime) : '—'}\n </p>\n {cart.averageOrderValue && (\n <p className=\"text-xs text-text-secondary\">\n Avg {formatCurrency(cart.averageOrderValue, { minimumFractionDigits: 0 })}\n </p>\n )}\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\">\n <div className=\"flex items-center gap-2 text-text-secondary text-xs mb-1\">\n <Star className=\"h-3.5 w-3.5\" />\n <span className=\"font-medium uppercase tracking-wider\">Rating</span>\n </div>\n <p className=\"text-sm text-text-primary\">{cart.rating} / 5.0</p>\n <p className=\"text-xs text-text-secondary\">{cart.totalRatings} reviews</p>\n </div>\n </div>\n\n {/* Business Hours */}\n {cart.businessHours && Object.keys(cart.businessHours).length > 0 && (\n <section className=\"mt-6\">\n <h2 className=\"text-base font-semibold text-text-primary mb-2\">Operating Hours</h2>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {Object.entries(cart.businessHours).map(([day, hours]) => (\n <div\n key={day}\n className=\"flex items-center justify-between px-4 py-2 border-b border-border-subtle last:border-b-0\"\n >\n <span className=\"text-sm font-medium text-text-primary capitalize\">{day}</span>\n {hours.isOpen ? (\n <span className=\"text-sm text-text-secondary\">\n {hours.open} - {hours.close}\n </span>\n ) : (\n <span className=\"text-sm text-status-error-text\">Closed</span>\n )}\n </div>\n ))}\n </div>\n </section>\n )}\n </>\n )}\n\n {/* ── Reviews tab ──────────────────────────────────────── */}\n {activeTab === 'reviews' && (\n <div className=\"mt-4 space-y-4\">\n {/* Aggregate */}\n <div className=\"flex flex-wrap items-center gap-4 rounded-xl border border-border-subtle bg-bg-surface p-5\">\n <div className=\"text-center\">\n <p className=\"text-4xl font-bold leading-none text-text-primary\">\n {cart.rating.toFixed(1)}\n </p>\n <div className=\"mt-1.5 flex justify-center\">\n <RatingStars value={cart.rating} size={16} />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">{cart.totalRatings} ratings</p>\n </div>\n <div className=\"flex-1 min-w-[180px] space-y-1.5\">\n {[5, 4, 3, 2, 1].map((star) => {\n const count = reviews.filter((r) => Math.round(r.rating) === star).length;\n const pct = reviews.length ? (count / reviews.length) * 100 : 0;\n return (\n <div key={star} className=\"flex items-center gap-2 text-xs\">\n <span className=\"flex w-6 items-center gap-0.5 text-text-secondary\">\n {star}\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n </span>\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-accent-primary transition-all\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-6 text-right tabular-nums text-text-muted\">{count}</span>\n </div>\n );\n })}\n </div>\n </div>\n\n {/* Review list */}\n {reviews.length === 0 ? (\n <div className=\"rounded-xl border border-border-subtle bg-bg-surface p-8 text-center\">\n <MessageSquare className=\"mx-auto h-10 w-10 text-text-secondary/40\" />\n <p className=\"mt-3 text-sm text-text-secondary\">\n No written reviews yet. Order and be the first to review this cart.\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {reviews.map((review) => (\n <div\n key={review.id}\n className=\"rounded-xl border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <RatingStars value={review.rating} size={14} showValue />\n <span className=\"text-xs text-text-muted\">\n {formatRelativeTime(review.createdAt)}\n </span>\n </div>\n {review.review && (\n <p className=\"mt-2 text-sm text-text-primary leading-relaxed\">\n {review.review}\n </p>\n )}\n {review.response && (\n <div className=\"mt-3 rounded-lg border-l-2 border-accent-primary bg-[var(--color-accent-soft)] p-3\">\n <p className=\"mb-1 text-xs font-medium text-text-primary\">Owner response</p>\n <p className=\"text-xs text-text-primary leading-relaxed\">{review.response}</p>\n </div>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n\n {/* ── Menu tab: Menu + Sidebar ─────────────────────────── */}\n {activeTab === 'menu' && (\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Menu Section */}\n <div className=\"lg:col-span-2\">\n {/* Search and Filter */}\n <div className=\"flex flex-col gap-3 sm:flex-row mb-4\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search offerings...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-10 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <select\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Categories</option>\n {menuCategories.map((cat) => (\n <option key={cat} value={cat}>\n {cat}\n </option>\n ))}\n </select>\n </div>\n\n {/* Menu Items */}\n <h2 className=\"text-lg font-semibold text-text-primary mb-3\">\n Menu & Services ({filteredMenu.length})\n </h2>\n\n {filteredMenu.length === 0 ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-8 text-center\">\n <Search className=\"mx-auto h-10 w-10 text-text-secondary/50\" />\n <p className=\"mt-3 text-sm text-text-secondary\">No offerings found</p>\n </div>\n ) : (\n <div className=\"space-y-6\">\n {Object.entries(groupedMenu).map(([category, items]) => (\n <div key={category}>\n <h3 className=\"text-sm font-semibold text-text-primary mb-2 px-1\">{category}</h3>\n <div className=\"space-y-2\">\n {items.map((item) => {\n const qty = getItemQuantity(item.id);\n return (\n <div\n key={item.id}\n className=\"flex gap-4 rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"h-20 w-20 shrink-0 rounded-md bg-bg-sunken overflow-hidden\">\n <ProviderImage src={item.image} alt={item.name} seed={item.name} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-start justify-between gap-2\">\n <div>\n <div className=\"flex items-center gap-2\">\n <h4 className=\"text-sm font-semibold text-text-primary\">\n {item.name}\n </h4>\n {item.isVeg && (\n <Leaf className=\"h-3.5 w-3.5 text-status-success-text\" />\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {item.category}\n </p>\n </div>\n <span className=\"text-sm font-bold text-text-primary whitespace-nowrap\">\n {formatCurrency(item.price, { minimumFractionDigits: 0 })}\n </span>\n </div>\n <p className=\"mt-1 text-xs text-text-secondary line-clamp-2\">\n {item.description}\n </p>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-[10px] text-text-secondary\">\n {item.duration != null && item.duration > 0 && (\n <span className=\"flex items-center gap-0.5\">\n <Clock className=\"h-3 w-3\" />\n {formatDuration(item.duration)}\n </span>\n )}\n {item.serviceType && (\n <span className=\"rounded bg-bg-sunken px-1.5 py-0.5 capitalize\">\n {item.serviceType}\n </span>\n )}\n {item.spiceLevel && (\n <span className=\"flex items-center gap-0.5 rounded bg-bg-sunken px-1.5 py-0.5 capitalize\">\n <Flame className=\"h-3 w-3\" />\n {item.spiceLevel}\n </span>\n )}\n </div>\n {/* Add/Quantity Controls */}\n <div className=\"mt-2 flex justify-end\">\n {qty > 0 ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, -1)}\n className=\"flex h-7 w-7 items-center justify-center rounded-full bg-bg-sunken text-text-primary hover:bg-accent\"\n >\n <Minus className=\"h-3.5 w-3.5\" />\n </button>\n <span className=\"min-w-[2ch] text-center text-sm font-medium text-text-primary\">\n {qty}\n </span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, 1)}\n className=\"flex h-7 w-7 items-center justify-center rounded-full bg-foreground text-background hover:opacity-90\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => removeFromCart(item.id)}\n className=\"ml-1 flex h-7 w-7 items-center justify-center rounded-full text-status-error-text hover:bg-status-error-bg/10\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n disabled={!cart.isOpen}\n onClick={() => handleAddToCart(item)}\n className={cn(\n 'rounded-lg px-4 py-1.5 text-xs font-medium transition-opacity',\n cart.isOpen\n ? 'bg-foreground text-background hover:opacity-90'\n : 'bg-bg-sunken text-text-secondary cursor-not-allowed'\n )}\n >\n Add\n </button>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-4\">\n {/* Shopping Cart */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 lg:sticky lg:top-6\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <ShoppingCart className=\"h-4 w-4\" />\n Your Order\n </h3>\n {cartItems.length === 0 ? (\n <div className=\"py-6 text-center\">\n <ShoppingCart className=\"mx-auto h-8 w-8 text-text-secondary/40\" />\n <p className=\"mt-2 text-sm text-text-secondary\">Your cart is empty</p>\n </div>\n ) : (\n <>\n <div className=\"space-y-2 max-h-64 overflow-y-auto mb-3\">\n {cartItems.map((item) => (\n <div key={item.id} className=\"flex items-center justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {item.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatCurrency(item.price, { minimumFractionDigits: 0 })} x{' '}\n {item.quantity}\n </p>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.productId, -1)}\n className=\"flex h-6 w-6 items-center justify-center rounded bg-bg-sunken text-text-primary text-xs\"\n >\n -\n </button>\n <span className=\"min-w-[2ch] text-center text-xs font-medium\">\n {item.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.productId, 1)}\n className=\"flex h-6 w-6 items-center justify-center rounded bg-bg-sunken text-text-primary text-xs\"\n >\n +\n </button>\n <button\n type=\"button\"\n onClick={() => removeFromCart(item.productId)}\n className=\"ml-1 text-status-error-text hover:text-status-error-text/80\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n <div className=\"border-t border-border-subtle pt-3\">\n <div className=\"flex justify-between items-center mb-3\">\n <span className=\"text-sm font-semibold text-text-primary\">\n Total: {formatCurrency(cartTotal, { minimumFractionDigits: 0 })}\n </span>\n <span className=\"text-xs text-text-secondary\">{cartCount} items</span>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/checkout')}\n className=\"w-full rounded-lg bg-foreground py-2.5 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Proceed to Checkout\n </button>\n </div>\n </>\n )}\n </div>\n\n {/* Direct Service Card */}\n {cart.serviceTypes?.includes('direct_service') && (\n <div className=\"rounded-lg border border-accent-purple/30 bg-accent-purple-subtle p-4\">\n <h3 className=\"text-sm font-semibold text-text-primary flex items-center gap-2 mb-2\">\n <Home className=\"h-4 w-4 text-text-primary\" />\n Direct Service\n </h3>\n <p className=\"text-xs text-text-secondary mb-3\">Cart comes to your location!</p>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/cart/${cart.id}/direct-service`)}\n className=\"w-full rounded-lg bg-accent-purple py-2 text-xs font-medium text-white hover:bg-accent-purple transition-colors\"\n >\n Book Direct Service\n </button>\n </div>\n )}\n\n {/* Scheduled Stops */}\n {cart.serviceTypes?.includes('appointment') && (\n <div className=\"rounded-lg border border-border-focus/30 bg-[var(--color-accent-soft)] p-4\">\n <h3 className=\"text-sm font-semibold text-text-primary flex items-center gap-2 mb-2\">\n <Calendar className=\"h-4 w-4 text-text-primary\" />\n Appointments Available\n </h3>\n <p className=\"text-xs text-text-secondary\">\n This service provider accepts appointments. Contact them to schedule.\n </p>\n </div>\n )}\n\n {/* Investment Opportunity */}\n {investment && (\n <div className=\"rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle p-4\">\n <h3 className=\"text-sm font-semibold text-text-primary flex items-center gap-2 mb-2\">\n <TrendingUp className=\"h-4 w-4 text-status-success-text\" />\n Investment Opportunity\n </h3>\n <p className=\"text-xs text-text-secondary mb-2\">{investment.description}</p>\n <div className=\"space-y-1 text-xs text-text-secondary mb-3\">\n <p>\n Target: {formatCurrency(investment.targetAmount, { minimumFractionDigits: 0 })}\n </p>\n <p>\n Raised: {formatCurrency(investment.raisedAmount, { minimumFractionDigits: 0 })}\n </p>\n <p>Expected Returns: {investment.expectedReturns}%</p>\n <p>\n Min Investment:{' '}\n {formatCurrency(investment.minimumInvestment, { minimumFractionDigits: 0 })}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/investor/opportunity/${investment.id}`)}\n className=\"w-full rounded-lg bg-status-success-text py-2 text-xs font-medium text-white hover:bg-status-success-text transition-colors\"\n >\n Fund This Business\n </button>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Sticky mobile cart bar (mockup: \"View Cart · N items · ₹X\") */}\n {activeTab === 'menu' && cartCount > 0 && (\n <>\n <div className=\"h-20 lg:hidden\" aria-hidden=\"true\" />\n <div className=\"fixed inset-x-0 bottom-0 z-40 border-t border-border-subtle bg-bg-surface/95 p-3 backdrop-blur-sm shadow-elevation-2 lg:hidden\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/checkout')}\n className=\"flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg py-3 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n <ShoppingCart className=\"h-4 w-4\" />\n View Cart · {cartCount} {cartCount === 1 ? 'item' : 'items'} ·{' '}\n {formatCurrency(cartTotal, { minimumFractionDigits: 0 })}\n </button>\n </div>\n </>\n )}\n\n {/* Customization Modal */}\n {showCustomization && customization && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"w-full max-w-md max-h-[90vh] overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"p-5\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {customization.product.name}\n </h3>\n <button\n type=\"button\"\n onClick={() => {\n setShowCustomization(false);\n setCustomization(null);\n }}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </div>\n\n <p className=\"text-sm text-text-secondary mb-4\">\n {customization.product.description}\n </p>\n <p className=\"text-lg font-bold text-text-primary mb-4\">\n {formatCurrency(customization.product.price, { minimumFractionDigits: 0 })}\n </p>\n\n {/* Quantity */}\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-text-primary mb-2\">Quantity</label>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() =>\n setCustomization((prev) =>\n prev ? { ...prev, quantity: Math.max(1, prev.quantity - 1) } : prev\n )\n }\n className=\"flex h-8 w-8 items-center justify-center rounded-full bg-bg-sunken text-text-primary\"\n >\n <Minus className=\"h-4 w-4\" />\n </button>\n <span className=\"min-w-[2ch] text-center font-medium text-text-primary\">\n {customization.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() =>\n setCustomization((prev) =>\n prev ? { ...prev, quantity: prev.quantity + 1 } : prev\n )\n }\n className=\"flex h-8 w-8 items-center justify-center rounded-full bg-bg-sunken text-text-primary\"\n >\n <Plus className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n\n {/* Special Instructions */}\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Special Instructions\n </label>\n <textarea\n value={customization.specialInstructions}\n onChange={(e) =>\n setCustomization((prev) =>\n prev ? { ...prev, specialInstructions: e.target.value } : prev\n )\n }\n placeholder=\"Any special requests...\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface p-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n rows={3}\n />\n </div>\n\n <button\n type=\"button\"\n onClick={confirmAddToCart}\n className=\"w-full rounded-lg bg-foreground py-2.5 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Add to Cart -{' '}\n {formatCurrency(customization.product.price * customization.quantity, {\n minimumFractionDigits: 0,\n })}\n </button>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CartDetailPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/customer/CartDetailPage.tsx"],"sourcesContent":["/**\n * Cart Detail Page\n * @module kadaikodi/pages/customer\n *\n * Full detail view with shopping cart sidebar (Your Order with +/- controls,\n * remove, total, Proceed to Checkout), product customization modal,\n * category-grouped product listing, search, category filter,\n * investment opportunity card, scheduled stops, direct service card,\n * veg/vegan icons, availability badges, calories, original price\n * strikethrough, and cart closed warning banner.\n */\n\nimport { useState, useMemo, useEffect } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Star,\n MapPin,\n Clock,\n CheckCircle2,\n ShoppingCart,\n Search,\n Leaf,\n Minus,\n Plus,\n X,\n TrendingUp,\n Home,\n Calendar,\n Flame,\n Heart,\n MessageSquare,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { RatingStars, SegmentedTabs } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useProviders,\n useOfferings,\n useInvestmentOpportunities,\n useReviews,\n} from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport {\n formatCurrency,\n formatDistance,\n formatDuration,\n formatRelativeTime,\n} from '../../utils/formatters';\nimport type { CartMenuItem, InvestmentOpportunity } from '../../types';\nimport { ProviderImage, useFavorites } from './shared';\n\ntype DetailTab = 'menu' | 'reviews' | 'info';\n\ninterface CartItem {\n id: string;\n productId: string;\n name: string;\n price: number;\n quantity: number;\n specialInstructions?: string;\n}\n\ninterface CustomizationState {\n product: CartMenuItem;\n quantity: number;\n specialInstructions: string;\n}\n\nconst DAY_LABELS: Record<string, string> = {\n monday: 'Mon',\n tuesday: 'Tue',\n wednesday: 'Wed',\n thursday: 'Thu',\n friday: 'Fri',\n saturday: 'Sat',\n sunday: 'Sun',\n};\n\nexport function CartDetailPage() {\n const { cartId } = useParams<{ cartId: string }>();\n const { navigateTo } = useKadaikodi();\n\n const { data: providers = [], isLoading: isLoadingProviders } = useProviders();\n const { data: offerings = [], isLoading: isLoadingOfferings } = useOfferings(cartId);\n const { data: investments = [], isLoading: isLoadingInvestments } = useInvestmentOpportunities();\n\n const { data: reviews = [] } = useReviews(cartId ?? '');\n const favorites = useFavorites();\n\n const cart = useMemo(() => providers.find((p) => p.id === cartId), [providers, cartId]);\n const investment = useMemo(\n () => investments.find((i) => i.cartId === cartId),\n [investments, cartId]\n );\n\n const [activeTab, setActiveTab] = useState<DetailTab>('menu');\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedCategory, setSelectedCategory] = useState('');\n const [cartItems, setCartItems] = useState<CartItem[]>([]);\n const [showCustomization, setShowCustomization] = useState(false);\n const [customization, setCustomization] = useState<CustomizationState | null>(null);\n\n const menuItems = useMemo(\n () =>\n offerings.map((o) => ({\n id: o.id,\n name: o.name,\n price: o.price,\n description: o.description ?? '',\n image: o.media?.[0] ?? '',\n category: o.category,\n isVeg: true,\n duration: o.durationMinutes ?? undefined,\n serviceType: (o.serviceType ?? undefined) as CartMenuItem['serviceType'],\n })),\n [offerings]\n );\n\n const menuCategories = useMemo(() => {\n const cats = [...new Set(menuItems.map((item) => item.category))];\n return cats;\n }, [menuItems]);\n\n const filteredMenu = useMemo(() => {\n let items = menuItems;\n\n if (selectedCategory) {\n items = items.filter((item) => item.category === selectedCategory);\n }\n\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n items = items.filter(\n (item) => item.name.toLowerCase().includes(q) || item.description.toLowerCase().includes(q)\n );\n }\n\n return items;\n }, [menuItems, selectedCategory, searchQuery]);\n\n const isLoading = isLoadingProviders || isLoadingOfferings || isLoadingInvestments;\n\n const groupedMenu = useMemo(() => {\n const groups: Record<string, CartMenuItem[]> = {};\n filteredMenu.forEach((item) => {\n if (!groups[item.category]) groups[item.category] = [];\n groups[item.category].push(item);\n });\n return groups;\n }, [filteredMenu]);\n\n // Cart operations\n const addToCart = (product: CartMenuItem, qty: number = 1, instructions?: string) => {\n setCartItems((prev) => {\n const existing = prev.find((item) => item.productId === product.id);\n if (existing) {\n return prev.map((item) =>\n item.productId === product.id ? { ...item, quantity: item.quantity + qty } : item\n );\n }\n return [\n ...prev,\n {\n id: `cart-${Date.now()}`,\n productId: product.id,\n name: product.name,\n price: product.price,\n quantity: qty,\n specialInstructions: instructions,\n },\n ];\n });\n };\n\n const updateQuantity = (productId: string, delta: number) => {\n setCartItems((prev) =>\n prev\n .map((item) =>\n item.productId === productId\n ? { ...item, quantity: Math.max(0, item.quantity + delta) }\n : item\n )\n .filter((item) => item.quantity > 0)\n );\n };\n\n const removeFromCart = (productId: string) => {\n setCartItems((prev) => prev.filter((item) => item.productId !== productId));\n };\n\n const getItemQuantity = (productId: string) => {\n return cartItems.find((item) => item.productId === productId)?.quantity ?? 0;\n };\n\n const cartTotal = cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);\n const cartCount = cartItems.reduce((sum, item) => sum + item.quantity, 0);\n\n const handleAddToCart = (product: CartMenuItem) => {\n setCustomization({ product, quantity: 1, specialInstructions: '' });\n setShowCustomization(true);\n };\n\n const confirmAddToCart = () => {\n if (!customization) return;\n addToCart(customization.product, customization.quantity, customization.specialInstructions);\n setShowCustomization(false);\n setCustomization(null);\n alert(`${customization.product.name} added to cart`);\n };\n\n if (!cart) {\n return (\n <PageLayout title=\"Cart Not Found\">\n <EmptyState\n icon={<ShoppingCart className=\"h-12 w-12\" />}\n title=\"Cart not found\"\n description={`No cart found with ID \"${cartId}\".`}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/discover')}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Browse Carts\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/discover')}\n className=\"rounded-md p-1 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n <span className=\"truncate\">{cart.name}</span>\n {cart.isVerified && <CheckCircle2 className=\"h-5 w-5 text-text-primary shrink-0\" />}\n </div>\n }\n description={cart.category}\n >\n <PagePurpose className=\"mb-4\">\n Everything about this cart in one place — browse its menu, check ratings, location and\n opening hours, then add items to your order. When you're ready, head to checkout to confirm\n and pay.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"kadaikodi-cart-detail\"\n className=\"mb-4\"\n steps={[\n {\n id: 'menu',\n label: 'Browse the menu and add items',\n description: 'Tap items below to build your order.',\n },\n {\n id: 'reviews',\n label: 'Read what others say',\n description: 'Check ratings and reviews before you order.',\n onClick: () => navigateTo('/customer/reviews'),\n },\n {\n id: 'checkout',\n label: 'Proceed to checkout',\n description: 'Review your order, choose payment and confirm.',\n onClick: () => navigateTo('/customer/checkout'),\n },\n ]}\n />\n\n {/* Hero Image — the one emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"relative h-48 overflow-hidden p-0\">\n <ProviderImage src={cart.image} alt={cart.name} seed={cart.name} />\n <button\n type=\"button\"\n onClick={() => favorites.toggle(cart.id)}\n aria-pressed={favorites.has(cart.id)}\n aria-label={favorites.has(cart.id) ? 'Remove from favorites' : 'Save to favorites'}\n className=\"absolute top-3 right-3 flex h-9 w-9 items-center justify-center rounded-full bg-bg-surface/80 backdrop-blur-sm text-text-secondary hover:text-status-error-text transition-colors\"\n >\n <Heart\n className={cn(\n 'h-4.5 w-4.5',\n favorites.has(cart.id) && 'fill-status-error-text text-status-error-text'\n )}\n />\n </button>\n <div className=\"absolute bottom-0 inset-x-0 bg-gradient-to-t from-black/60 to-transparent p-4\">\n <div className=\"flex items-center gap-3\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n cart.isOpen\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-error-bg-subtle text-status-error-text'\n )}\n >\n {cart.isOpen ? 'Open Now' : 'Closed'}\n </span>\n <div className=\"flex items-center gap-1.5 text-white text-sm\">\n <RatingStars value={cart.rating} size={14} />\n <span className=\"font-medium\">{cart.rating}</span>\n <span className=\"text-white/70\">({cart.totalRatings} reviews)</span>\n </div>\n </div>\n </div>\n </GlassCard>\n\n {/* Cart Closed Warning */}\n {!cart.isOpen && (\n <div className=\"mt-4 rounded-lg border border-status-warning-text/30 bg-status-warning-bg-subtle p-3\">\n <p className=\"text-sm text-status-warning-text\">\n <strong>This cart is currently closed.</strong> You can browse the offerings but orders\n are not being accepted.\n </p>\n </div>\n )}\n\n {/* Menu / Reviews / Info tabs */}\n <div className=\"mt-5\">\n <SegmentedTabs\n aria-label=\"Cart sections\"\n variant=\"underline\"\n tabs={[\n { id: 'menu', label: 'Menu' },\n { id: 'reviews', label: 'Reviews', count: reviews.length || undefined },\n { id: 'info', label: 'Info' },\n ]}\n value={activeTab}\n onChange={(id) => setActiveTab(id as DetailTab)}\n />\n </div>\n\n {/* ── Info tab ─────────────────────────────────────────── */}\n {activeTab === 'info' && (\n <>\n <div className=\"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\">\n <div className=\"flex items-center gap-2 text-text-secondary text-xs mb-1\">\n <MapPin className=\"h-3.5 w-3.5\" />\n <span className=\"font-medium uppercase tracking-wider\">Location</span>\n </div>\n <p className=\"text-sm text-text-primary\">{cart.location.address}</p>\n {cart.distance != null && (\n <p className=\"text-xs text-text-secondary\">{formatDistance(cart.distance)} away</p>\n )}\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\">\n <div className=\"flex items-center gap-2 text-text-secondary text-xs mb-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n <span className=\"font-medium uppercase tracking-wider\">Estimated Time</span>\n </div>\n <p className=\"text-sm text-text-primary\">\n {cart.estimatedTime != null ? formatDuration(cart.estimatedTime) : '—'}\n </p>\n {cart.averageOrderValue && (\n <p className=\"text-xs text-text-secondary\">\n Avg {formatCurrency(cart.averageOrderValue, { minimumFractionDigits: 0 })}\n </p>\n )}\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\">\n <div className=\"flex items-center gap-2 text-text-secondary text-xs mb-1\">\n <Star className=\"h-3.5 w-3.5\" />\n <span className=\"font-medium uppercase tracking-wider\">Rating</span>\n </div>\n <p className=\"text-sm text-text-primary\">{cart.rating} / 5.0</p>\n <p className=\"text-xs text-text-secondary\">{cart.totalRatings} reviews</p>\n </div>\n </div>\n\n {/* Business Hours */}\n {cart.businessHours && Object.keys(cart.businessHours).length > 0 && (\n <section className=\"mt-6\">\n <h2 className=\"text-base font-semibold text-text-primary mb-2\">Operating Hours</h2>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {Object.entries(cart.businessHours).map(([day, hours]) => (\n <div\n key={day}\n className=\"flex items-center justify-between px-4 py-2 border-b border-border-subtle last:border-b-0\"\n >\n <span className=\"text-sm font-medium text-text-primary capitalize\">{day}</span>\n {hours.isOpen ? (\n <span className=\"text-sm text-text-secondary\">\n {hours.open} - {hours.close}\n </span>\n ) : (\n <span className=\"text-sm text-status-error-text\">Closed</span>\n )}\n </div>\n ))}\n </div>\n </section>\n )}\n </>\n )}\n\n {/* ── Reviews tab ──────────────────────────────────────── */}\n {activeTab === 'reviews' && (\n <div className=\"mt-4 space-y-4\">\n {/* Aggregate */}\n <div className=\"flex flex-wrap items-center gap-4 rounded-xl border border-border-subtle bg-bg-surface p-5\">\n <div className=\"text-center\">\n <p className=\"text-4xl font-bold leading-none text-text-primary\">\n {cart.rating.toFixed(1)}\n </p>\n <div className=\"mt-1.5 flex justify-center\">\n <RatingStars value={cart.rating} size={16} />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">{cart.totalRatings} ratings</p>\n </div>\n <div className=\"flex-1 min-w-[180px] space-y-1.5\">\n {[5, 4, 3, 2, 1].map((star) => {\n const count = reviews.filter((r) => Math.round(r.rating) === star).length;\n const pct = reviews.length ? (count / reviews.length) * 100 : 0;\n return (\n <div key={star} className=\"flex items-center gap-2 text-xs\">\n <span className=\"flex w-6 items-center gap-0.5 text-text-secondary\">\n {star}\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n </span>\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-accent-primary transition-all\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-6 text-right tabular-nums text-text-muted\">{count}</span>\n </div>\n );\n })}\n </div>\n </div>\n\n {/* Review list */}\n {reviews.length === 0 ? (\n <div className=\"rounded-xl border border-border-subtle bg-bg-surface p-8 text-center\">\n <MessageSquare className=\"mx-auto h-10 w-10 text-text-secondary/40\" />\n <p className=\"mt-3 text-sm text-text-secondary\">\n No written reviews yet. Order and be the first to review this cart.\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {reviews.map((review) => (\n <div\n key={review.id}\n className=\"rounded-xl border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <RatingStars value={review.rating} size={14} showValue />\n <span className=\"text-xs text-text-muted\">\n {formatRelativeTime(review.createdAt)}\n </span>\n </div>\n {review.review && (\n <p className=\"mt-2 text-sm text-text-primary leading-relaxed\">\n {review.review}\n </p>\n )}\n {review.response && (\n <div className=\"mt-3 rounded-lg border-l-2 border-accent-primary bg-[var(--color-accent-soft)] p-3\">\n <p className=\"mb-1 text-xs font-medium text-text-primary\">Owner response</p>\n <p className=\"text-xs text-text-primary leading-relaxed\">{review.response}</p>\n </div>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n\n {/* ── Menu tab: Menu + Sidebar ─────────────────────────── */}\n {activeTab === 'menu' && (\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Menu Section */}\n <div className=\"lg:col-span-2\">\n {/* Search and Filter */}\n <div className=\"flex flex-col gap-3 sm:flex-row mb-4\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search offerings...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-10 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <select\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Categories</option>\n {menuCategories.map((cat) => (\n <option key={cat} value={cat}>\n {cat}\n </option>\n ))}\n </select>\n </div>\n\n {/* Menu Items */}\n <h2 className=\"text-lg font-semibold text-text-primary mb-3\">\n Menu & Services ({filteredMenu.length})\n </h2>\n\n {filteredMenu.length === 0 ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-8 text-center\">\n <Search className=\"mx-auto h-10 w-10 text-text-secondary/50\" />\n <p className=\"mt-3 text-sm text-text-secondary\">No offerings found</p>\n </div>\n ) : (\n <div className=\"space-y-6\">\n {Object.entries(groupedMenu).map(([category, items]) => (\n <div key={category}>\n <h3 className=\"text-sm font-semibold text-text-primary mb-2 px-1\">\n {category}\n </h3>\n <div className=\"space-y-2\">\n {items.map((item) => {\n const qty = getItemQuantity(item.id);\n return (\n <div\n key={item.id}\n className=\"flex gap-4 rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"h-20 w-20 shrink-0 rounded-md bg-bg-sunken overflow-hidden\">\n <ProviderImage src={item.image} alt={item.name} seed={item.name} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-start justify-between gap-2\">\n <div>\n <div className=\"flex items-center gap-2\">\n <h4 className=\"text-sm font-semibold text-text-primary\">\n {item.name}\n </h4>\n {item.isVeg && (\n <Leaf className=\"h-3.5 w-3.5 text-status-success-text\" />\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {item.category}\n </p>\n </div>\n <span className=\"text-sm font-bold text-text-primary whitespace-nowrap\">\n {formatCurrency(item.price, { minimumFractionDigits: 0 })}\n </span>\n </div>\n <p className=\"mt-1 text-xs text-text-secondary line-clamp-2\">\n {item.description}\n </p>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-[10px] text-text-secondary\">\n {item.duration != null && item.duration > 0 && (\n <span className=\"flex items-center gap-0.5\">\n <Clock className=\"h-3 w-3\" />\n {formatDuration(item.duration)}\n </span>\n )}\n {item.serviceType && (\n <span className=\"rounded bg-bg-sunken px-1.5 py-0.5 capitalize\">\n {item.serviceType}\n </span>\n )}\n {item.spiceLevel && (\n <span className=\"flex items-center gap-0.5 rounded bg-bg-sunken px-1.5 py-0.5 capitalize\">\n <Flame className=\"h-3 w-3\" />\n {item.spiceLevel}\n </span>\n )}\n </div>\n {/* Add/Quantity Controls */}\n <div className=\"mt-2 flex justify-end\">\n {qty > 0 ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, -1)}\n className=\"flex h-7 w-7 items-center justify-center rounded-full bg-bg-sunken text-text-primary hover:bg-accent\"\n >\n <Minus className=\"h-3.5 w-3.5\" />\n </button>\n <span className=\"min-w-[2ch] text-center text-sm font-medium text-text-primary\">\n {qty}\n </span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, 1)}\n className=\"flex h-7 w-7 items-center justify-center rounded-full bg-foreground text-background hover:opacity-90\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => removeFromCart(item.id)}\n className=\"ml-1 flex h-7 w-7 items-center justify-center rounded-full text-status-error-text hover:bg-status-error-bg/10\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n disabled={!cart.isOpen}\n onClick={() => handleAddToCart(item)}\n className={cn(\n 'rounded-lg px-4 py-1.5 text-xs font-medium transition-opacity',\n cart.isOpen\n ? 'bg-foreground text-background hover:opacity-90'\n : 'bg-bg-sunken text-text-secondary cursor-not-allowed'\n )}\n >\n Add\n </button>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-4\">\n {/* Shopping Cart */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 lg:sticky lg:top-6\">\n <h3 className=\"text-base font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <ShoppingCart className=\"h-4 w-4\" />\n Your Order\n </h3>\n {cartItems.length === 0 ? (\n <div className=\"py-6 text-center\">\n <ShoppingCart className=\"mx-auto h-8 w-8 text-text-secondary/40\" />\n <p className=\"mt-2 text-sm text-text-secondary\">Your cart is empty</p>\n </div>\n ) : (\n <>\n <div className=\"space-y-2 max-h-64 overflow-y-auto mb-3\">\n {cartItems.map((item) => (\n <div key={item.id} className=\"flex items-center justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {item.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatCurrency(item.price, { minimumFractionDigits: 0 })} x{' '}\n {item.quantity}\n </p>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.productId, -1)}\n className=\"flex h-6 w-6 items-center justify-center rounded bg-bg-sunken text-text-primary text-xs\"\n >\n -\n </button>\n <span className=\"min-w-[2ch] text-center text-xs font-medium\">\n {item.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.productId, 1)}\n className=\"flex h-6 w-6 items-center justify-center rounded bg-bg-sunken text-text-primary text-xs\"\n >\n +\n </button>\n <button\n type=\"button\"\n onClick={() => removeFromCart(item.productId)}\n className=\"ml-1 text-status-error-text hover:text-status-error-text/80\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n <div className=\"border-t border-border-subtle pt-3\">\n <div className=\"flex justify-between items-center mb-3\">\n <span className=\"text-sm font-semibold text-text-primary\">\n Total: {formatCurrency(cartTotal, { minimumFractionDigits: 0 })}\n </span>\n <span className=\"text-xs text-text-secondary\">{cartCount} items</span>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/checkout')}\n className=\"w-full rounded-lg bg-foreground py-2.5 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Proceed to Checkout\n </button>\n </div>\n </>\n )}\n </div>\n\n {/* Direct Service Card */}\n {cart.serviceTypes?.includes('direct_service') && (\n <div className=\"rounded-lg border border-accent-purple/30 bg-accent-purple-subtle p-4\">\n <h3 className=\"text-sm font-semibold text-text-primary flex items-center gap-2 mb-2\">\n <Home className=\"h-4 w-4 text-text-primary\" />\n Direct Service\n </h3>\n <p className=\"text-xs text-text-secondary mb-3\">Cart comes to your location!</p>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/cart/${cart.id}/direct-service`)}\n className=\"w-full rounded-lg bg-accent-purple py-2 text-xs font-medium text-white hover:bg-accent-purple transition-colors\"\n >\n Book Direct Service\n </button>\n </div>\n )}\n\n {/* Scheduled Stops */}\n {cart.serviceTypes?.includes('appointment') && (\n <div className=\"rounded-lg border border-border-focus/30 bg-[var(--color-accent-soft)] p-4\">\n <h3 className=\"text-sm font-semibold text-text-primary flex items-center gap-2 mb-2\">\n <Calendar className=\"h-4 w-4 text-text-primary\" />\n Appointments Available\n </h3>\n <p className=\"text-xs text-text-secondary\">\n This service provider accepts appointments. Contact them to schedule.\n </p>\n </div>\n )}\n\n {/* Investment Opportunity */}\n {investment && (\n <div className=\"rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle p-4\">\n <h3 className=\"text-sm font-semibold text-text-primary flex items-center gap-2 mb-2\">\n <TrendingUp className=\"h-4 w-4 text-status-success-text\" />\n Investment Opportunity\n </h3>\n <p className=\"text-xs text-text-secondary mb-2\">{investment.description}</p>\n <div className=\"space-y-1 text-xs text-text-secondary mb-3\">\n <p>\n Target: {formatCurrency(investment.targetAmount, { minimumFractionDigits: 0 })}\n </p>\n <p>\n Raised: {formatCurrency(investment.raisedAmount, { minimumFractionDigits: 0 })}\n </p>\n <p>Expected Returns: {investment.expectedReturns}%</p>\n <p>\n Min Investment:{' '}\n {formatCurrency(investment.minimumInvestment, { minimumFractionDigits: 0 })}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/investor/opportunity/${investment.id}`)}\n className=\"w-full rounded-lg bg-status-success-text py-2 text-xs font-medium text-white hover:bg-status-success-text transition-colors\"\n >\n Fund This Business\n </button>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Sticky mobile cart bar (mockup: \"View Cart · N items · ₹X\") */}\n {activeTab === 'menu' && cartCount > 0 && (\n <>\n <div className=\"h-20 lg:hidden\" aria-hidden=\"true\" />\n <div className=\"fixed inset-x-0 bottom-0 z-40 border-t border-border-subtle bg-bg-surface/95 p-3 backdrop-blur-sm shadow-elevation-2 lg:hidden\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/customer/checkout')}\n className=\"flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg py-3 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n <ShoppingCart className=\"h-4 w-4\" />\n View Cart · {cartCount} {cartCount === 1 ? 'item' : 'items'} ·{' '}\n {formatCurrency(cartTotal, { minimumFractionDigits: 0 })}\n </button>\n </div>\n </>\n )}\n\n {/* Customization Modal */}\n {showCustomization && customization && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"w-full max-w-md max-h-[90vh] overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"p-5\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {customization.product.name}\n </h3>\n <button\n type=\"button\"\n onClick={() => {\n setShowCustomization(false);\n setCustomization(null);\n }}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </div>\n\n <p className=\"text-sm text-text-secondary mb-4\">\n {customization.product.description}\n </p>\n <p className=\"text-lg font-bold text-text-primary mb-4\">\n {formatCurrency(customization.product.price, { minimumFractionDigits: 0 })}\n </p>\n\n {/* Quantity */}\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-text-primary mb-2\">Quantity</label>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() =>\n setCustomization((prev) =>\n prev ? { ...prev, quantity: Math.max(1, prev.quantity - 1) } : prev\n )\n }\n className=\"flex h-8 w-8 items-center justify-center rounded-full bg-bg-sunken text-text-primary\"\n >\n <Minus className=\"h-4 w-4\" />\n </button>\n <span className=\"min-w-[2ch] text-center font-medium text-text-primary\">\n {customization.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() =>\n setCustomization((prev) =>\n prev ? { ...prev, quantity: prev.quantity + 1 } : prev\n )\n }\n className=\"flex h-8 w-8 items-center justify-center rounded-full bg-bg-sunken text-text-primary\"\n >\n <Plus className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n\n {/* Special Instructions */}\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Special Instructions\n </label>\n <textarea\n value={customization.specialInstructions}\n onChange={(e) =>\n setCustomization((prev) =>\n prev ? { ...prev, specialInstructions: e.target.value } : prev\n )\n }\n placeholder=\"Any special requests...\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface p-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n rows={3}\n />\n </div>\n\n <button\n type=\"button\"\n onClick={confirmAddToCart}\n className=\"w-full rounded-lg bg-foreground py-2.5 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Add to Cart -{' '}\n {formatCurrency(customization.product.price * customization.quantity, {\n minimumFractionDigits: 0,\n })}\n </button>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|