@burdenoff/microfe-adaptercloud 2026.625.1 → 2026.625.2
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/adaptercloud/components/PageLayout.js +24 -17
- package/dist/adaptercloud/components/PageLayout.js.map +1 -1
- package/dist/adaptercloud/pages/HomePage.js +18 -16
- package/dist/adaptercloud/pages/HomePage.js.map +1 -1
- package/dist/adaptercloud/pages/admin/AdminAnalyticsPage.js +90 -82
- package/dist/adaptercloud/pages/admin/AdminAnalyticsPage.js.map +1 -1
- package/dist/adaptercloud/pages/admin/AdminDashboard.js +70 -44
- package/dist/adaptercloud/pages/admin/AdminDashboard.js.map +1 -1
- package/dist/adaptercloud/pages/admin/AdminFinancialPage.js +60 -55
- package/dist/adaptercloud/pages/admin/AdminFinancialPage.js.map +1 -1
- package/dist/adaptercloud/pages/admin/AdminOrdersPage.js +97 -92
- package/dist/adaptercloud/pages/admin/AdminOrdersPage.js.map +1 -1
- package/dist/adaptercloud/pages/admin/AdminProfilePage.js +59 -57
- package/dist/adaptercloud/pages/admin/AdminProfilePage.js.map +1 -1
- package/dist/adaptercloud/pages/admin/AdminSettingsPage.js +118 -104
- package/dist/adaptercloud/pages/admin/AdminSettingsPage.js.map +1 -1
- package/dist/adaptercloud/pages/admin/CartManagementPage.js +145 -140
- package/dist/adaptercloud/pages/admin/CartManagementPage.js.map +1 -1
- package/dist/adaptercloud/pages/admin/UserManagementPage.js +152 -147
- package/dist/adaptercloud/pages/admin/UserManagementPage.js.map +1 -1
- package/dist/adaptercloud/pages/cart-owner/AnalyticsDashboard.js +87 -81
- package/dist/adaptercloud/pages/cart-owner/AnalyticsDashboard.js.map +1 -1
- package/dist/adaptercloud/pages/cart-owner/BusinessSettingsPage.js +179 -165
- package/dist/adaptercloud/pages/cart-owner/BusinessSettingsPage.js.map +1 -1
- package/dist/adaptercloud/pages/cart-owner/CartOwnerDashboard.js +73 -48
- package/dist/adaptercloud/pages/cart-owner/CartOwnerDashboard.js.map +1 -1
- package/dist/adaptercloud/pages/cart-owner/CartOwnerProfilePage.js +95 -93
- package/dist/adaptercloud/pages/cart-owner/CartOwnerProfilePage.js.map +1 -1
- package/dist/adaptercloud/pages/cart-owner/CartServiceConfigPage.js +110 -107
- package/dist/adaptercloud/pages/cart-owner/CartServiceConfigPage.js.map +1 -1
- package/dist/adaptercloud/pages/cart-owner/InventoryManagementPage.js +293 -299
- package/dist/adaptercloud/pages/cart-owner/InventoryManagementPage.js.map +1 -1
- package/dist/adaptercloud/pages/cart-owner/OrderManagementPage.js +218 -224
- package/dist/adaptercloud/pages/cart-owner/OrderManagementPage.js.map +1 -1
- package/dist/adaptercloud/pages/cart-owner/StaffManagementPage.js +122 -128
- package/dist/adaptercloud/pages/cart-owner/StaffManagementPage.js.map +1 -1
- package/dist/adaptercloud/pages/common/HelpSupportPage.js +248 -244
- package/dist/adaptercloud/pages/common/HelpSupportPage.js.map +1 -1
- package/dist/adaptercloud/pages/common/NotificationCenterPage.js +96 -91
- package/dist/adaptercloud/pages/common/NotificationCenterPage.js.map +1 -1
- package/dist/adaptercloud/pages/common/SettingsPage.js +754 -736
- package/dist/adaptercloud/pages/common/SettingsPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/BookingDetailsPage.js +63 -61
- package/dist/adaptercloud/pages/customer/BookingDetailsPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/CartDetailPage.js +110 -108
- package/dist/adaptercloud/pages/customer/CartDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/CartDiscoveryPage.js +221 -217
- package/dist/adaptercloud/pages/customer/CartDiscoveryPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/CheckoutPage.js +89 -87
- package/dist/adaptercloud/pages/customer/CheckoutPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/CustomerDashboard.js +410 -381
- package/dist/adaptercloud/pages/customer/CustomerDashboard.js.map +1 -1
- package/dist/adaptercloud/pages/customer/CustomerProfilePage.js +120 -118
- package/dist/adaptercloud/pages/customer/CustomerProfilePage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/DirectServiceBookingPage.js +86 -81
- package/dist/adaptercloud/pages/customer/DirectServiceBookingPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/FavoritesPage.js +53 -51
- package/dist/adaptercloud/pages/customer/FavoritesPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/OrderHistoryPage.js +114 -109
- package/dist/adaptercloud/pages/customer/OrderHistoryPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/OrderTrackingPage.js +60 -58
- package/dist/adaptercloud/pages/customer/OrderTrackingPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/PaymentMethodsPage.js +87 -72
- package/dist/adaptercloud/pages/customer/PaymentMethodsPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/ReviewsPage.js +65 -63
- package/dist/adaptercloud/pages/customer/ReviewsPage.js.map +1 -1
- package/dist/adaptercloud/pages/customer/SavedAddressesPage.js +68 -53
- package/dist/adaptercloud/pages/customer/SavedAddressesPage.js.map +1 -1
- package/dist/adaptercloud/pages/investor/InvestmentDetailsPage.js +63 -61
- package/dist/adaptercloud/pages/investor/InvestmentDetailsPage.js.map +1 -1
- package/dist/adaptercloud/pages/investor/InvestmentOpportunitiesPage.js +86 -84
- package/dist/adaptercloud/pages/investor/InvestmentOpportunitiesPage.js.map +1 -1
- package/dist/adaptercloud/pages/investor/InvestorAnalyticsPage.js +57 -55
- package/dist/adaptercloud/pages/investor/InvestorAnalyticsPage.js.map +1 -1
- package/dist/adaptercloud/pages/investor/InvestorDashboard.js +341 -308
- package/dist/adaptercloud/pages/investor/InvestorDashboard.js.map +1 -1
- package/dist/adaptercloud/pages/investor/InvestorInvestmentsPage.js +34 -32
- package/dist/adaptercloud/pages/investor/InvestorInvestmentsPage.js.map +1 -1
- package/dist/adaptercloud/pages/investor/InvestorPortfolioPage.js +48 -46
- package/dist/adaptercloud/pages/investor/InvestorPortfolioPage.js.map +1 -1
- package/dist/adaptercloud/pages/investor/InvestorProfilePage.js +383 -381
- package/dist/adaptercloud/pages/investor/InvestorProfilePage.js.map +1 -1
- package/dist/adaptercloud/pages/worker/TaskDetailsPage.js +348 -319
- package/dist/adaptercloud/pages/worker/TaskDetailsPage.js.map +1 -1
- package/dist/adaptercloud/pages/worker/WorkerDashboard.js +84 -59
- package/dist/adaptercloud/pages/worker/WorkerDashboard.js.map +1 -1
- package/dist/adaptercloud/pages/worker/WorkerOrdersPage.js +73 -71
- package/dist/adaptercloud/pages/worker/WorkerOrdersPage.js.map +1 -1
- package/dist/adaptercloud/pages/worker/WorkerProfilePage.js +76 -74
- package/dist/adaptercloud/pages/worker/WorkerProfilePage.js.map +1 -1
- package/dist/adaptercloud/pages/worker/WorkerSchedulePage.js +70 -68
- package/dist/adaptercloud/pages/worker/WorkerSchedulePage.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReviewsPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/customer/ReviewsPage.tsx"],"sourcesContent":["/**\n * Reviews Page\n * @module adaptercloud/pages/customer\n *\n * Full-featured reviews: Write Review modal (interactive star rating with\n * text labels, title, comment, guidelines), Owner Response callout,\n * review images, verified badge, Mark as Helpful toggle, 4 filters\n * (search, type, rating, sort), Your Review label, 5 mock reviews.\n */\n\nimport { useState, useCallback } from'react';\nimport {\n Star,\n MessageSquare,\n ThumbsUp,\n PenSquare,\n X,\n CheckCircle2,\n Image,\n MessageCircle,\n Info,\n Search,\n} from'lucide-react';\nimport { PageLayout } from'../../components/PageLayout';\nimport { RatingDisplay } from'../../components/RatingDisplay';\nimport { EmptyState } from'../../components/EmptyState';\nimport { useAdapterCloud } from'../../context/AdapterCloudContext';\nimport { cn } from'../../utils/cn';\nimport { formatDate } from'../../utils/formatters';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface Review {\n id: string;\n userId: string;\n userName: string;\n userAvatar: string;\n itemId: string;\n itemName: string;\n itemType: 'cart' | 'product';\n cartId: string;\n cartName: string;\n orderId: string;\n rating: number;\n title: string;\n comment: string;\n images?: string[];\n helpfulCount: number;\n isHelpful: boolean;\n isVerified: boolean;\n createdAt: string;\n response?: {\n message: string;\n respondedBy: string;\n respondedAt: string;\n };\n}\n\ninterface ReviewFormData {\n rating: number;\n title: string;\n comment: string;\n}\n\ninterface ReviewFilters {\n searchQuery: string;\n type: 'all' | 'cart' | 'product' | 'my-reviews';\n rating: string;\n sortBy: 'recent' | 'helpful' | 'rating-high' | 'rating-low';\n}\n\nconst CURRENT_USER_ID = 'user-001';\n\nconst ratingLabels: Record<number, string> = {\n 1: 'Terrible',\n 2: 'Poor',\n 3: 'Average',\n 4: 'Good',\n 5: 'Excellent',\n};\n\n// ============================================================================\n// Mock Data\n// ============================================================================\n\nconst mockReviews: Review[] = [\n {\n id: 'review-001',\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-001',\n rating: 5,\n title: 'Excellent breakfast spot!',\n comment:\n 'Amazing dosas and filter coffee. The masala dosa was perfectly crispy and the potato filling was delicious. Service was quick and friendly. Highly recommend for authentic South Indian breakfast!',\n helpfulCount: 12,\n isHelpful: true,\n isVerified: true,\n createdAt: '2024-06-20T09:30:00Z',\n response: {\n message:\n \"Thank you so much for your wonderful review! We're thrilled you enjoyed the dosas. We look forward to serving you again soon!\",\n respondedBy: 'Dosa Express Team',\n respondedAt: '2024-06-20T15:45:00Z',\n },\n },\n {\n id: 'review-002',\n userId: 'user-002',\n userName: 'Priya Patel',\n userAvatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100',\n itemId: 'prod-002',\n itemName: 'Pani Puri',\n itemType: 'product',\n cartId: 'FOOD-002',\n cartName: 'Chaat Corner',\n orderId: 'ORD-2024-002',\n rating: 4,\n title: 'Good but could be spicier',\n comment:\n 'The pani puri was good overall. The puris were crispy and fresh. However, the pani could have been more spicy and tangy. The portions were generous though.',\n helpfulCount: 8,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-18T19:15:00Z',\n },\n {\n id: 'review-003',\n userId: 'user-003',\n userName: 'Amit Kumar',\n userAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100',\n itemId: 'FOOD-002',\n itemName: 'Chaat Corner',\n itemType: 'cart',\n cartId: 'FOOD-002',\n cartName: 'Chaat Corner',\n orderId: 'ORD-2024-003',\n rating: 5,\n title: 'Authentic Mumbai street food!',\n comment:\n 'This place brings back memories of Mumbai streets. Every item I ordered was authentic and delicious. The bhel puri and vada pav were exceptional. Will definitely order again!',\n images: ['https://images.unsplash.com/photo-1601050690597-df0568f70950?w=400'],\n helpfulCount: 15,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-17T20:30:00Z',\n },\n {\n id: 'review-004',\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'prod-001',\n itemName: 'Masala Dosa',\n itemType: 'product',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-004',\n rating: 5,\n title: 'Perfect masala dosa!',\n comment:\n 'The masala dosa here is absolutely perfect. Crispy, golden brown, and the potato masala inside is perfectly spiced. Comes with great chutneys and sambar.',\n helpfulCount: 6,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-15T08:45:00Z',\n response: {\n message: 'Glad you loved it, Rahul! Our chef takes great care with every dosa. See you soon!',\n respondedBy: 'Dosa Express Team',\n respondedAt: '2024-06-15T14:00:00Z',\n },\n },\n {\n id: 'review-005',\n userId: 'user-004',\n userName: 'Sneha Reddy',\n userAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-005',\n rating: 3,\n title: 'Average experience',\n comment:\n 'The food was okay but delivery took longer than expected. The taste was good but not exceptional. The packaging could be better to keep food warm.',\n helpfulCount: 4,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-12T12:20:00Z',\n },\n];\n\n// ============================================================================\n// Star Rating Interactive Component\n// ============================================================================\n\nfunction InteractiveStarRating({\n value,\n onChange,\n}: {\n value: number;\n onChange: (v: number) => void;\n}) {\n const [hoverValue, setHoverValue] = useState(0);\n const displayValue = hoverValue || value;\n\n return (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n type=\"button\"\n onClick={() => onChange(star)}\n onMouseEnter={() => setHoverValue(star)}\n onMouseLeave={() => setHoverValue(0)}\n className=\"p-0.5 transition-transform\"\n >\n <Star\n className={cn(\n 'h-6 w-6',\n star <= displayValue\n ? 'fill-status-warning-text text-status-warning-text'\n : 'fill-none text-text-secondary/40'\n )}\n />\n </button>\n ))}\n </div>\n <span className=\"text-sm font-medium text-text-secondary\">\n {ratingLabels[displayValue] ?? ''}\n </span>\n </div>\n );\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function ReviewsPage() {\n const { navigateTo } = useAdapterCloud();\n const [reviews, setReviews] = useState<Review[]>(mockReviews);\n const [showWriteReview, setShowWriteReview] = useState(false);\n const [reviewForm, setReviewForm] = useState<ReviewFormData>({\n rating: 5,\n title: '',\n comment: '',\n });\n const [filters, setFilters] = useState<ReviewFilters>({\n searchQuery: '',\n type: 'all',\n rating: '',\n sortBy: 'recent',\n });\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n\n const showStatus = (msg: string) => {\n setStatusMessage(msg);\n setTimeout(() => setStatusMessage(null), 3000);\n };\n\n // --- Filtered reviews ---\n const filteredReviews = useCallback(() => {\n let result = [...reviews];\n\n // Type filter\n if (filters.type === 'my-reviews') {\n result = result.filter((r) => r.userId === CURRENT_USER_ID);\n } else if (filters.type !== 'all') {\n result = result.filter((r) => r.itemType === filters.type);\n }\n\n // Rating filter\n if (filters.rating) {\n const ratingVal = parseInt(filters.rating, 10);\n result = result.filter((r) => r.rating === ratingVal);\n }\n\n // Search\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n result = result.filter(\n (r) =>\n r.itemName.toLowerCase().includes(q) ||\n r.title.toLowerCase().includes(q) ||\n r.comment.toLowerCase().includes(q) ||\n r.cartName.toLowerCase().includes(q)\n );\n }\n\n // Sort\n switch (filters.sortBy) {\n case'recent':\n result.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\n break;\n case'helpful':\n result.sort((a, b) => b.helpfulCount - a.helpfulCount);\n break;\n case'rating-high':\n result.sort((a, b) => b.rating - a.rating);\n break;\n case'rating-low':\n result.sort((a, b) => a.rating - b.rating);\n break;\n }\n\n return result;\n }, [reviews, filters])();\n\n // --- Stats ---\n const myReviewCount = reviews.filter((r) => r.userId === CURRENT_USER_ID).length;\n const avgRating =\n reviews.length > 0\n ? (reviews.reduce((s, r) => s + r.rating, 0) / reviews.length).toFixed(1)\n : '0';\n\n // --- Handlers ---\n const handleSubmitReview = () => {\n if (!reviewForm.title.trim() || !reviewForm.comment.trim()) {\n alert('Please fill in the Review Title and Comment.');\n return;\n }\n\n const newReview: Review = {\n id: `review-${Date.now()}`,\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: `ORD-2024-${Date.now()}`,\n rating: reviewForm.rating,\n title: reviewForm.title,\n comment: reviewForm.comment,\n helpfulCount: 0,\n isHelpful: false,\n isVerified: true,\n createdAt: new Date().toISOString(),\n };\n\n setReviews((prev) => [newReview, ...prev]);\n setReviewForm({ rating: 5, title: '', comment: '' });\n setShowWriteReview(false);\n showStatus('Review submitted successfully!');\n };\n\n const handleMarkHelpful = (reviewId: string) => {\n setReviews((prev) =>\n prev.map((r) =>\n r.id === reviewId\n ? {\n ...r,\n isHelpful: !r.isHelpful,\n helpfulCount: r.isHelpful ? r.helpfulCount - 1 : r.helpfulCount + 1,\n }\n : r\n )\n );\n };\n\n return (\n <PageLayout\n title=\"Reviews & Ratings\"\n description={`${filteredReviews.length} reviews found`}\n actions={\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(true)}\n className={cn(\n 'flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-opacity',\n 'bg-foreground text-background hover:opacity-90'\n )}\n >\n <PenSquare className=\"h-4 w-4\" />\n Write Review\n </button>\n }\n >\n {/* Status message */}\n {statusMessage && (\n <div className=\"mb-4 rounded-lg border border-status-success-border/30 bg-status-success-bg-subtle/10 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 {/* Stats */}\n <div className=\"flex flex-wrap items-center gap-4 text-sm text-text-secondary mb-6\">\n <span>{reviews.length} total reviews</span>\n <span>{myReviewCount} your reviews</span>\n <span className=\"flex items-center gap-1\">\n Average: {avgRating}\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n </span>\n </div>\n\n {/* Filters */}\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-4 mb-6 bg-gloss-card shadow-pop\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3\">\n {/* Search */}\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search reviews...\"\n value={filters.searchQuery}\n onChange={(e) => setFilters((p) => ({ ...p, searchQuery: e.target.value }))}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-9 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\n {/* Type */}\n <select\n value={filters.type}\n onChange={(e) =>\n setFilters((p) => ({ ...p, type: e.target.value as ReviewFilters['type'] }))\n }\n className=\"w-full 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\">All Reviews</option>\n <option value=\"cart\">Cart Reviews</option>\n <option value=\"product\">Product Reviews</option>\n <option value=\"my-reviews\">My Reviews</option>\n </select>\n\n {/* Rating */}\n <select\n value={filters.rating}\n onChange={(e) => setFilters((p) => ({ ...p, rating: e.target.value }))}\n className=\"w-full 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 Ratings</option>\n <option value=\"5\">5 Stars</option>\n <option value=\"4\">4 Stars</option>\n <option value=\"3\">3 Stars</option>\n <option value=\"2\">2 Stars</option>\n <option value=\"1\">1 Star</option>\n </select>\n\n {/* Sort */}\n <select\n value={filters.sortBy}\n onChange={(e) =>\n setFilters((p) => ({ ...p, sortBy: e.target.value as ReviewFilters['sortBy'] }))\n }\n className=\"w-full 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=\"recent\">Most Recent</option>\n <option value=\"helpful\">Most Helpful</option>\n <option value=\"rating-high\">Highest Rating</option>\n <option value=\"rating-low\">Lowest Rating</option>\n </select>\n </div>\n </div>\n\n {/* Reviews List */}\n {filteredReviews.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<MessageSquare className=\"h-12 w-12\" />}\n title=\"No reviews found\"\n description=\"Be the first to write a review!\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(true)}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Write a Review\n </button>\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {filteredReviews.map((review) => {\n const isMyReview = review.userId === CURRENT_USER_ID;\n return (\n <div\n key={review.id}\n className={cn(\n 'rounded-lg border bg-bg-surface p-5',\n isMyReview ? 'border-foreground/20' : 'border-border-subtle'\n )}\n >\n {/* Header */}\n <div className=\"flex items-start gap-3 mb-3\">\n <img\n src={review.userAvatar}\n alt={review.userName}\n className=\"h-10 w-10 rounded-full object-cover shrink-0\"\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {review.userName}\n </span>\n {review.isVerified && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle/10 px-2 py-0.5 text-[10px] font-medium text-status-success-text\">\n <CheckCircle2 className=\"h-3 w-3\" />\n Verified\n </span>\n )}\n {isMyReview && (\n <span className=\"inline-flex items-center rounded-full bg-accent-blue-subtle/10 px-2 py-0.5 text-[10px] font-medium text-accent-blue\">\n Your Review\n </span>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-secondary mt-0.5\">\n <span>\n {review.itemType === 'cart' ? (\n <MessageSquare className=\"inline h-3 w-3 mr-0.5\" />\n ) : (\n <Star className=\"inline h-3 w-3 mr-0.5\" />\n )}\n {review.itemName}\n </span>\n <span>\n {formatDate(review.createdAt, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n </div>\n </div>\n <div className=\"shrink-0\">\n <RatingDisplay value={review.rating} size=\"sm\" />\n <p className=\"text-xs text-text-secondary mt-0.5 text-right\">\n From: {review.cartName}\n </p>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"mb-3\">\n <h4 className=\"text-sm font-semibold text-text-primary mb-1\">{review.title}</h4>\n <p className=\"text-sm text-text-primary leading-relaxed\">{review.comment}</p>\n </div>\n\n {/* Review Images */}\n {review.images && review.images.length > 0 && (\n <div className=\"mb-3 flex gap-2 overflow-x-auto pb-1\">\n {review.images.map((img, idx) => (\n <img\n key={idx}\n src={img}\n alt={`Review image ${idx + 1}`}\n className=\"h-20 w-20 rounded-lg object-cover shrink-0\"\n />\n ))}\n </div>\n )}\n\n {/* Owner Response */}\n {review.response && (\n <div className=\"mb-3 rounded-lg border border-accent-blue-subtle/20 bg-accent-blue-subtle/5 p-3\">\n <div className=\"flex flex-wrap items-center gap-2 mb-1.5\">\n <MessageCircle className=\"h-3.5 w-3.5 text-accent-blue\" />\n <span className=\"text-xs font-medium text-accent-blue\">\n Response from {review.response.respondedBy}\n </span>\n <span className=\"text-[10px] text-accent-blue\">\n {formatDate(review.response.respondedAt, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n </div>\n <p className=\"text-xs text-accent-blue leading-relaxed\">\n {review.response.message}\n </p>\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between pt-3 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={() => handleMarkHelpful(review.id)}\n className={cn(\n 'flex items-center gap-1.5 text-xs transition-colors',\n review.isHelpful\n ? 'text-accent-blue font-medium'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <ThumbsUp className={cn('h-3.5 w-3.5', review.isHelpful && 'fill-current')} />\n Helpful ({review.helpfulCount})\n </button>\n\n <div className=\"flex items-center gap-3 text-xs text-text-secondary\">\n <span>Order: {review.orderId}</span>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/carts/${review.cartId}`)}\n className=\"font-medium hover:text-text-primary transition-colors\"\n >\n View Cart\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Write Review Modal */}\n {showWriteReview && (\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-2xl max-h-[90vh] overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface shadow-lg\">\n <div className=\"p-5\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-5\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Write a Review</h3>\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(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=\"space-y-4\">\n {/* Rating */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Rating <span className=\"text-status-error-text\">*</span>\n </label>\n <InteractiveStarRating\n value={reviewForm.rating}\n onChange={(v) => setReviewForm((p) => ({ ...p, rating: v }))}\n />\n </div>\n\n {/* Title */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Review Title <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"Summarize your experience\"\n value={reviewForm.title}\n onChange={(e) => setReviewForm((p) => ({ ...p, title: e.target.value }))}\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)]\"\n />\n </div>\n\n {/* Comment */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Your Review <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n rows={5}\n placeholder=\"Share your experience with others...\"\n value={reviewForm.comment}\n onChange={(e) => setReviewForm((p) => ({ ...p, comment: e.target.value }))}\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 {/* Guidelines */}\n <div className=\"rounded-lg bg-bg-sunken p-4\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Info className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-primary\">Review Guidelines</h4>\n </div>\n <ul className=\"text-xs text-text-secondary space-y-1\">\n <li>- Be honest and helpful to other customers</li>\n <li>- Focus on your experience with the food and service</li>\n <li>- Avoid personal information or inappropriate content</li>\n <li>- Reviews are public and cannot be deleted once submitted</li>\n </ul>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-5\">\n <button\n type=\"button\"\n onClick={handleSubmitReview}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Submit Review\n </button>\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(false)}\n className=\"rounded-lg border border-border-subtle px-4 py-2 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":"ReviewsPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/customer/ReviewsPage.tsx"],"sourcesContent":["/**\n * Reviews Page\n * @module adaptercloud/pages/customer\n *\n * Full-featured reviews: Write Review modal (interactive star rating with\n * text labels, title, comment, guidelines), Owner Response callout,\n * review images, verified badge, Mark as Helpful toggle, 4 filters\n * (search, type, rating, sort), Your Review label, 5 mock reviews.\n */\n\nimport { useState, useCallback } from'react';\nimport {\n Star,\n MessageSquare,\n ThumbsUp,\n PenSquare,\n X,\n CheckCircle2,\n Image,\n MessageCircle,\n Info,\n Search,\n} from'lucide-react';\nimport { EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from'../../components/PageLayout';\nimport { RatingDisplay } from'../../components/RatingDisplay';\nimport { EmptyState } from'../../components/EmptyState';\nimport { useAdapterCloud } from'../../context/AdapterCloudContext';\nimport { cn } from'../../utils/cn';\nimport { formatDate } from'../../utils/formatters';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface Review {\n id: string;\n userId: string;\n userName: string;\n userAvatar: string;\n itemId: string;\n itemName: string;\n itemType: 'cart' | 'product';\n cartId: string;\n cartName: string;\n orderId: string;\n rating: number;\n title: string;\n comment: string;\n images?: string[];\n helpfulCount: number;\n isHelpful: boolean;\n isVerified: boolean;\n createdAt: string;\n response?: {\n message: string;\n respondedBy: string;\n respondedAt: string;\n };\n}\n\ninterface ReviewFormData {\n rating: number;\n title: string;\n comment: string;\n}\n\ninterface ReviewFilters {\n searchQuery: string;\n type: 'all' | 'cart' | 'product' | 'my-reviews';\n rating: string;\n sortBy: 'recent' | 'helpful' | 'rating-high' | 'rating-low';\n}\n\nconst CURRENT_USER_ID = 'user-001';\n\nconst ratingLabels: Record<number, string> = {\n 1: 'Terrible',\n 2: 'Poor',\n 3: 'Average',\n 4: 'Good',\n 5: 'Excellent',\n};\n\n// ============================================================================\n// Mock Data\n// ============================================================================\n\nconst mockReviews: Review[] = [\n {\n id: 'review-001',\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-001',\n rating: 5,\n title: 'Excellent breakfast spot!',\n comment:\n 'Amazing dosas and filter coffee. The masala dosa was perfectly crispy and the potato filling was delicious. Service was quick and friendly. Highly recommend for authentic South Indian breakfast!',\n helpfulCount: 12,\n isHelpful: true,\n isVerified: true,\n createdAt: '2024-06-20T09:30:00Z',\n response: {\n message:\n \"Thank you so much for your wonderful review! We're thrilled you enjoyed the dosas. We look forward to serving you again soon!\",\n respondedBy: 'Dosa Express Team',\n respondedAt: '2024-06-20T15:45:00Z',\n },\n },\n {\n id: 'review-002',\n userId: 'user-002',\n userName: 'Priya Patel',\n userAvatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100',\n itemId: 'prod-002',\n itemName: 'Pani Puri',\n itemType: 'product',\n cartId: 'FOOD-002',\n cartName: 'Chaat Corner',\n orderId: 'ORD-2024-002',\n rating: 4,\n title: 'Good but could be spicier',\n comment:\n 'The pani puri was good overall. The puris were crispy and fresh. However, the pani could have been more spicy and tangy. The portions were generous though.',\n helpfulCount: 8,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-18T19:15:00Z',\n },\n {\n id: 'review-003',\n userId: 'user-003',\n userName: 'Amit Kumar',\n userAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100',\n itemId: 'FOOD-002',\n itemName: 'Chaat Corner',\n itemType: 'cart',\n cartId: 'FOOD-002',\n cartName: 'Chaat Corner',\n orderId: 'ORD-2024-003',\n rating: 5,\n title: 'Authentic Mumbai street food!',\n comment:\n 'This place brings back memories of Mumbai streets. Every item I ordered was authentic and delicious. The bhel puri and vada pav were exceptional. Will definitely order again!',\n images: ['https://images.unsplash.com/photo-1601050690597-df0568f70950?w=400'],\n helpfulCount: 15,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-17T20:30:00Z',\n },\n {\n id: 'review-004',\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'prod-001',\n itemName: 'Masala Dosa',\n itemType: 'product',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-004',\n rating: 5,\n title: 'Perfect masala dosa!',\n comment:\n 'The masala dosa here is absolutely perfect. Crispy, golden brown, and the potato masala inside is perfectly spiced. Comes with great chutneys and sambar.',\n helpfulCount: 6,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-15T08:45:00Z',\n response: {\n message: 'Glad you loved it, Rahul! Our chef takes great care with every dosa. See you soon!',\n respondedBy: 'Dosa Express Team',\n respondedAt: '2024-06-15T14:00:00Z',\n },\n },\n {\n id: 'review-005',\n userId: 'user-004',\n userName: 'Sneha Reddy',\n userAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-005',\n rating: 3,\n title: 'Average experience',\n comment:\n 'The food was okay but delivery took longer than expected. The taste was good but not exceptional. The packaging could be better to keep food warm.',\n helpfulCount: 4,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-12T12:20:00Z',\n },\n];\n\n// ============================================================================\n// Star Rating Interactive Component\n// ============================================================================\n\nfunction InteractiveStarRating({\n value,\n onChange,\n}: {\n value: number;\n onChange: (v: number) => void;\n}) {\n const [hoverValue, setHoverValue] = useState(0);\n const displayValue = hoverValue || value;\n\n return (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n type=\"button\"\n onClick={() => onChange(star)}\n onMouseEnter={() => setHoverValue(star)}\n onMouseLeave={() => setHoverValue(0)}\n className=\"p-0.5 transition-transform\"\n >\n <Star\n className={cn(\n 'h-6 w-6',\n star <= displayValue\n ? 'fill-status-warning-text text-status-warning-text'\n : 'fill-none text-text-secondary/40'\n )}\n />\n </button>\n ))}\n </div>\n <span className=\"text-sm font-medium text-text-secondary\">\n {ratingLabels[displayValue] ?? ''}\n </span>\n </div>\n );\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function ReviewsPage() {\n const { navigateTo } = useAdapterCloud();\n const [reviews, setReviews] = useState<Review[]>(mockReviews);\n const [showWriteReview, setShowWriteReview] = useState(false);\n const [reviewForm, setReviewForm] = useState<ReviewFormData>({\n rating: 5,\n title: '',\n comment: '',\n });\n const [filters, setFilters] = useState<ReviewFilters>({\n searchQuery: '',\n type: 'all',\n rating: '',\n sortBy: 'recent',\n });\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n\n const showStatus = (msg: string) => {\n setStatusMessage(msg);\n setTimeout(() => setStatusMessage(null), 3000);\n };\n\n // --- Filtered reviews ---\n const filteredReviews = useCallback(() => {\n let result = [...reviews];\n\n // Type filter\n if (filters.type === 'my-reviews') {\n result = result.filter((r) => r.userId === CURRENT_USER_ID);\n } else if (filters.type !== 'all') {\n result = result.filter((r) => r.itemType === filters.type);\n }\n\n // Rating filter\n if (filters.rating) {\n const ratingVal = parseInt(filters.rating, 10);\n result = result.filter((r) => r.rating === ratingVal);\n }\n\n // Search\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n result = result.filter(\n (r) =>\n r.itemName.toLowerCase().includes(q) ||\n r.title.toLowerCase().includes(q) ||\n r.comment.toLowerCase().includes(q) ||\n r.cartName.toLowerCase().includes(q)\n );\n }\n\n // Sort\n switch (filters.sortBy) {\n case'recent':\n result.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\n break;\n case'helpful':\n result.sort((a, b) => b.helpfulCount - a.helpfulCount);\n break;\n case'rating-high':\n result.sort((a, b) => b.rating - a.rating);\n break;\n case'rating-low':\n result.sort((a, b) => a.rating - b.rating);\n break;\n }\n\n return result;\n }, [reviews, filters])();\n\n // --- Stats ---\n const myReviewCount = reviews.filter((r) => r.userId === CURRENT_USER_ID).length;\n const avgRating =\n reviews.length > 0\n ? (reviews.reduce((s, r) => s + r.rating, 0) / reviews.length).toFixed(1)\n : '0';\n\n // --- Handlers ---\n const handleSubmitReview = () => {\n if (!reviewForm.title.trim() || !reviewForm.comment.trim()) {\n alert('Please fill in the Review Title and Comment.');\n return;\n }\n\n const newReview: Review = {\n id: `review-${Date.now()}`,\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: `ORD-2024-${Date.now()}`,\n rating: reviewForm.rating,\n title: reviewForm.title,\n comment: reviewForm.comment,\n helpfulCount: 0,\n isHelpful: false,\n isVerified: true,\n createdAt: new Date().toISOString(),\n };\n\n setReviews((prev) => [newReview, ...prev]);\n setReviewForm({ rating: 5, title: '', comment: '' });\n setShowWriteReview(false);\n showStatus('Review submitted successfully!');\n };\n\n const handleMarkHelpful = (reviewId: string) => {\n setReviews((prev) =>\n prev.map((r) =>\n r.id === reviewId\n ? {\n ...r,\n isHelpful: !r.isHelpful,\n helpfulCount: r.isHelpful ? r.helpfulCount - 1 : r.helpfulCount + 1,\n }\n : r\n )\n );\n };\n\n return (\n <PageLayout\n title=\"Reviews & Ratings\"\n description={`${filteredReviews.length} reviews found`}\n purpose=\"The feedback you've left for carts you've ordered from. Revisit and edit your reviews, and rate recent orders that are still waiting for one — your ratings help other customers and reward great carts.\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(true)}\n className={cn(\n 'flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-opacity',\n 'bg-foreground text-background hover:opacity-90'\n )}\n >\n <PenSquare className=\"h-4 w-4\" />\n Write Review\n </button>\n }\n >\n {/* Status message */}\n {statusMessage && (\n <div className=\"mb-4 rounded-lg border border-status-success-border/30 bg-status-success-bg-subtle/10 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 {/* Stats (emphasis zone) */}\n <EmphasisPanel className=\"mb-6 flex flex-wrap items-center gap-4 text-sm text-text-secondary py-4\">\n <span>{reviews.length} total reviews</span>\n <span>{myReviewCount} your reviews</span>\n <span className=\"flex items-center gap-1\">\n Average: {avgRating}\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n </span>\n </EmphasisPanel>\n\n {/* Filters */}\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-4 mb-6 bg-gloss-card shadow-pop\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3\">\n {/* Search */}\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search reviews...\"\n value={filters.searchQuery}\n onChange={(e) => setFilters((p) => ({ ...p, searchQuery: e.target.value }))}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-9 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\n {/* Type */}\n <select\n value={filters.type}\n onChange={(e) =>\n setFilters((p) => ({ ...p, type: e.target.value as ReviewFilters['type'] }))\n }\n className=\"w-full 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\">All Reviews</option>\n <option value=\"cart\">Cart Reviews</option>\n <option value=\"product\">Product Reviews</option>\n <option value=\"my-reviews\">My Reviews</option>\n </select>\n\n {/* Rating */}\n <select\n value={filters.rating}\n onChange={(e) => setFilters((p) => ({ ...p, rating: e.target.value }))}\n className=\"w-full 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 Ratings</option>\n <option value=\"5\">5 Stars</option>\n <option value=\"4\">4 Stars</option>\n <option value=\"3\">3 Stars</option>\n <option value=\"2\">2 Stars</option>\n <option value=\"1\">1 Star</option>\n </select>\n\n {/* Sort */}\n <select\n value={filters.sortBy}\n onChange={(e) =>\n setFilters((p) => ({ ...p, sortBy: e.target.value as ReviewFilters['sortBy'] }))\n }\n className=\"w-full 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=\"recent\">Most Recent</option>\n <option value=\"helpful\">Most Helpful</option>\n <option value=\"rating-high\">Highest Rating</option>\n <option value=\"rating-low\">Lowest Rating</option>\n </select>\n </div>\n </div>\n\n {/* Reviews List */}\n {filteredReviews.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<MessageSquare className=\"h-12 w-12\" />}\n title=\"No reviews found\"\n description=\"Be the first to write a review!\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(true)}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Write a Review\n </button>\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {filteredReviews.map((review) => {\n const isMyReview = review.userId === CURRENT_USER_ID;\n return (\n <div\n key={review.id}\n className={cn(\n 'rounded-lg border bg-bg-surface p-5',\n isMyReview ? 'border-foreground/20' : 'border-border-subtle'\n )}\n >\n {/* Header */}\n <div className=\"flex items-start gap-3 mb-3\">\n <img\n src={review.userAvatar}\n alt={review.userName}\n className=\"h-10 w-10 rounded-full object-cover shrink-0\"\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {review.userName}\n </span>\n {review.isVerified && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle/10 px-2 py-0.5 text-[10px] font-medium text-status-success-text\">\n <CheckCircle2 className=\"h-3 w-3\" />\n Verified\n </span>\n )}\n {isMyReview && (\n <span className=\"inline-flex items-center rounded-full bg-accent-blue-subtle/10 px-2 py-0.5 text-[10px] font-medium text-accent-blue\">\n Your Review\n </span>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-secondary mt-0.5\">\n <span>\n {review.itemType === 'cart' ? (\n <MessageSquare className=\"inline h-3 w-3 mr-0.5\" />\n ) : (\n <Star className=\"inline h-3 w-3 mr-0.5\" />\n )}\n {review.itemName}\n </span>\n <span>\n {formatDate(review.createdAt, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n </div>\n </div>\n <div className=\"shrink-0\">\n <RatingDisplay value={review.rating} size=\"sm\" />\n <p className=\"text-xs text-text-secondary mt-0.5 text-right\">\n From: {review.cartName}\n </p>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"mb-3\">\n <h4 className=\"text-sm font-semibold text-text-primary mb-1\">{review.title}</h4>\n <p className=\"text-sm text-text-primary leading-relaxed\">{review.comment}</p>\n </div>\n\n {/* Review Images */}\n {review.images && review.images.length > 0 && (\n <div className=\"mb-3 flex gap-2 overflow-x-auto pb-1\">\n {review.images.map((img, idx) => (\n <img\n key={idx}\n src={img}\n alt={`Review image ${idx + 1}`}\n className=\"h-20 w-20 rounded-lg object-cover shrink-0\"\n />\n ))}\n </div>\n )}\n\n {/* Owner Response */}\n {review.response && (\n <div className=\"mb-3 rounded-lg border border-accent-blue-subtle/20 bg-accent-blue-subtle/5 p-3\">\n <div className=\"flex flex-wrap items-center gap-2 mb-1.5\">\n <MessageCircle className=\"h-3.5 w-3.5 text-accent-blue\" />\n <span className=\"text-xs font-medium text-accent-blue\">\n Response from {review.response.respondedBy}\n </span>\n <span className=\"text-[10px] text-accent-blue\">\n {formatDate(review.response.respondedAt, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n </div>\n <p className=\"text-xs text-accent-blue leading-relaxed\">\n {review.response.message}\n </p>\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between pt-3 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={() => handleMarkHelpful(review.id)}\n className={cn(\n 'flex items-center gap-1.5 text-xs transition-colors',\n review.isHelpful\n ? 'text-accent-blue font-medium'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <ThumbsUp className={cn('h-3.5 w-3.5', review.isHelpful && 'fill-current')} />\n Helpful ({review.helpfulCount})\n </button>\n\n <div className=\"flex items-center gap-3 text-xs text-text-secondary\">\n <span>Order: {review.orderId}</span>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/carts/${review.cartId}`)}\n className=\"font-medium hover:text-text-primary transition-colors\"\n >\n View Cart\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Write Review Modal */}\n {showWriteReview && (\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-2xl max-h-[90vh] overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface shadow-lg\">\n <div className=\"p-5\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-5\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Write a Review</h3>\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(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=\"space-y-4\">\n {/* Rating */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Rating <span className=\"text-status-error-text\">*</span>\n </label>\n <InteractiveStarRating\n value={reviewForm.rating}\n onChange={(v) => setReviewForm((p) => ({ ...p, rating: v }))}\n />\n </div>\n\n {/* Title */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Review Title <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"Summarize your experience\"\n value={reviewForm.title}\n onChange={(e) => setReviewForm((p) => ({ ...p, title: e.target.value }))}\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)]\"\n />\n </div>\n\n {/* Comment */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Your Review <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n rows={5}\n placeholder=\"Share your experience with others...\"\n value={reviewForm.comment}\n onChange={(e) => setReviewForm((p) => ({ ...p, comment: e.target.value }))}\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 {/* Guidelines */}\n <div className=\"rounded-lg bg-bg-sunken p-4\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Info className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-primary\">Review Guidelines</h4>\n </div>\n <ul className=\"text-xs text-text-secondary space-y-1\">\n <li>- Be honest and helpful to other customers</li>\n <li>- Focus on your experience with the food and service</li>\n <li>- Avoid personal information or inappropriate content</li>\n <li>- Reviews are public and cannot be deleted once submitted</li>\n </ul>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-5\">\n <button\n type=\"button\"\n onClick={handleSubmitReview}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Submit Review\n </button>\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(false)}\n className=\"rounded-lg border border-border-subtle px-4 py-2 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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"}
|
|
@@ -6,8 +6,9 @@ import { formatDate as i } from "../../utils/formatters.js";
|
|
|
6
6
|
import { useState as a } from "react";
|
|
7
7
|
import { Briefcase as o, CheckCircle2 as s, Clock as c, Hash as l, Home as u, MapPin as d, Pencil as f, Plus as p, Star as m, Trash2 as h, X as g } from "lucide-react";
|
|
8
8
|
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
9
|
+
import { EmphasisPanel as y } from "@burdenoff/fe-libs/ui";
|
|
9
10
|
//#region src/adaptercloud/pages/customer/SavedAddressesPage.tsx
|
|
10
|
-
var
|
|
11
|
+
var b = {
|
|
11
12
|
type: "home",
|
|
12
13
|
nickname: "",
|
|
13
14
|
addressLine1: "",
|
|
@@ -16,7 +17,7 @@ var y = {
|
|
|
16
17
|
state: "",
|
|
17
18
|
zipCode: "",
|
|
18
19
|
isDefault: !1
|
|
19
|
-
},
|
|
20
|
+
}, x = [
|
|
20
21
|
{
|
|
21
22
|
id: "addr-001",
|
|
22
23
|
type: "home",
|
|
@@ -69,16 +70,16 @@ var y = {
|
|
|
69
70
|
usageCount: 8,
|
|
70
71
|
lastUsed: "2024-06-18T11:30:00Z"
|
|
71
72
|
}
|
|
72
|
-
],
|
|
73
|
+
], S = {
|
|
73
74
|
home: u,
|
|
74
75
|
office: o,
|
|
75
76
|
other: d
|
|
76
|
-
},
|
|
77
|
+
}, C = {
|
|
77
78
|
home: "bg-status-success-bg-subtle/10 text-status-success-text",
|
|
78
79
|
office: "bg-accent-blue-subtle/10 text-accent-blue",
|
|
79
80
|
other: "bg-accent-purple-subtle/10 text-accent-purple"
|
|
80
81
|
};
|
|
81
|
-
function
|
|
82
|
+
function w({ data: e, onChange: t, onSubmit: n, onCancel: r, submitLabel: i }) {
|
|
82
83
|
return /* @__PURE__ */ v("div", {
|
|
83
84
|
className: "space-y-4",
|
|
84
85
|
children: [
|
|
@@ -215,47 +216,47 @@ function C({ data: e, onChange: t, onSubmit: n, onCancel: r, submitLabel: i }) {
|
|
|
215
216
|
]
|
|
216
217
|
});
|
|
217
218
|
}
|
|
218
|
-
function
|
|
219
|
-
let { navigateTo: o } = e(), [u,
|
|
220
|
-
|
|
221
|
-
},
|
|
222
|
-
if (!
|
|
219
|
+
function T() {
|
|
220
|
+
let { navigateTo: o } = e(), [u, T] = a(x), [E, D] = a(!1), [O, k] = a(null), [A, j] = a({ ...b }), [M, N] = a(null), P = (e) => {
|
|
221
|
+
N(e), setTimeout(() => N(null), 3e3);
|
|
222
|
+
}, F = (e) => !e.addressLine1.trim() || !e.city.trim() || !e.state.trim() || !e.zipCode.trim() ? (alert("Please fill in all required fields (Address Line 1, City, State, ZIP Code)."), !1) : !0, I = () => {
|
|
223
|
+
if (!F(A)) return;
|
|
223
224
|
let e = {
|
|
224
225
|
id: `addr-${Date.now()}`,
|
|
225
|
-
type:
|
|
226
|
-
nickname:
|
|
227
|
-
addressLine1:
|
|
228
|
-
addressLine2:
|
|
229
|
-
city:
|
|
230
|
-
state:
|
|
231
|
-
zipCode:
|
|
232
|
-
isDefault:
|
|
226
|
+
type: A.type,
|
|
227
|
+
nickname: A.nickname,
|
|
228
|
+
addressLine1: A.addressLine1,
|
|
229
|
+
addressLine2: A.addressLine2,
|
|
230
|
+
city: A.city,
|
|
231
|
+
state: A.state,
|
|
232
|
+
zipCode: A.zipCode,
|
|
233
|
+
isDefault: A.isDefault,
|
|
233
234
|
usageCount: 0
|
|
234
235
|
};
|
|
235
|
-
|
|
236
|
+
T((t) => {
|
|
236
237
|
let n = [...t];
|
|
237
238
|
return e.isDefault && (n = n.map((e) => ({
|
|
238
239
|
...e,
|
|
239
240
|
isDefault: !1
|
|
240
241
|
}))), [...n, e];
|
|
241
|
-
}),
|
|
242
|
-
},
|
|
243
|
-
|
|
242
|
+
}), j({ ...b }), D(!1), P("Address added successfully");
|
|
243
|
+
}, L = () => {
|
|
244
|
+
O && F(O) && (T((e) => e.map((e) => e.id === O.id ? O : O.isDefault ? {
|
|
244
245
|
...e,
|
|
245
246
|
isDefault: !1
|
|
246
|
-
} : e)),
|
|
247
|
-
},
|
|
247
|
+
} : e)), k(null), P("Address updated successfully"));
|
|
248
|
+
}, R = (e) => {
|
|
248
249
|
if (u.find((t) => t.id === e)?.isDefault) {
|
|
249
250
|
alert("Cannot delete default address. Set another address as default first.");
|
|
250
251
|
return;
|
|
251
252
|
}
|
|
252
|
-
|
|
253
|
-
},
|
|
254
|
-
|
|
253
|
+
T((t) => t.filter((t) => t.id !== e)), P("Address deleted successfully");
|
|
254
|
+
}, z = (e) => {
|
|
255
|
+
T((t) => t.map((t) => ({
|
|
255
256
|
...t,
|
|
256
257
|
isDefault: t.id === e
|
|
257
|
-
}))),
|
|
258
|
-
},
|
|
258
|
+
}))), P("Default address updated");
|
|
259
|
+
}, B = (e, t, n) => {
|
|
259
260
|
e((e) => ({
|
|
260
261
|
...e,
|
|
261
262
|
[t]: n
|
|
@@ -264,31 +265,45 @@ function w() {
|
|
|
264
265
|
return /* @__PURE__ */ v(n, {
|
|
265
266
|
title: "Saved Addresses",
|
|
266
267
|
description: "Manage your delivery addresses for faster checkout",
|
|
267
|
-
|
|
268
|
+
purpose: "Store the places you order to — home, work, a friend's flat — so you pick a delivery address at checkout instead of typing it each time. Mark a default and your most common address is selected automatically.",
|
|
269
|
+
actions: E ? void 0 : /* @__PURE__ */ v("button", {
|
|
268
270
|
type: "button",
|
|
269
|
-
onClick: () =>
|
|
271
|
+
onClick: () => D(!0),
|
|
270
272
|
className: t("flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-opacity", "bg-foreground text-background hover:opacity-90"),
|
|
271
273
|
children: [/* @__PURE__ */ _(p, { className: "h-4 w-4" }), "Add New Address"]
|
|
272
274
|
}),
|
|
273
275
|
children: [
|
|
274
|
-
|
|
276
|
+
M && /* @__PURE__ */ v("div", {
|
|
275
277
|
className: "mb-4 rounded-lg border border-status-success-border/30 bg-status-success-bg-subtle/10 p-3 flex items-center gap-2",
|
|
276
278
|
children: [/* @__PURE__ */ _(s, { className: "h-4 w-4 text-status-success-text shrink-0" }), /* @__PURE__ */ _("p", {
|
|
277
279
|
className: "text-sm text-status-success-text",
|
|
278
|
-
children:
|
|
280
|
+
children: M
|
|
279
281
|
})]
|
|
280
282
|
}),
|
|
281
|
-
|
|
283
|
+
/* @__PURE__ */ v(y, {
|
|
284
|
+
className: "mb-6 flex items-start gap-3 p-4",
|
|
285
|
+
children: [/* @__PURE__ */ _(d, {
|
|
286
|
+
className: "h-5 w-5 shrink-0 text-text-secondary",
|
|
287
|
+
"aria-hidden": "true"
|
|
288
|
+
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("h2", {
|
|
289
|
+
className: "text-sm font-semibold text-text-primary",
|
|
290
|
+
children: "Save once, checkout faster"
|
|
291
|
+
}), /* @__PURE__ */ _("p", {
|
|
292
|
+
className: "mt-0.5 text-sm text-text-secondary",
|
|
293
|
+
children: "Add the places you order to and pick one at checkout instead of typing it each time. Mark a default to have your most-used address selected automatically."
|
|
294
|
+
})] })]
|
|
295
|
+
}),
|
|
296
|
+
E && /* @__PURE__ */ v("div", {
|
|
282
297
|
className: "rounded-lg border border-border-seam bg-bg-surface p-5 mb-6 bg-gloss-card shadow-pop",
|
|
283
298
|
children: [/* @__PURE__ */ _("h3", {
|
|
284
299
|
className: "text-base font-semibold text-text-primary mb-4",
|
|
285
300
|
children: "Add New Address"
|
|
286
|
-
}), /* @__PURE__ */ _(
|
|
287
|
-
data:
|
|
288
|
-
onChange: (e, t) =>
|
|
289
|
-
onSubmit:
|
|
301
|
+
}), /* @__PURE__ */ _(w, {
|
|
302
|
+
data: A,
|
|
303
|
+
onChange: (e, t) => B(j, e, t),
|
|
304
|
+
onSubmit: I,
|
|
290
305
|
onCancel: () => {
|
|
291
|
-
|
|
306
|
+
D(!1), j({ ...b });
|
|
292
307
|
},
|
|
293
308
|
submitLabel: "Save Address"
|
|
294
309
|
})]
|
|
@@ -296,7 +311,7 @@ function w() {
|
|
|
296
311
|
u.length > 0 ? /* @__PURE__ */ _("div", {
|
|
297
312
|
className: "space-y-3",
|
|
298
313
|
children: u.map((e) => {
|
|
299
|
-
let n =
|
|
314
|
+
let n = S[e.type];
|
|
300
315
|
return /* @__PURE__ */ _("div", {
|
|
301
316
|
className: t("rounded-lg border bg-bg-surface p-4", e.isDefault ? "border-foreground/30" : "border-border-subtle"),
|
|
302
317
|
children: /* @__PURE__ */ v("div", {
|
|
@@ -312,7 +327,7 @@ function w() {
|
|
|
312
327
|
/* @__PURE__ */ v("div", {
|
|
313
328
|
className: "flex flex-wrap items-center gap-2",
|
|
314
329
|
children: [/* @__PURE__ */ _("span", {
|
|
315
|
-
className: t("inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium capitalize",
|
|
330
|
+
className: t("inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium capitalize", C[e.type]),
|
|
316
331
|
children: e.type
|
|
317
332
|
}), e.isDefault && /* @__PURE__ */ v("span", {
|
|
318
333
|
className: "inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle/10 px-2 py-0.5 text-[10px] font-medium text-status-success-text",
|
|
@@ -372,21 +387,21 @@ function w() {
|
|
|
372
387
|
children: [
|
|
373
388
|
!e.isDefault && /* @__PURE__ */ _("button", {
|
|
374
389
|
type: "button",
|
|
375
|
-
onClick: () =>
|
|
390
|
+
onClick: () => z(e.id),
|
|
376
391
|
title: "Set as default",
|
|
377
392
|
className: "rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors",
|
|
378
393
|
children: /* @__PURE__ */ _(m, { className: "h-4 w-4" })
|
|
379
394
|
}),
|
|
380
395
|
/* @__PURE__ */ _("button", {
|
|
381
396
|
type: "button",
|
|
382
|
-
onClick: () =>
|
|
397
|
+
onClick: () => k({ ...e }),
|
|
383
398
|
title: "Edit address",
|
|
384
399
|
className: "rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors",
|
|
385
400
|
children: /* @__PURE__ */ _(f, { className: "h-4 w-4" })
|
|
386
401
|
}),
|
|
387
402
|
/* @__PURE__ */ _("button", {
|
|
388
403
|
type: "button",
|
|
389
|
-
onClick: () =>
|
|
404
|
+
onClick: () => R(e.id),
|
|
390
405
|
title: "Delete address",
|
|
391
406
|
className: "rounded-md p-2 text-text-secondary hover:bg-status-error-bg/10 hover:text-status-error-text transition-colors",
|
|
392
407
|
children: /* @__PURE__ */ _(h, { className: "h-4 w-4" })
|
|
@@ -403,12 +418,12 @@ function w() {
|
|
|
403
418
|
description: "Add a delivery address to get started.",
|
|
404
419
|
action: /* @__PURE__ */ _("button", {
|
|
405
420
|
type: "button",
|
|
406
|
-
onClick: () =>
|
|
421
|
+
onClick: () => D(!0),
|
|
407
422
|
className: "rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity",
|
|
408
423
|
children: "Add Address"
|
|
409
424
|
})
|
|
410
425
|
}),
|
|
411
|
-
|
|
426
|
+
O && /* @__PURE__ */ _("div", {
|
|
412
427
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4",
|
|
413
428
|
children: /* @__PURE__ */ _("div", {
|
|
414
429
|
className: "w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface shadow-lg",
|
|
@@ -421,15 +436,15 @@ function w() {
|
|
|
421
436
|
children: "Edit Address"
|
|
422
437
|
}), /* @__PURE__ */ _("button", {
|
|
423
438
|
type: "button",
|
|
424
|
-
onClick: () =>
|
|
439
|
+
onClick: () => k(null),
|
|
425
440
|
className: "rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors",
|
|
426
441
|
children: /* @__PURE__ */ _(g, { className: "h-4 w-4" })
|
|
427
442
|
})]
|
|
428
|
-
}), /* @__PURE__ */ _(
|
|
429
|
-
data:
|
|
430
|
-
onChange: (e, t) =>
|
|
431
|
-
onSubmit:
|
|
432
|
-
onCancel: () =>
|
|
443
|
+
}), /* @__PURE__ */ _(w, {
|
|
444
|
+
data: O,
|
|
445
|
+
onChange: (e, t) => B(k, e, t),
|
|
446
|
+
onSubmit: L,
|
|
447
|
+
onCancel: () => k(null),
|
|
433
448
|
submitLabel: "Save Changes"
|
|
434
449
|
})]
|
|
435
450
|
})
|
|
@@ -439,6 +454,6 @@ function w() {
|
|
|
439
454
|
});
|
|
440
455
|
}
|
|
441
456
|
//#endregion
|
|
442
|
-
export {
|
|
457
|
+
export { T as SavedAddressesPage };
|
|
443
458
|
|
|
444
459
|
//# sourceMappingURL=SavedAddressesPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SavedAddressesPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/customer/SavedAddressesPage.tsx"],"sourcesContent":["/**\n * Saved Addresses Page\n * @module adaptercloud/pages/customer\n *\n * Manage delivery addresses: list, add (inline expandable), edit (modal),\n * delete (with default protection), set default, usage count, last used date,\n * address nickname support. 4 mock addresses.\n */\n\nimport { useState } from'react';\nimport {\n MapPin,\n Plus,\n Pencil,\n Trash2,\n CheckCircle2,\n Home,\n Briefcase,\n Star,\n X,\n Hash,\n Clock,\n} from'lucide-react';\nimport { PageLayout } from'../../components/PageLayout';\nimport { EmptyState } from'../../components/EmptyState';\nimport { useAdapterCloud } from'../../context/AdapterCloudContext';\nimport { cn } from'../../utils/cn';\nimport { formatDate } from'../../utils/formatters';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface SavedAddress {\n id: string;\n type: 'home' | 'office' | 'other';\n nickname: string;\n addressLine1: string;\n addressLine2: string;\n city: string;\n state: string;\n zipCode: string;\n isDefault: boolean;\n usageCount: number;\n lastUsed?: string;\n}\n\ntype AddressFormData = Omit<SavedAddress, 'id' | 'usageCount' | 'lastUsed'>;\n\nconst emptyForm: AddressFormData = {\n type: 'home',\n nickname: '',\n addressLine1: '',\n addressLine2: '',\n city: '',\n state: '',\n zipCode: '',\n isDefault: false,\n};\n\n// ============================================================================\n// Mock Data\n// ============================================================================\n\nconst initialAddresses: SavedAddress[] = [\n {\n id: 'addr-001',\n type: 'home',\n nickname: 'Home Sweet Home',\n addressLine1: '123, MG Road',\n addressLine2: 'Near Central Mall',\n city: 'Bangalore',\n state: 'Karnataka',\n zipCode: '560001',\n isDefault: true,\n usageCount: 45,\n lastUsed: '2024-06-24T08:30:00Z',\n },\n {\n id: 'addr-002',\n type: 'office',\n nickname: 'Office',\n addressLine1: '456, Brigade Road',\n addressLine2: 'Floor 3, Tech Tower',\n city: 'Bangalore',\n state: 'Karnataka',\n zipCode: '560025',\n isDefault: false,\n usageCount: 28,\n lastUsed: '2024-06-23T13:15:00Z',\n },\n {\n id: 'addr-003',\n type: 'other',\n nickname: \"Friend's Place\",\n addressLine1: '789, Koramangala',\n addressLine2: 'Near Forum Mall',\n city: 'Bangalore',\n state: 'Karnataka',\n zipCode: '560034',\n isDefault: false,\n usageCount: 12,\n lastUsed: '2024-06-20T19:45:00Z',\n },\n {\n id: 'addr-004',\n type: 'home',\n nickname: 'Weekend Place',\n addressLine1: '321, Indiranagar',\n addressLine2: 'Near Metro Station',\n city: 'Bangalore',\n state: 'Karnataka',\n zipCode: '560038',\n isDefault: false,\n usageCount: 8,\n lastUsed: '2024-06-18T11:30:00Z',\n },\n];\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst iconMap = {\n home: Home,\n office: Briefcase,\n other: MapPin,\n};\n\nconst typeBadgeStyles: Record<string, string> = {\n home: 'bg-status-success-bg-subtle/10 text-status-success-text',\n office: 'bg-accent-blue-subtle/10 text-accent-blue',\n other: 'bg-accent-purple-subtle/10 text-accent-purple',\n};\n\n// ============================================================================\n// Address Form Component\n// ============================================================================\n\nfunction AddressForm({\n data,\n onChange,\n onSubmit,\n onCancel,\n submitLabel,\n}: {\n data: AddressFormData;\n onChange: (field: string, value: string | boolean) => void;\n onSubmit: () => void;\n onCancel: () => void;\n submitLabel: string;\n}) {\n return (\n <div className=\"space-y-4\">\n {/* Type + Nickname row */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Address Type <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n value={data.type}\n onChange={(e) => onChange('type', e.target.value)}\n className=\"w-full 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=\"home\">Home</option>\n <option value=\"office\">Office</option>\n <option value=\"other\">Other</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">Nickname</label>\n <input\n type=\"text\"\n placeholder=\"e.g., Home Sweet Home\"\n value={data.nickname}\n onChange={(e) => onChange('nickname', e.target.value)}\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)]\"\n />\n </div>\n </div>\n\n {/* Address Line 1 */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Address Line 1 <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"House/Flat number, Street name\"\n value={data.addressLine1}\n onChange={(e) => onChange('addressLine1', e.target.value)}\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)]\"\n />\n </div>\n\n {/* Address Line 2 */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">Address Line 2</label>\n <input\n type=\"text\"\n placeholder=\"Area, Landmark\"\n value={data.addressLine2}\n onChange={(e) => onChange('addressLine2', e.target.value)}\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)]\"\n />\n </div>\n\n {/* City / State / ZIP */}\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n City <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"City\"\n value={data.city}\n onChange={(e) => onChange('city', e.target.value)}\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)]\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n State <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"State\"\n value={data.state}\n onChange={(e) => onChange('state', e.target.value)}\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)]\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n ZIP Code <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"560001\"\n value={data.zipCode}\n onChange={(e) => onChange('zipCode', e.target.value)}\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)]\"\n />\n </div>\n </div>\n\n {/* Set Default checkbox */}\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={data.isDefault}\n onChange={(e) => onChange('isDefault', e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle text-text-primary\"\n />\n <span className=\"text-sm text-text-secondary\">Set as default address</span>\n </label>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={onSubmit}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n {submitLabel}\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport function SavedAddressesPage() {\n const { navigateTo } = useAdapterCloud();\n const [addresses, setAddresses] = useState<SavedAddress[]>(initialAddresses);\n const [showAddForm, setShowAddForm] = useState(false);\n const [editingAddress, setEditingAddress] = useState<SavedAddress | null>(null);\n const [newAddress, setNewAddress] = useState<AddressFormData>({ ...emptyForm });\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n\n // --- Helpers ---\n const showStatus = (msg: string) => {\n setStatusMessage(msg);\n setTimeout(() => setStatusMessage(null), 3000);\n };\n\n const validateForm = (data: AddressFormData): boolean => {\n if (\n !data.addressLine1.trim() ||\n !data.city.trim() ||\n !data.state.trim() ||\n !data.zipCode.trim()\n ) {\n alert('Please fill in all required fields (Address Line 1, City, State, ZIP Code).');\n return false;\n }\n return true;\n };\n\n // --- Handlers ---\n const handleAddAddress = () => {\n if (!validateForm(newAddress)) return;\n\n const addressToAdd: SavedAddress = {\n id: `addr-${Date.now()}`,\n type: newAddress.type,\n nickname: newAddress.nickname,\n addressLine1: newAddress.addressLine1,\n addressLine2: newAddress.addressLine2,\n city: newAddress.city,\n state: newAddress.state,\n zipCode: newAddress.zipCode,\n isDefault: newAddress.isDefault,\n usageCount: 0,\n };\n\n setAddresses((prev) => {\n let updated = [...prev];\n if (addressToAdd.isDefault) {\n updated = updated.map((a) => ({ ...a, isDefault: false }));\n }\n return [...updated, addressToAdd];\n });\n\n setNewAddress({ ...emptyForm });\n setShowAddForm(false);\n showStatus('Address added successfully');\n };\n\n const handleEditSave = () => {\n if (!editingAddress) return;\n if (!validateForm(editingAddress)) return;\n\n setAddresses((prev) =>\n prev.map((a) => {\n if (a.id === editingAddress.id) return editingAddress;\n if (editingAddress.isDefault) return { ...a, isDefault: false };\n return a;\n })\n );\n setEditingAddress(null);\n showStatus('Address updated successfully');\n };\n\n const handleDelete = (id: string) => {\n const addr = addresses.find((a) => a.id === id);\n if (addr?.isDefault) {\n alert('Cannot delete default address. Set another address as default first.');\n return;\n }\n setAddresses((prev) => prev.filter((a) => a.id !== id));\n showStatus('Address deleted successfully');\n };\n\n const handleSetDefault = (id: string) => {\n setAddresses((prev) => prev.map((a) => ({ ...a, isDefault: a.id === id })));\n showStatus('Default address updated');\n };\n\n const handleFormChange = (\n setter: React.Dispatch<React.SetStateAction<any>>,\n field: string,\n value: string | boolean\n ) => {\n setter((prev: any) => ({ ...prev, [field]: value }));\n };\n\n return (\n <PageLayout\n title=\"Saved Addresses\"\n description=\"Manage your delivery addresses for faster checkout\"\n actions={\n !showAddForm ? (\n <button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className={cn(\n 'flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-opacity',\n 'bg-foreground text-background hover:opacity-90'\n )}\n >\n <Plus className=\"h-4 w-4\" />\n Add New Address\n </button>\n ) : undefined\n }\n >\n {/* Status message */}\n {statusMessage && (\n <div className=\"mb-4 rounded-lg border border-status-success-border/30 bg-status-success-bg-subtle/10 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 {/* Inline Add Form */}\n {showAddForm && (\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-5 mb-6 bg-gloss-card shadow-pop\">\n <h3 className=\"text-base font-semibold text-text-primary mb-4\">Add New Address</h3>\n <AddressForm\n data={newAddress}\n onChange={(field, value) => handleFormChange(setNewAddress, field, value)}\n onSubmit={handleAddAddress}\n onCancel={() => {\n setShowAddForm(false);\n setNewAddress({ ...emptyForm });\n }}\n submitLabel=\"Save Address\"\n />\n </div>\n )}\n\n {/* Address List */}\n {addresses.length > 0 ? (\n <div className=\"space-y-3\">\n {addresses.map((address) => {\n const Icon = iconMap[address.type];\n return (\n <div\n key={address.id}\n className={cn(\n 'rounded-lg border bg-bg-surface p-4',\n address.isDefault ? 'border-foreground/30' : 'border-border-subtle'\n )}\n >\n <div className=\"flex items-start justify-between gap-3\">\n {/* Left: icon + details */}\n <div className=\"flex items-start gap-3 flex-1 min-w-0\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken\">\n <Icon className=\"h-5 w-5 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n {/* Badges row */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium capitalize',\n typeBadgeStyles[address.type]\n )}\n >\n {address.type}\n </span>\n {address.isDefault && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle/10 px-2 py-0.5 text-[10px] font-medium text-status-success-text\">\n <CheckCircle2 className=\"h-3 w-3\" />\n Default\n </span>\n )}\n </div>\n\n {/* Nickname */}\n {address.nickname && (\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">\n {address.nickname}\n </p>\n )}\n\n {/* Address lines */}\n <p className=\"mt-0.5 text-sm text-text-primary\">{address.addressLine1}</p>\n {address.addressLine2 && (\n <p className=\"text-sm text-text-secondary\">{address.addressLine2}</p>\n )}\n <p className=\"text-sm text-text-secondary\">\n {address.city}, {address.state} - {address.zipCode}\n </p>\n\n {/* Usage stats */}\n <div className=\"mt-2 flex flex-wrap items-center gap-3 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Hash className=\"h-3 w-3\" />\n Used {address.usageCount} times\n </span>\n {address.lastUsed && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3 w-3\" />\n Last used:{' '}\n {formatDate(address.lastUsed, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n )}\n </div>\n </div>\n </div>\n\n {/* Right: actions */}\n <div className=\"flex items-center gap-1 shrink-0\">\n {!address.isDefault && (\n <button\n type=\"button\"\n onClick={() => handleSetDefault(address.id)}\n title=\"Set as default\"\n className=\"rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <Star className=\"h-4 w-4\" />\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => setEditingAddress({ ...address })}\n title=\"Edit address\"\n className=\"rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleDelete(address.id)}\n title=\"Delete address\"\n className=\"rounded-md p-2 text-text-secondary hover:bg-status-error-bg/10 hover:text-status-error-text transition-colors\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n icon={<MapPin className=\"h-12 w-12\" />}\n title=\"No saved addresses\"\n description=\"Add a delivery address to get started.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Add Address\n </button>\n }\n />\n )}\n\n {/* Edit Address Modal */}\n {editingAddress && (\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-2xl max-h-[90vh] overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface shadow-lg\">\n <div className=\"p-5\">\n {/* Modal header */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Edit Address</h3>\n <button\n type=\"button\"\n onClick={() => setEditingAddress(null)}\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 <AddressForm\n data={editingAddress}\n onChange={(field, value) => handleFormChange(setEditingAddress, field, value)}\n onSubmit={handleEditSave}\n onCancel={() => setEditingAddress(null)}\n submitLabel=\"Save Changes\"\n />\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SavedAddressesPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/customer/SavedAddressesPage.tsx"],"sourcesContent":["/**\n * Saved Addresses Page\n * @module adaptercloud/pages/customer\n *\n * Manage delivery addresses: list, add (inline expandable), edit (modal),\n * delete (with default protection), set default, usage count, last used date,\n * address nickname support. 4 mock addresses.\n */\n\nimport { useState } from'react';\nimport {\n MapPin,\n Plus,\n Pencil,\n Trash2,\n CheckCircle2,\n Home,\n Briefcase,\n Star,\n X,\n Hash,\n Clock,\n} from'lucide-react';\nimport { EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from'../../components/PageLayout';\nimport { EmptyState } from'../../components/EmptyState';\nimport { useAdapterCloud } from'../../context/AdapterCloudContext';\nimport { cn } from'../../utils/cn';\nimport { formatDate } from'../../utils/formatters';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface SavedAddress {\n id: string;\n type: 'home' | 'office' | 'other';\n nickname: string;\n addressLine1: string;\n addressLine2: string;\n city: string;\n state: string;\n zipCode: string;\n isDefault: boolean;\n usageCount: number;\n lastUsed?: string;\n}\n\ntype AddressFormData = Omit<SavedAddress, 'id' | 'usageCount' | 'lastUsed'>;\n\nconst emptyForm: AddressFormData = {\n type: 'home',\n nickname: '',\n addressLine1: '',\n addressLine2: '',\n city: '',\n state: '',\n zipCode: '',\n isDefault: false,\n};\n\n// ============================================================================\n// Mock Data\n// ============================================================================\n\nconst initialAddresses: SavedAddress[] = [\n {\n id: 'addr-001',\n type: 'home',\n nickname: 'Home Sweet Home',\n addressLine1: '123, MG Road',\n addressLine2: 'Near Central Mall',\n city: 'Bangalore',\n state: 'Karnataka',\n zipCode: '560001',\n isDefault: true,\n usageCount: 45,\n lastUsed: '2024-06-24T08:30:00Z',\n },\n {\n id: 'addr-002',\n type: 'office',\n nickname: 'Office',\n addressLine1: '456, Brigade Road',\n addressLine2: 'Floor 3, Tech Tower',\n city: 'Bangalore',\n state: 'Karnataka',\n zipCode: '560025',\n isDefault: false,\n usageCount: 28,\n lastUsed: '2024-06-23T13:15:00Z',\n },\n {\n id: 'addr-003',\n type: 'other',\n nickname: \"Friend's Place\",\n addressLine1: '789, Koramangala',\n addressLine2: 'Near Forum Mall',\n city: 'Bangalore',\n state: 'Karnataka',\n zipCode: '560034',\n isDefault: false,\n usageCount: 12,\n lastUsed: '2024-06-20T19:45:00Z',\n },\n {\n id: 'addr-004',\n type: 'home',\n nickname: 'Weekend Place',\n addressLine1: '321, Indiranagar',\n addressLine2: 'Near Metro Station',\n city: 'Bangalore',\n state: 'Karnataka',\n zipCode: '560038',\n isDefault: false,\n usageCount: 8,\n lastUsed: '2024-06-18T11:30:00Z',\n },\n];\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst iconMap = {\n home: Home,\n office: Briefcase,\n other: MapPin,\n};\n\nconst typeBadgeStyles: Record<string, string> = {\n home: 'bg-status-success-bg-subtle/10 text-status-success-text',\n office: 'bg-accent-blue-subtle/10 text-accent-blue',\n other: 'bg-accent-purple-subtle/10 text-accent-purple',\n};\n\n// ============================================================================\n// Address Form Component\n// ============================================================================\n\nfunction AddressForm({\n data,\n onChange,\n onSubmit,\n onCancel,\n submitLabel,\n}: {\n data: AddressFormData;\n onChange: (field: string, value: string | boolean) => void;\n onSubmit: () => void;\n onCancel: () => void;\n submitLabel: string;\n}) {\n return (\n <div className=\"space-y-4\">\n {/* Type + Nickname row */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Address Type <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n value={data.type}\n onChange={(e) => onChange('type', e.target.value)}\n className=\"w-full 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=\"home\">Home</option>\n <option value=\"office\">Office</option>\n <option value=\"other\">Other</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">Nickname</label>\n <input\n type=\"text\"\n placeholder=\"e.g., Home Sweet Home\"\n value={data.nickname}\n onChange={(e) => onChange('nickname', e.target.value)}\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)]\"\n />\n </div>\n </div>\n\n {/* Address Line 1 */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Address Line 1 <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"House/Flat number, Street name\"\n value={data.addressLine1}\n onChange={(e) => onChange('addressLine1', e.target.value)}\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)]\"\n />\n </div>\n\n {/* Address Line 2 */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">Address Line 2</label>\n <input\n type=\"text\"\n placeholder=\"Area, Landmark\"\n value={data.addressLine2}\n onChange={(e) => onChange('addressLine2', e.target.value)}\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)]\"\n />\n </div>\n\n {/* City / State / ZIP */}\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n City <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"City\"\n value={data.city}\n onChange={(e) => onChange('city', e.target.value)}\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)]\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n State <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"State\"\n value={data.state}\n onChange={(e) => onChange('state', e.target.value)}\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)]\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n ZIP Code <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"560001\"\n value={data.zipCode}\n onChange={(e) => onChange('zipCode', e.target.value)}\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)]\"\n />\n </div>\n </div>\n\n {/* Set Default checkbox */}\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={data.isDefault}\n onChange={(e) => onChange('isDefault', e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle text-text-primary\"\n />\n <span className=\"text-sm text-text-secondary\">Set as default address</span>\n </label>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={onSubmit}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n {submitLabel}\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport function SavedAddressesPage() {\n const { navigateTo } = useAdapterCloud();\n const [addresses, setAddresses] = useState<SavedAddress[]>(initialAddresses);\n const [showAddForm, setShowAddForm] = useState(false);\n const [editingAddress, setEditingAddress] = useState<SavedAddress | null>(null);\n const [newAddress, setNewAddress] = useState<AddressFormData>({ ...emptyForm });\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n\n // --- Helpers ---\n const showStatus = (msg: string) => {\n setStatusMessage(msg);\n setTimeout(() => setStatusMessage(null), 3000);\n };\n\n const validateForm = (data: AddressFormData): boolean => {\n if (\n !data.addressLine1.trim() ||\n !data.city.trim() ||\n !data.state.trim() ||\n !data.zipCode.trim()\n ) {\n alert('Please fill in all required fields (Address Line 1, City, State, ZIP Code).');\n return false;\n }\n return true;\n };\n\n // --- Handlers ---\n const handleAddAddress = () => {\n if (!validateForm(newAddress)) return;\n\n const addressToAdd: SavedAddress = {\n id: `addr-${Date.now()}`,\n type: newAddress.type,\n nickname: newAddress.nickname,\n addressLine1: newAddress.addressLine1,\n addressLine2: newAddress.addressLine2,\n city: newAddress.city,\n state: newAddress.state,\n zipCode: newAddress.zipCode,\n isDefault: newAddress.isDefault,\n usageCount: 0,\n };\n\n setAddresses((prev) => {\n let updated = [...prev];\n if (addressToAdd.isDefault) {\n updated = updated.map((a) => ({ ...a, isDefault: false }));\n }\n return [...updated, addressToAdd];\n });\n\n setNewAddress({ ...emptyForm });\n setShowAddForm(false);\n showStatus('Address added successfully');\n };\n\n const handleEditSave = () => {\n if (!editingAddress) return;\n if (!validateForm(editingAddress)) return;\n\n setAddresses((prev) =>\n prev.map((a) => {\n if (a.id === editingAddress.id) return editingAddress;\n if (editingAddress.isDefault) return { ...a, isDefault: false };\n return a;\n })\n );\n setEditingAddress(null);\n showStatus('Address updated successfully');\n };\n\n const handleDelete = (id: string) => {\n const addr = addresses.find((a) => a.id === id);\n if (addr?.isDefault) {\n alert('Cannot delete default address. Set another address as default first.');\n return;\n }\n setAddresses((prev) => prev.filter((a) => a.id !== id));\n showStatus('Address deleted successfully');\n };\n\n const handleSetDefault = (id: string) => {\n setAddresses((prev) => prev.map((a) => ({ ...a, isDefault: a.id === id })));\n showStatus('Default address updated');\n };\n\n const handleFormChange = (\n setter: React.Dispatch<React.SetStateAction<any>>,\n field: string,\n value: string | boolean\n ) => {\n setter((prev: any) => ({ ...prev, [field]: value }));\n };\n\n return (\n <PageLayout\n title=\"Saved Addresses\"\n description=\"Manage your delivery addresses for faster checkout\"\n purpose=\"Store the places you order to — home, work, a friend's flat — so you pick a delivery address at checkout instead of typing it each time. Mark a default and your most common address is selected automatically.\"\n actions={\n !showAddForm ? (\n <button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className={cn(\n 'flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-opacity',\n 'bg-foreground text-background hover:opacity-90'\n )}\n >\n <Plus className=\"h-4 w-4\" />\n Add New Address\n </button>\n ) : undefined\n }\n >\n {/* Status message */}\n {statusMessage && (\n <div className=\"mb-4 rounded-lg border border-status-success-border/30 bg-status-success-bg-subtle/10 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 {/* Faster-checkout assurance (emphasis zone) */}\n <EmphasisPanel className=\"mb-6 flex items-start gap-3 p-4\">\n <MapPin className=\"h-5 w-5 shrink-0 text-text-secondary\" aria-hidden=\"true\" />\n <div>\n <h2 className=\"text-sm font-semibold text-text-primary\">Save once, checkout faster</h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Add the places you order to and pick one at checkout instead of typing it each time. Mark\n a default to have your most-used address selected automatically.\n </p>\n </div>\n </EmphasisPanel>\n\n {/* Inline Add Form */}\n {showAddForm && (\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-5 mb-6 bg-gloss-card shadow-pop\">\n <h3 className=\"text-base font-semibold text-text-primary mb-4\">Add New Address</h3>\n <AddressForm\n data={newAddress}\n onChange={(field, value) => handleFormChange(setNewAddress, field, value)}\n onSubmit={handleAddAddress}\n onCancel={() => {\n setShowAddForm(false);\n setNewAddress({ ...emptyForm });\n }}\n submitLabel=\"Save Address\"\n />\n </div>\n )}\n\n {/* Address List */}\n {addresses.length > 0 ? (\n <div className=\"space-y-3\">\n {addresses.map((address) => {\n const Icon = iconMap[address.type];\n return (\n <div\n key={address.id}\n className={cn(\n 'rounded-lg border bg-bg-surface p-4',\n address.isDefault ? 'border-foreground/30' : 'border-border-subtle'\n )}\n >\n <div className=\"flex items-start justify-between gap-3\">\n {/* Left: icon + details */}\n <div className=\"flex items-start gap-3 flex-1 min-w-0\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken\">\n <Icon className=\"h-5 w-5 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n {/* Badges row */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium capitalize',\n typeBadgeStyles[address.type]\n )}\n >\n {address.type}\n </span>\n {address.isDefault && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle/10 px-2 py-0.5 text-[10px] font-medium text-status-success-text\">\n <CheckCircle2 className=\"h-3 w-3\" />\n Default\n </span>\n )}\n </div>\n\n {/* Nickname */}\n {address.nickname && (\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">\n {address.nickname}\n </p>\n )}\n\n {/* Address lines */}\n <p className=\"mt-0.5 text-sm text-text-primary\">{address.addressLine1}</p>\n {address.addressLine2 && (\n <p className=\"text-sm text-text-secondary\">{address.addressLine2}</p>\n )}\n <p className=\"text-sm text-text-secondary\">\n {address.city}, {address.state} - {address.zipCode}\n </p>\n\n {/* Usage stats */}\n <div className=\"mt-2 flex flex-wrap items-center gap-3 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Hash className=\"h-3 w-3\" />\n Used {address.usageCount} times\n </span>\n {address.lastUsed && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3 w-3\" />\n Last used:{' '}\n {formatDate(address.lastUsed, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n )}\n </div>\n </div>\n </div>\n\n {/* Right: actions */}\n <div className=\"flex items-center gap-1 shrink-0\">\n {!address.isDefault && (\n <button\n type=\"button\"\n onClick={() => handleSetDefault(address.id)}\n title=\"Set as default\"\n className=\"rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <Star className=\"h-4 w-4\" />\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => setEditingAddress({ ...address })}\n title=\"Edit address\"\n className=\"rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleDelete(address.id)}\n title=\"Delete address\"\n className=\"rounded-md p-2 text-text-secondary hover:bg-status-error-bg/10 hover:text-status-error-text transition-colors\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n icon={<MapPin className=\"h-12 w-12\" />}\n title=\"No saved addresses\"\n description=\"Add a delivery address to get started.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Add Address\n </button>\n }\n />\n )}\n\n {/* Edit Address Modal */}\n {editingAddress && (\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-2xl max-h-[90vh] overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface shadow-lg\">\n <div className=\"p-5\">\n {/* Modal header */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Edit Address</h3>\n <button\n type=\"button\"\n onClick={() => setEditingAddress(null)}\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 <AddressForm\n data={editingAddress}\n onChange={(field, value) => handleFormChange(setEditingAddress, field, value)}\n onSubmit={handleEditSave}\n onCancel={() => setEditingAddress(null)}\n submitLabel=\"Save Changes\"\n />\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAkDA,IAAM,IAA6B;CACjC,MAAM;CACN,UAAU;CACV,cAAc;CACd,cAAc;CACd,MAAM;CACN,OAAO;CACP,SAAS;CACT,WAAW;CACZ,EAMK,IAAmC;CACvC;EACE,IAAI;EACJ,MAAM;EACN,UAAU;EACV,cAAc;EACd,cAAc;EACd,MAAM;EACN,OAAO;EACP,SAAS;EACT,WAAW;EACX,YAAY;EACZ,UAAU;EACX;CACD;EACE,IAAI;EACJ,MAAM;EACN,UAAU;EACV,cAAc;EACd,cAAc;EACd,MAAM;EACN,OAAO;EACP,SAAS;EACT,WAAW;EACX,YAAY;EACZ,UAAU;EACX;CACD;EACE,IAAI;EACJ,MAAM;EACN,UAAU;EACV,cAAc;EACd,cAAc;EACd,MAAM;EACN,OAAO;EACP,SAAS;EACT,WAAW;EACX,YAAY;EACZ,UAAU;EACX;CACD;EACE,IAAI;EACJ,MAAM;EACN,UAAU;EACV,cAAc;EACd,cAAc;EACd,MAAM;EACN,OAAO;EACP,SAAS;EACT,WAAW;EACX,YAAY;EACZ,UAAU;EACX;CACF,EAMK,IAAU;CACd,MAAM;CACN,QAAQ;CACR,OAAO;CACR,EAEK,IAA0C;CAC9C,MAAM;CACN,QAAQ;CACR,OAAO;CACR;AAMD,SAAS,EAAY,EACnB,SACA,aACA,aACA,aACA,kBAOC;AACD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,WAAU;eAAjB,CAAsE,iBACvD,kBAAC,QAAD;MAAM,WAAU;gBAAyB;MAAQ,CAAA,CACxD;QACR,kBAAC,UAAD;KACE,OAAO,EAAK;KACZ,WAAW,MAAM,EAAS,QAAQ,EAAE,OAAO,MAAM;KACjD,WAAU;eAHZ;MAKE,kBAAC,UAAD;OAAQ,OAAM;iBAAO;OAAa,CAAA;MAClC,kBAAC,UAAD;OAAQ,OAAM;iBAAS;OAAe,CAAA;MACtC,kBAAC,UAAD;OAAQ,OAAM;iBAAQ;OAAc,CAAA;MAC7B;OACL,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,WAAU;eAAqD;KAAgB,CAAA,EACtF,kBAAC,SAAD;KACE,MAAK;KACL,aAAY;KACZ,OAAO,EAAK;KACZ,WAAW,MAAM,EAAS,YAAY,EAAE,OAAO,MAAM;KACrD,WAAU;KACV,CAAA,CACE,EAAA,CAAA,CACF;;GAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IAAO,WAAU;cAAjB,CAAsE,mBACrD,kBAAC,QAAD;KAAM,WAAU;eAAyB;KAAQ,CAAA,CAC1D;OACR,kBAAC,SAAD;IACE,MAAK;IACL,aAAY;IACZ,OAAO,EAAK;IACZ,WAAW,MAAM,EAAS,gBAAgB,EAAE,OAAO,MAAM;IACzD,WAAU;IACV,CAAA,CACE,EAAA,CAAA;GAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IAAO,WAAU;cAAqD;IAAsB,CAAA,EAC5F,kBAAC,SAAD;IACE,MAAK;IACL,aAAY;IACZ,OAAO,EAAK;IACZ,WAAW,MAAM,EAAS,gBAAgB,EAAE,OAAO,MAAM;IACzD,WAAU;IACV,CAAA,CACE,EAAA,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CAAsE,SAC/D,kBAAC,QAAD;OAAM,WAAU;iBAAyB;OAAQ,CAAA,CAChD;SACR,kBAAC,SAAD;MACE,MAAK;MACL,aAAY;MACZ,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,QAAQ,EAAE,OAAO,MAAM;MACjD,WAAU;MACV,CAAA,CACE,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CAAsE,UAC9D,kBAAC,QAAD;OAAM,WAAU;iBAAyB;OAAQ,CAAA,CACjD;SACR,kBAAC,SAAD;MACE,MAAK;MACL,aAAY;MACZ,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,SAAS,EAAE,OAAO,MAAM;MAClD,WAAU;MACV,CAAA,CACE,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CAAsE,aAC3D,kBAAC,QAAD;OAAM,WAAU;iBAAyB;OAAQ,CAAA,CACpD;SACR,kBAAC,SAAD;MACE,MAAK;MACL,aAAY;MACZ,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,WAAW,EAAE,OAAO,MAAM;MACpD,WAAU;MACV,CAAA,CACE,EAAA,CAAA;KACF;;GAGN,kBAAC,SAAD;IAAO,WAAU;cAAjB,CACE,kBAAC,SAAD;KACE,MAAK;KACL,SAAS,EAAK;KACd,WAAW,MAAM,EAAS,aAAa,EAAE,OAAO,QAAQ;KACxD,WAAU;KACV,CAAA,EACF,kBAAC,QAAD;KAAM,WAAU;eAA8B;KAA6B,CAAA,CACrE;;GAGR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET;KACM,CAAA,EACT,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eACX;KAEQ,CAAA,CACL;;GACF;;;AAQV,SAAgB,IAAqB;CACnC,IAAM,EAAE,kBAAe,GAAiB,EAClC,CAAC,GAAW,KAAgB,EAAyB,EAAiB,EACtE,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAgB,KAAqB,EAA8B,KAAK,EACzE,CAAC,GAAY,KAAiB,EAA0B,EAAE,GAAG,GAAW,CAAC,EACzE,CAAC,GAAe,KAAoB,EAAwB,KAAK,EAGjE,KAAc,MAAgB;AAElC,EADA,EAAiB,EAAI,EACrB,iBAAiB,EAAiB,KAAK,EAAE,IAAK;IAG1C,KAAgB,MAElB,CAAC,EAAK,aAAa,MAAM,IACzB,CAAC,EAAK,KAAK,MAAM,IACjB,CAAC,EAAK,MAAM,MAAM,IAClB,CAAC,EAAK,QAAQ,MAAM,IAEpB,MAAM,8EAA8E,EAC7E,MAEF,IAIH,UAAyB;AAC7B,MAAI,CAAC,EAAa,EAAW,CAAE;EAE/B,IAAM,IAA6B;GACjC,IAAI,QAAQ,KAAK,KAAK;GACtB,MAAM,EAAW;GACjB,UAAU,EAAW;GACrB,cAAc,EAAW;GACzB,cAAc,EAAW;GACzB,MAAM,EAAW;GACjB,OAAO,EAAW;GAClB,SAAS,EAAW;GACpB,WAAW,EAAW;GACtB,YAAY;GACb;AAYD,EAVA,GAAc,MAAS;GACrB,IAAI,IAAU,CAAC,GAAG,EAAK;AAIvB,UAHI,EAAa,cACf,IAAU,EAAQ,KAAK,OAAO;IAAE,GAAG;IAAG,WAAW;IAAO,EAAE,GAErD,CAAC,GAAG,GAAS,EAAa;IACjC,EAEF,EAAc,EAAE,GAAG,GAAW,CAAC,EAC/B,EAAe,GAAM,EACrB,EAAW,6BAA6B;IAGpC,UAAuB;AACtB,OACA,EAAa,EAAe,KAEjC,GAAc,MACZ,EAAK,KAAK,MACJ,EAAE,OAAO,EAAe,KAAW,IACnC,EAAe,YAAkB;GAAE,GAAG;GAAG,WAAW;GAAO,GACxD,EACP,CACH,EACD,EAAkB,KAAK,EACvB,EAAW,+BAA+B;IAGtC,KAAgB,MAAe;AAEnC,MADa,EAAU,MAAM,MAAM,EAAE,OAAO,EAAG,EACrC,WAAW;AACnB,SAAM,uEAAuE;AAC7E;;AAGF,EADA,GAAc,MAAS,EAAK,QAAQ,MAAM,EAAE,OAAO,EAAG,CAAC,EACvD,EAAW,+BAA+B;IAGtC,KAAoB,MAAe;AAEvC,EADA,GAAc,MAAS,EAAK,KAAK,OAAO;GAAE,GAAG;GAAG,WAAW,EAAE,OAAO;GAAI,EAAE,CAAC,EAC3E,EAAW,0BAA0B;IAGjC,KACJ,GACA,GACA,MACG;AACH,KAAQ,OAAe;GAAE,GAAG;IAAO,IAAQ;GAAO,EAAE;;AAGtD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACG,IAYG,KAAA,IAXF,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAe,GAAK;GACnC,WAAW,EACT,uFACA,iDACD;aANH,CAQE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,kBAErB;;YAhBf;GAqBG,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAc,WAAU,6CAA8C,CAAA,EACtE,kBAAC,KAAD;KAAG,WAAU;eAAoC;KAAkB,CAAA,CAC/D;;GAIR,kBAAC,GAAD;IAAe,WAAU;cAAzB,CACE,kBAAC,GAAD;KAAQ,WAAU;KAAuC,eAAY;KAAS,CAAA,EAC9E,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;KAA+B,CAAA,EACvF,kBAAC,KAAD;KAAG,WAAU;eAAqC;KAG9C,CAAA,CACA,EAAA,CAAA,CACQ;;GAGf,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAAiD;KAAoB,CAAA,EACnF,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,GAAO,MAAU,EAAiB,GAAe,GAAO,EAAM;KACzE,UAAU;KACV,gBAAgB;AAEd,MADA,EAAe,GAAM,EACrB,EAAc,EAAE,GAAG,GAAW,CAAC;;KAEjC,aAAY;KACZ,CAAA,CACE;;GAIP,EAAU,SAAS,IAClB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAU,KAAK,MAAY;KAC1B,IAAM,IAAO,EAAQ,EAAQ;AAC7B,YACE,kBAAC,OAAD;MAEE,WAAW,EACT,uCACA,EAAQ,YAAY,yBAAyB,uBAC9C;gBAED,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAEE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAM,WAAU,+BAAgC,CAAA;SAC5C,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UAEE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,QAAD;YACE,WAAW,EACT,wFACA,EAAgB,EAAQ,MACzB;sBAEA,EAAQ;YACJ,CAAA,EACN,EAAQ,aACP,kBAAC,QAAD;YAAM,WAAU;sBAAhB,CACE,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,EAAA,UAE/B;cAEL;;UAGL,EAAQ,YACP,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAQ;WACP,CAAA;UAIN,kBAAC,KAAD;WAAG,WAAU;qBAAoC,EAAQ;WAAiB,CAAA;UACzE,EAAQ,gBACP,kBAAC,KAAD;WAAG,WAAU;qBAA+B,EAAQ;WAAiB,CAAA;UAEvE,kBAAC,KAAD;WAAG,WAAU;qBAAb;YACG,EAAQ;YAAK;YAAG,EAAQ;YAAM;YAAI,EAAQ;YACzC;;UAGJ,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,QAAD;YAAM,WAAU;sBAAhB;aACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;;aACtB,EAAQ;aAAW;aACpB;eACN,EAAQ,YACP,kBAAC,QAAD;YAAM,WAAU;sBAAhB;aACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;;aAClB;aACV,EAAW,EAAQ,UAAU;cAC5B,MAAM,KAAA;cACN,QAAQ,KAAA;cACR,QAAQ,KAAA;cACT,CAAC;aACG;cAEL;;UACF;WACF;WAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACG,CAAC,EAAQ,aACR,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAiB,EAAQ,GAAG;UAC3C,OAAM;UACN,WAAU;oBAEV,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;UACrB,CAAA;SAEX,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAkB,EAAE,GAAG,GAAS,CAAC;UAChD,OAAM;UACN,WAAU;oBAEV,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;UACvB,CAAA;SACT,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAa,EAAQ,GAAG;UACvC,OAAM;UACN,WAAU;oBAEV,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;UACvB,CAAA;SACL;UACF;;MACF,EAlGC,EAAQ,GAkGT;MAER;IACE,CAAA,GAEN,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;IACtC,OAAM;IACN,aAAY;IACZ,QACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAe,GAAK;KACnC,WAAU;eACX;KAEQ,CAAA;IAEX,CAAA;GAIH,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CAEE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA0C;QAAiB,CAAA,EACzE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAkB,KAAK;QACtC,WAAU;kBAEV,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;QAClB,CAAA,CACL;UAEN,kBAAC,GAAD;OACE,MAAM;OACN,WAAW,GAAO,MAAU,EAAiB,GAAmB,GAAO,EAAM;OAC7E,UAAU;OACV,gBAAgB,EAAkB,KAAK;OACvC,aAAY;OACZ,CAAA,CACE;;KACF,CAAA;IACF,CAAA;GAEG"}
|