From 0293655fc1292a0090ef96079444328f662786f3 Mon Sep 17 00:00:00 2001 From: abolfazl Date: Wed, 29 Apr 2026 01:44:16 +0330 Subject: [PATCH] first --- .dockerignore | 26 + .env.development | 3 + .env.example | 23 + .env.production | 21 + .eslintrc.cjs | 81 + .gitignore | 111 + DEPLOYMENT.md | 190 + Dockerfile | 53 + Dockerfile.dev | 20 + app/assets/data/consts.tsx | 390 + app/assets/icons/cart/basket.svg | 4 + app/assets/icons/cart/failure_pay.svg | 5 + app/assets/icons/cart/sucess_pay.svg | 3 + app/assets/icons/filter-dark.png | Bin 0 -> 578 bytes app/assets/icons/filter.png | Bin 0 -> 677 bytes app/assets/icons/login/verify-banner.svg | 4 + app/assets/icons/navbar/buy-filled.svg | 3 + app/assets/icons/navbar/buy-outline.svg | 3 + app/assets/icons/navbar/document-filled.svg | 3 + app/assets/icons/navbar/document-outline.svg | 3 + app/assets/icons/navbar/home-filled.svg | 3 + app/assets/icons/navbar/home-outline.svg | 3 + app/assets/icons/navbar/products-filled.svg | 3 + app/assets/icons/navbar/products-outline.svg | 3 + app/assets/icons/navbar/profile-filled.svg | 4 + app/assets/icons/navbar/profile-outline.svg | 4 + app/assets/icons/navbar/search-filled.svg | 3 + app/assets/icons/navbar/search-outline.svg | 4 + app/assets/icons/navbar/setting-filled.svg | 3 + app/assets/icons/navbar/setting-outline.svg | 4 + app/assets/icons/product-dark.png | Bin 0 -> 1540 bytes app/assets/icons/product.png | Bin 0 -> 1476 bytes app/assets/icons/product/discount-red.svg | 3 + app/assets/icons/product/longArrow.svg | 3 + app/assets/icons/product/tomanBlack.svg | 3 + app/assets/icons/product/tomanGray.svg | 3 + .../icons/profile/bookmark/bookmark.svg | 4 + .../icons/profile/location/location.svg | 4 + app/assets/icons/profile/orders/paper.svg | 5 + .../icons/profile/threads/messageBox.svg | 4 + app/assets/icons/sellerPlaceholder-dark.svg | 4 + app/assets/icons/sellerPlaceholder.svg | 4 + app/assets/logo/SVG-01.svg | 26 + app/assets/logo/SVG-02.svg | 26 + app/assets/logo/SVG-03.svg | 17 + app/assets/logo/SVG-04.svg | 21 + app/assets/logo/SVG-05.svg | 34 + app/assets/logo/SVG-06.svg | 42 + app/assets/logo/SVG-07.svg | 3 + app/auth.server.ts | 21 + app/components/AIPromoteModal.tsx | 585 + app/components/AppInput.tsx | 77 + app/components/AppSelectBox.tsx | 112 + app/components/AppTextArea.tsx | 39 + app/components/BankLogo.tsx | 393 + app/components/CallDialog.tsx | 57 + app/components/ClientOnly.tsx | 16 + app/components/FilterDrawer.tsx | 276 + app/components/HeaderWithSupport.tsx | 70 + app/components/HorizontalCollectionsList.tsx | 278 + app/components/HorizontalProductList.tsx | 289 + app/components/InteractiveMap.tsx | 1137 ++ app/components/LoginRequiredDialog.tsx | 298 + app/components/MondrianProductList.tsx | 600 + app/components/MyLayout.tsx | 355 + app/components/PersianDatePicker.tsx | 205 + app/components/ProfilePagesHeader.tsx | 38 + app/components/SearchTopBar.tsx | 69 + app/components/SellerLogo.tsx | 76 + app/components/SellerTilesGrid.tsx | 101 + app/components/SunMoonToggle.tsx | 99 + app/components/ThemeToggle.css | 19 + app/components/ThemeToggle.tsx | 49 + app/components/UiProvider.tsx | 206 + app/components/cart/CartHeader.tsx | 74 + app/components/cart/CartItem.tsx | 178 + app/components/cart/CartSummary.tsx | 37 + app/components/cart/QuantityControl.tsx | 190 + app/components/cart/ReceiverInformation.tsx | 228 + app/components/cart/ShippingInformation.tsx | 440 + .../cart/ThrottledQuantityControl.tsx | 133 + app/components/explore/ExploreTopBar.tsx | 83 + app/components/explore/MainSection.tsx | 126 + app/components/explore/SearchSection.tsx | 248 + app/components/explore/TabContainer.tsx | 35 + app/components/home/HomePageTopBar.tsx | 108 + app/components/home/InvoiceModal.tsx | 166 + app/components/product/BottomBar.tsx | 223 + app/components/product/ColorSelector.tsx | 38 + app/components/product/ContactButton.tsx | 49 + .../product/InactiveProductView.tsx | 57 + app/components/product/ProductActions.tsx | 336 + app/components/product/ProductDetailView.tsx | 347 + app/components/product/ProductHeader.tsx | 82 + .../product/ProductImageCarousel.tsx | 449 + app/components/product/ProductInfo.tsx | 30 + app/components/product/Review.tsx | 193 + app/components/product/ReviewSection.tsx | 413 + app/components/product/SizeSelector.tsx | 33 + app/components/product/index.ts | 11 + app/components/sets/AddToSetDrawer.tsx | 209 + app/components/sets/CreateSetModal.tsx | 131 + app/components/sets/SetSuccessModal.tsx | 69 + app/components/sets/index.ts | 3 + app/components/store/ColorSelector.tsx | 139 + app/components/store/ImageEditor.tsx | 500 + app/components/store/ImageUpload.tsx | 97 + app/components/store/ImageUploadDrawer.tsx | 67 + app/components/store/ImageUploadSection.tsx | 138 + app/components/store/PricingForm.tsx | 187 + app/components/store/ProductDetailsForm.tsx | 39 + app/components/store/ProductVariantDrawer.tsx | 535 + .../store/ProductVariantsSection.tsx | 167 + app/components/store/SizeSelector.tsx | 103 + app/components/store/StoreForm.tsx | 838 + app/components/thread/ChatHeader.tsx | 60 + app/components/thread/EmojiPicker.tsx | 80 + app/components/thread/MessageBubble.tsx | 214 + app/components/thread/MessageInput.tsx | 173 + app/components/thread/MessageList.tsx | 200 + app/components/thread/ThreadChatPage.tsx | 287 + app/components/thread/ThreadsPage.tsx | 156 + app/components/ui/FormField.tsx | 26 + app/components/ui/button.tsx | 59 + app/components/ui/card.tsx | 83 + app/components/ui/carousel.tsx | 280 + app/components/ui/command.tsx | 152 + app/components/ui/dialog.tsx | 124 + app/components/ui/drawer.tsx | 115 + app/components/ui/dropdown-menu.tsx | 198 + app/components/ui/input-otp.tsx | 74 + app/components/ui/input.tsx | 69 + app/components/ui/label.tsx | 23 + app/components/ui/popover.tsx | 31 + app/components/ui/radio-group.tsx | 44 + app/components/ui/select.tsx | 158 + app/components/ui/skeleton.tsx | 15 + app/components/ui/slider.tsx | 32 + app/components/ui/textarea.tsx | 22 + app/components/ui/toast.tsx | 127 + app/components/ui/toaster.tsx | 33 + app/cookie.ts | 11 + app/data/iranian-cities.ts | 484 + app/entry.client.tsx | 31 + app/entry.server.tsx | 140 + app/hooks/use-prefetch.ts | 54 + app/hooks/use-root-data.ts | 26 + app/hooks/use-toast.ts | 188 + app/hooks/useCarousel.ts | 26 + app/hooks/useDebounce.ts | 25 + app/hooks/useStoreOwnership.ts | 46 + app/hooks/useWebSocket.ts | 183 + app/i18n.ts | 32 + app/lib/utils.ts | 6 + app/requestHandler/use-address-hooks.tsx | 168 + app/requestHandler/use-auth-hooks.tsx | 63 + app/requestHandler/use-bookmark-hooks.tsx | 92 + app/requestHandler/use-cart-hooks.ts | 256 + app/requestHandler/use-chat-hooks.tsx | 202 + app/requestHandler/use-collection-hooks.tsx | 88 + app/requestHandler/use-comments-hooks.tsx | 90 + app/requestHandler/use-courier-hooks.tsx | 134 + app/requestHandler/use-discount-hooks.ts | 59 + app/requestHandler/use-explore-hooks.tsx | 48 + app/requestHandler/use-following-hooks.tsx | 25 + app/requestHandler/use-home-hooks.ts | 228 + app/requestHandler/use-instagram-hooks.ts | 168 + app/requestHandler/use-media-hooks.ts | 54 + app/requestHandler/use-order-hooks.ts | 375 + app/requestHandler/use-product-hooks.ts | 428 + app/requestHandler/use-profile-hooks.tsx | 35 + app/requestHandler/use-search-hooks.ts | 121 + app/requestHandler/use-seller-hooks.ts | 288 + app/requestHandler/use-shipping-hooks.ts | 30 + .../use-user-collection-hooks.tsx | 243 + app/requestHandler/use-wallet-hooks.tsx | 216 + app/root.tsx | 187 + app/routes/$.tsx | 119 + app/routes/_index.tsx | 283 + app/routes/cart.$cartId.tsx | 326 + app/routes/cart._index.tsx | 217 + app/routes/collection.$id.tsx | 335 + app/routes/collections.tsx | 259 + app/routes/explore.tsx | 56 + app/routes/login.tsx | 329 + app/routes/logout.tsx | 49 + app/routes/product.$id.tsx | 282 + app/routes/profile._index.tsx | 429 + app/routes/profile.about.tsx | 209 + app/routes/profile.bookmarks.tsx | 87 + app/routes/profile.faq.tsx | 177 + app/routes/profile.following.tsx | 84 + app/routes/profile.location._index.tsx | 290 + app/routes/profile.location.add.tsx | 126 + app/routes/profile.location.edit.$id.tsx | 110 + app/routes/profile.orders.tsx | 538 + app/routes/profile.sets.$id.tsx | 677 + app/routes/profile.sets._index.tsx | 288 + app/routes/profile.setting.tsx | 236 + app/routes/profile.threads.$threadId.tsx | 46 + app/routes/profile.threads._index.tsx | 36 + app/routes/profile.wallet._index.tsx | 209 + app/routes/profile.wallet.payment.tsx | 63 + .../resources.manifest[.]webmanifest.ts | 114 + app/routes/robots[.]txt.tsx | 28 + app/routes/search.$query.tsx | 103 + app/routes/seller.$sellerId._index.tsx | 464 + app/routes/sellers.tsx | 140 + app/routes/sitemap[.]xml.tsx | 104 + app/routes/store.$storeId._index.tsx | 604 + app/routes/store.$storeId.edit.tsx | 23 + ...re.$storeId.financial-dashboard._index.tsx | 101 + ...storeId.financial-dashboard.cash-funds.tsx | 811 + ...e.$storeId.financial-dashboard.reports.tsx | 165 + ...oreId.financial-dashboard.transactions.tsx | 422 + app/routes/store.$storeId.orders.$orderId.tsx | 941 + app/routes/store.$storeId.orders._index.tsx | 317 + app/routes/store.$storeId.product.$id.tsx | 99 + app/routes/store.$storeId.products.tsx | 256 + app/routes/store.$storeId.setting.tsx | 258 + app/routes/store.$storeId.shipping-method.tsx | 531 + .../store.$storeId.threads.$threadId.tsx | 46 + app/routes/store.$storeId.threads._index.tsx | 38 + app/routes/store.add.manual.$storeId.tsx | 975 + .../store.authenticate.instagram.$storeId.tsx | 193 + app/routes/store.create.tsx | 21 + app/sessions.server.ts | 34 + app/styles/global.css | 56 + app/tailwind.css | 300 + app/types/moment-jalaali.d.ts | 1 + app/types/theme.ts | 1 + app/utils/RequestHandler.tsx | 7 + app/utils/api-client-factory.ts | 168 + app/utils/api-config.ts | 178 + app/utils/api-error-handler.ts | 36 + app/utils/get-env.ts | 25 + app/utils/helpers.ts | 294 + app/utils/imageUrl.ts | 8 + app/utils/iranian-banks.ts | 91 + app/utils/lazy-import.tsx | 44 + app/utils/token-manager.server.ts | 147 + app/utils/validation.ts | 73 + bun.lock | 2372 +++ components.json | 21 + docker-compose.yml | 56 + docs/API_CONFIGURATION.md | 210 + generate-types.ps1 | 66 + generate-types.sh | 37 + k8s/PLAN.md | 378 + k8s/frontend-dev.yaml | 140 + k8s/frontend-ingress-dev.yaml | 31 + k8s/prod/frontend-prod.yaml | 175 + k8s/prod/ingress-prod.yaml | 64 + openapi-generator-cli.jar | Bin 0 -> 27103489 bytes package-lock.json | 16336 ++++++++++++++++ package.json | 88 + postcss.config.js | 6 + public/apple-touch-icon.png | Bin 0 -> 5607 bytes public/browserconfig.xml | 9 + public/favicon.ico | Bin 0 -> 3006 bytes public/favicon.svg | 3 + public/fonts/pinar/Pinar-Black.woff2 | Bin 0 -> 42212 bytes public/fonts/pinar/Pinar-Bold.woff2 | Bin 0 -> 44088 bytes public/fonts/pinar/Pinar-DS2-FD-Medium.woff2 | Bin 0 -> 43496 bytes public/fonts/pinar/Pinar-DS2-FD-Regular.woff2 | Bin 0 -> 43424 bytes public/fonts/pinar/Pinar-ExtraBold.woff2 | Bin 0 -> 44304 bytes public/fonts/pinar/Pinar-Light.woff2 | Bin 0 -> 42732 bytes public/fonts/pinar/Pinar-Medium.woff2 | Bin 0 -> 44264 bytes public/fonts/pinar/Pinar-Regular.woff2 | Bin 0 -> 44116 bytes public/fonts/pinar/Pinar-SemiBold.woff2 | Bin 0 -> 44100 bytes public/icon-144x144.png | Bin 0 -> 4410 bytes public/icon-192x192-maskable.png | Bin 0 -> 5840 bytes public/icon-192x192.png | Bin 0 -> 5840 bytes public/icon-256x256.png | Bin 0 -> 7379 bytes public/icon-48x48.png | Bin 0 -> 1474 bytes public/icon-512x512-maskable.png | Bin 0 -> 10332 bytes public/icon-512x512.png | Bin 0 -> 5718 bytes public/icon-72x72.png | Bin 0 -> 2253 bytes public/icon-96x96.png | Bin 0 -> 2936 bytes public/og-image.svg | 13 + public/styles/fonts.css | 9 + public/sw.js | 144 + scripts/monitor.sh | 107 + scripts/rollback.sh | 80 + src/api/types/.openapi-generator-ignore | 23 + src/api/types/.openapi-generator/FILES | 31 + src/api/types/.openapi-generator/VERSION | 1 + src/api/types/apis/AIFitApi.ts | 93 + src/api/types/apis/ApiApi.ts | 828 + src/api/types/apis/BookmarkApi.ts | 233 + src/api/types/apis/ChatApi.ts | 342 + src/api/types/apis/CollectionsAdminApi.ts | 318 + src/api/types/apis/CollectionsApi.ts | 75 + src/api/types/apis/CollectionsPublicApi.ts | 140 + src/api/types/apis/CollectionsSellerApi.ts | 334 + .../types/apis/CommentReviewManagmentApi.ts | 249 + src/api/types/apis/CourierManagementApi.ts | 99 + src/api/types/apis/DiscountManagementApi.ts | 285 + src/api/types/apis/FollowsManagementApi.ts | 178 + src/api/types/apis/HomepageApi.ts | 307 + src/api/types/apis/InstagramManagementApi.ts | 179 + src/api/types/apis/MediaManagementApi.ts | 213 + src/api/types/apis/NotificationsApi.ts | 222 + src/api/types/apis/OrderManagementApi.ts | 300 + src/api/types/apis/ProductManagementApi.ts | 849 + src/api/types/apis/SearchSystemApi.ts | 155 + .../types/apis/SellerCourierManagementApi.ts | 200 + src/api/types/apis/SellerManagementApi.ts | 465 + src/api/types/apis/ServiceProductApi.ts | 69 + src/api/types/apis/ServiceUserApi.ts | 107 + src/api/types/apis/ShippingManagementApi.ts | 155 + src/api/types/apis/ShoppingCartApi.ts | 301 + src/api/types/apis/UserManagementApi.ts | 615 + src/api/types/apis/WalletManagementApi.ts | 471 + src/api/types/apis/index.ts | 29 + src/api/types/index.ts | 5 + src/api/types/models/index.ts | 6959 +++++++ src/api/types/runtime.ts | 416 + tailwind.config.ts | 139 + tsconfig.json | 32 + vite.config.ts | 78 + 321 files changed, 71782 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.development create mode 100644 .env.example create mode 100644 .env.production create mode 100644 .eslintrc.cjs create mode 100644 .gitignore create mode 100644 DEPLOYMENT.md create mode 100644 Dockerfile create mode 100644 Dockerfile.dev create mode 100644 app/assets/data/consts.tsx create mode 100644 app/assets/icons/cart/basket.svg create mode 100644 app/assets/icons/cart/failure_pay.svg create mode 100644 app/assets/icons/cart/sucess_pay.svg create mode 100644 app/assets/icons/filter-dark.png create mode 100644 app/assets/icons/filter.png create mode 100644 app/assets/icons/login/verify-banner.svg create mode 100644 app/assets/icons/navbar/buy-filled.svg create mode 100644 app/assets/icons/navbar/buy-outline.svg create mode 100644 app/assets/icons/navbar/document-filled.svg create mode 100644 app/assets/icons/navbar/document-outline.svg create mode 100644 app/assets/icons/navbar/home-filled.svg create mode 100644 app/assets/icons/navbar/home-outline.svg create mode 100644 app/assets/icons/navbar/products-filled.svg create mode 100644 app/assets/icons/navbar/products-outline.svg create mode 100644 app/assets/icons/navbar/profile-filled.svg create mode 100644 app/assets/icons/navbar/profile-outline.svg create mode 100644 app/assets/icons/navbar/search-filled.svg create mode 100644 app/assets/icons/navbar/search-outline.svg create mode 100644 app/assets/icons/navbar/setting-filled.svg create mode 100644 app/assets/icons/navbar/setting-outline.svg create mode 100644 app/assets/icons/product-dark.png create mode 100644 app/assets/icons/product.png create mode 100644 app/assets/icons/product/discount-red.svg create mode 100644 app/assets/icons/product/longArrow.svg create mode 100644 app/assets/icons/product/tomanBlack.svg create mode 100644 app/assets/icons/product/tomanGray.svg create mode 100644 app/assets/icons/profile/bookmark/bookmark.svg create mode 100644 app/assets/icons/profile/location/location.svg create mode 100644 app/assets/icons/profile/orders/paper.svg create mode 100644 app/assets/icons/profile/threads/messageBox.svg create mode 100644 app/assets/icons/sellerPlaceholder-dark.svg create mode 100644 app/assets/icons/sellerPlaceholder.svg create mode 100644 app/assets/logo/SVG-01.svg create mode 100644 app/assets/logo/SVG-02.svg create mode 100644 app/assets/logo/SVG-03.svg create mode 100644 app/assets/logo/SVG-04.svg create mode 100644 app/assets/logo/SVG-05.svg create mode 100644 app/assets/logo/SVG-06.svg create mode 100644 app/assets/logo/SVG-07.svg create mode 100644 app/auth.server.ts create mode 100644 app/components/AIPromoteModal.tsx create mode 100644 app/components/AppInput.tsx create mode 100644 app/components/AppSelectBox.tsx create mode 100644 app/components/AppTextArea.tsx create mode 100644 app/components/BankLogo.tsx create mode 100644 app/components/CallDialog.tsx create mode 100644 app/components/ClientOnly.tsx create mode 100644 app/components/FilterDrawer.tsx create mode 100644 app/components/HeaderWithSupport.tsx create mode 100644 app/components/HorizontalCollectionsList.tsx create mode 100644 app/components/HorizontalProductList.tsx create mode 100644 app/components/InteractiveMap.tsx create mode 100644 app/components/LoginRequiredDialog.tsx create mode 100644 app/components/MondrianProductList.tsx create mode 100644 app/components/MyLayout.tsx create mode 100644 app/components/PersianDatePicker.tsx create mode 100644 app/components/ProfilePagesHeader.tsx create mode 100644 app/components/SearchTopBar.tsx create mode 100644 app/components/SellerLogo.tsx create mode 100644 app/components/SellerTilesGrid.tsx create mode 100644 app/components/SunMoonToggle.tsx create mode 100644 app/components/ThemeToggle.css create mode 100644 app/components/ThemeToggle.tsx create mode 100644 app/components/UiProvider.tsx create mode 100644 app/components/cart/CartHeader.tsx create mode 100644 app/components/cart/CartItem.tsx create mode 100644 app/components/cart/CartSummary.tsx create mode 100644 app/components/cart/QuantityControl.tsx create mode 100644 app/components/cart/ReceiverInformation.tsx create mode 100644 app/components/cart/ShippingInformation.tsx create mode 100644 app/components/cart/ThrottledQuantityControl.tsx create mode 100644 app/components/explore/ExploreTopBar.tsx create mode 100644 app/components/explore/MainSection.tsx create mode 100644 app/components/explore/SearchSection.tsx create mode 100644 app/components/explore/TabContainer.tsx create mode 100644 app/components/home/HomePageTopBar.tsx create mode 100644 app/components/home/InvoiceModal.tsx create mode 100644 app/components/product/BottomBar.tsx create mode 100644 app/components/product/ColorSelector.tsx create mode 100644 app/components/product/ContactButton.tsx create mode 100644 app/components/product/InactiveProductView.tsx create mode 100644 app/components/product/ProductActions.tsx create mode 100644 app/components/product/ProductDetailView.tsx create mode 100644 app/components/product/ProductHeader.tsx create mode 100644 app/components/product/ProductImageCarousel.tsx create mode 100644 app/components/product/ProductInfo.tsx create mode 100644 app/components/product/Review.tsx create mode 100644 app/components/product/ReviewSection.tsx create mode 100644 app/components/product/SizeSelector.tsx create mode 100644 app/components/product/index.ts create mode 100644 app/components/sets/AddToSetDrawer.tsx create mode 100644 app/components/sets/CreateSetModal.tsx create mode 100644 app/components/sets/SetSuccessModal.tsx create mode 100644 app/components/sets/index.ts create mode 100644 app/components/store/ColorSelector.tsx create mode 100644 app/components/store/ImageEditor.tsx create mode 100644 app/components/store/ImageUpload.tsx create mode 100644 app/components/store/ImageUploadDrawer.tsx create mode 100644 app/components/store/ImageUploadSection.tsx create mode 100644 app/components/store/PricingForm.tsx create mode 100644 app/components/store/ProductDetailsForm.tsx create mode 100644 app/components/store/ProductVariantDrawer.tsx create mode 100644 app/components/store/ProductVariantsSection.tsx create mode 100644 app/components/store/SizeSelector.tsx create mode 100644 app/components/store/StoreForm.tsx create mode 100644 app/components/thread/ChatHeader.tsx create mode 100644 app/components/thread/EmojiPicker.tsx create mode 100644 app/components/thread/MessageBubble.tsx create mode 100644 app/components/thread/MessageInput.tsx create mode 100644 app/components/thread/MessageList.tsx create mode 100644 app/components/thread/ThreadChatPage.tsx create mode 100644 app/components/thread/ThreadsPage.tsx create mode 100644 app/components/ui/FormField.tsx create mode 100644 app/components/ui/button.tsx create mode 100644 app/components/ui/card.tsx create mode 100644 app/components/ui/carousel.tsx create mode 100644 app/components/ui/command.tsx create mode 100644 app/components/ui/dialog.tsx create mode 100644 app/components/ui/drawer.tsx create mode 100644 app/components/ui/dropdown-menu.tsx create mode 100644 app/components/ui/input-otp.tsx create mode 100644 app/components/ui/input.tsx create mode 100644 app/components/ui/label.tsx create mode 100644 app/components/ui/popover.tsx create mode 100644 app/components/ui/radio-group.tsx create mode 100644 app/components/ui/select.tsx create mode 100644 app/components/ui/skeleton.tsx create mode 100644 app/components/ui/slider.tsx create mode 100644 app/components/ui/textarea.tsx create mode 100644 app/components/ui/toast.tsx create mode 100644 app/components/ui/toaster.tsx create mode 100644 app/cookie.ts create mode 100644 app/data/iranian-cities.ts create mode 100644 app/entry.client.tsx create mode 100644 app/entry.server.tsx create mode 100644 app/hooks/use-prefetch.ts create mode 100644 app/hooks/use-root-data.ts create mode 100644 app/hooks/use-toast.ts create mode 100644 app/hooks/useCarousel.ts create mode 100644 app/hooks/useDebounce.ts create mode 100644 app/hooks/useStoreOwnership.ts create mode 100644 app/hooks/useWebSocket.ts create mode 100644 app/i18n.ts create mode 100644 app/lib/utils.ts create mode 100644 app/requestHandler/use-address-hooks.tsx create mode 100644 app/requestHandler/use-auth-hooks.tsx create mode 100644 app/requestHandler/use-bookmark-hooks.tsx create mode 100644 app/requestHandler/use-cart-hooks.ts create mode 100644 app/requestHandler/use-chat-hooks.tsx create mode 100644 app/requestHandler/use-collection-hooks.tsx create mode 100644 app/requestHandler/use-comments-hooks.tsx create mode 100644 app/requestHandler/use-courier-hooks.tsx create mode 100644 app/requestHandler/use-discount-hooks.ts create mode 100644 app/requestHandler/use-explore-hooks.tsx create mode 100644 app/requestHandler/use-following-hooks.tsx create mode 100644 app/requestHandler/use-home-hooks.ts create mode 100644 app/requestHandler/use-instagram-hooks.ts create mode 100644 app/requestHandler/use-media-hooks.ts create mode 100644 app/requestHandler/use-order-hooks.ts create mode 100644 app/requestHandler/use-product-hooks.ts create mode 100644 app/requestHandler/use-profile-hooks.tsx create mode 100644 app/requestHandler/use-search-hooks.ts create mode 100644 app/requestHandler/use-seller-hooks.ts create mode 100644 app/requestHandler/use-shipping-hooks.ts create mode 100644 app/requestHandler/use-user-collection-hooks.tsx create mode 100644 app/requestHandler/use-wallet-hooks.tsx create mode 100644 app/root.tsx create mode 100644 app/routes/$.tsx create mode 100644 app/routes/_index.tsx create mode 100644 app/routes/cart.$cartId.tsx create mode 100644 app/routes/cart._index.tsx create mode 100644 app/routes/collection.$id.tsx create mode 100644 app/routes/collections.tsx create mode 100644 app/routes/explore.tsx create mode 100644 app/routes/login.tsx create mode 100644 app/routes/logout.tsx create mode 100644 app/routes/product.$id.tsx create mode 100644 app/routes/profile._index.tsx create mode 100644 app/routes/profile.about.tsx create mode 100644 app/routes/profile.bookmarks.tsx create mode 100644 app/routes/profile.faq.tsx create mode 100644 app/routes/profile.following.tsx create mode 100644 app/routes/profile.location._index.tsx create mode 100644 app/routes/profile.location.add.tsx create mode 100644 app/routes/profile.location.edit.$id.tsx create mode 100644 app/routes/profile.orders.tsx create mode 100644 app/routes/profile.sets.$id.tsx create mode 100644 app/routes/profile.sets._index.tsx create mode 100644 app/routes/profile.setting.tsx create mode 100644 app/routes/profile.threads.$threadId.tsx create mode 100644 app/routes/profile.threads._index.tsx create mode 100644 app/routes/profile.wallet._index.tsx create mode 100644 app/routes/profile.wallet.payment.tsx create mode 100644 app/routes/resources.manifest[.]webmanifest.ts create mode 100644 app/routes/robots[.]txt.tsx create mode 100644 app/routes/search.$query.tsx create mode 100644 app/routes/seller.$sellerId._index.tsx create mode 100644 app/routes/sellers.tsx create mode 100644 app/routes/sitemap[.]xml.tsx create mode 100644 app/routes/store.$storeId._index.tsx create mode 100644 app/routes/store.$storeId.edit.tsx create mode 100644 app/routes/store.$storeId.financial-dashboard._index.tsx create mode 100644 app/routes/store.$storeId.financial-dashboard.cash-funds.tsx create mode 100644 app/routes/store.$storeId.financial-dashboard.reports.tsx create mode 100644 app/routes/store.$storeId.financial-dashboard.transactions.tsx create mode 100644 app/routes/store.$storeId.orders.$orderId.tsx create mode 100644 app/routes/store.$storeId.orders._index.tsx create mode 100644 app/routes/store.$storeId.product.$id.tsx create mode 100644 app/routes/store.$storeId.products.tsx create mode 100644 app/routes/store.$storeId.setting.tsx create mode 100644 app/routes/store.$storeId.shipping-method.tsx create mode 100644 app/routes/store.$storeId.threads.$threadId.tsx create mode 100644 app/routes/store.$storeId.threads._index.tsx create mode 100644 app/routes/store.add.manual.$storeId.tsx create mode 100644 app/routes/store.authenticate.instagram.$storeId.tsx create mode 100644 app/routes/store.create.tsx create mode 100644 app/sessions.server.ts create mode 100644 app/styles/global.css create mode 100644 app/tailwind.css create mode 100644 app/types/moment-jalaali.d.ts create mode 100644 app/types/theme.ts create mode 100644 app/utils/RequestHandler.tsx create mode 100644 app/utils/api-client-factory.ts create mode 100644 app/utils/api-config.ts create mode 100644 app/utils/api-error-handler.ts create mode 100644 app/utils/get-env.ts create mode 100644 app/utils/helpers.ts create mode 100644 app/utils/imageUrl.ts create mode 100644 app/utils/iranian-banks.ts create mode 100644 app/utils/lazy-import.tsx create mode 100644 app/utils/token-manager.server.ts create mode 100644 app/utils/validation.ts create mode 100644 bun.lock create mode 100644 components.json create mode 100644 docker-compose.yml create mode 100644 docs/API_CONFIGURATION.md create mode 100644 generate-types.ps1 create mode 100644 generate-types.sh create mode 100644 k8s/PLAN.md create mode 100644 k8s/frontend-dev.yaml create mode 100644 k8s/frontend-ingress-dev.yaml create mode 100644 k8s/prod/frontend-prod.yaml create mode 100644 k8s/prod/ingress-prod.yaml create mode 100644 openapi-generator-cli.jar create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 public/apple-touch-icon.png create mode 100644 public/browserconfig.xml create mode 100644 public/favicon.ico create mode 100644 public/favicon.svg create mode 100644 public/fonts/pinar/Pinar-Black.woff2 create mode 100644 public/fonts/pinar/Pinar-Bold.woff2 create mode 100644 public/fonts/pinar/Pinar-DS2-FD-Medium.woff2 create mode 100644 public/fonts/pinar/Pinar-DS2-FD-Regular.woff2 create mode 100644 public/fonts/pinar/Pinar-ExtraBold.woff2 create mode 100644 public/fonts/pinar/Pinar-Light.woff2 create mode 100644 public/fonts/pinar/Pinar-Medium.woff2 create mode 100644 public/fonts/pinar/Pinar-Regular.woff2 create mode 100644 public/fonts/pinar/Pinar-SemiBold.woff2 create mode 100644 public/icon-144x144.png create mode 100644 public/icon-192x192-maskable.png create mode 100644 public/icon-192x192.png create mode 100644 public/icon-256x256.png create mode 100644 public/icon-48x48.png create mode 100644 public/icon-512x512-maskable.png create mode 100644 public/icon-512x512.png create mode 100644 public/icon-72x72.png create mode 100644 public/icon-96x96.png create mode 100644 public/og-image.svg create mode 100644 public/styles/fonts.css create mode 100644 public/sw.js create mode 100644 scripts/monitor.sh create mode 100644 scripts/rollback.sh create mode 100644 src/api/types/.openapi-generator-ignore create mode 100644 src/api/types/.openapi-generator/FILES create mode 100644 src/api/types/.openapi-generator/VERSION create mode 100644 src/api/types/apis/AIFitApi.ts create mode 100644 src/api/types/apis/ApiApi.ts create mode 100644 src/api/types/apis/BookmarkApi.ts create mode 100644 src/api/types/apis/ChatApi.ts create mode 100644 src/api/types/apis/CollectionsAdminApi.ts create mode 100644 src/api/types/apis/CollectionsApi.ts create mode 100644 src/api/types/apis/CollectionsPublicApi.ts create mode 100644 src/api/types/apis/CollectionsSellerApi.ts create mode 100644 src/api/types/apis/CommentReviewManagmentApi.ts create mode 100644 src/api/types/apis/CourierManagementApi.ts create mode 100644 src/api/types/apis/DiscountManagementApi.ts create mode 100644 src/api/types/apis/FollowsManagementApi.ts create mode 100644 src/api/types/apis/HomepageApi.ts create mode 100644 src/api/types/apis/InstagramManagementApi.ts create mode 100644 src/api/types/apis/MediaManagementApi.ts create mode 100644 src/api/types/apis/NotificationsApi.ts create mode 100644 src/api/types/apis/OrderManagementApi.ts create mode 100644 src/api/types/apis/ProductManagementApi.ts create mode 100644 src/api/types/apis/SearchSystemApi.ts create mode 100644 src/api/types/apis/SellerCourierManagementApi.ts create mode 100644 src/api/types/apis/SellerManagementApi.ts create mode 100644 src/api/types/apis/ServiceProductApi.ts create mode 100644 src/api/types/apis/ServiceUserApi.ts create mode 100644 src/api/types/apis/ShippingManagementApi.ts create mode 100644 src/api/types/apis/ShoppingCartApi.ts create mode 100644 src/api/types/apis/UserManagementApi.ts create mode 100644 src/api/types/apis/WalletManagementApi.ts create mode 100644 src/api/types/apis/index.ts create mode 100644 src/api/types/index.ts create mode 100644 src/api/types/models/index.ts create mode 100644 src/api/types/runtime.ts create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8c40783 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,26 @@ +node_modules +npm-debug.log* +.npm +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +.git +.gitignore +README.md +.eslintcache +.nyc_output +coverage +.DS_Store +*.log +build +dist +.cache +.vscode +.idea +*.swp +*.swo +.dockerignore +Dockerfile* +docker-compose*.yml \ No newline at end of file diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..4010b1c --- /dev/null +++ b/.env.development @@ -0,0 +1,3 @@ +VITE_API_BASE_URL=https://api.vitrown.abrino.cloud +VITE_API_SOCKET_BASE_URL=wss://api.vitrown.abrino.cloud +VITE_SUPPORT_PHONE=+989223740993 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7105031 --- /dev/null +++ b/.env.example @@ -0,0 +1,23 @@ +# API Configuration +# K8s Dev: api.vitrown.abrino.cloud | Production: api.vitrown.com +VITE_API_BASE_URL=https://api.vitrown.abrino.cloud +VITE_API_SOCKET_BASE_URL=wss://api.vitrown.abrino.cloud +VITE_API_URL=https://api.vitrown.abrino.cloud + +# Application Environment +VITE_APP_VERSION_TYPE=development +NODE_ENV=development + +# Site Configuration +# K8s Dev: vitrown.abrino.cloud | Production: vitrown.com +VITE_SITE_URL=https://vitrown.abrino.cloud +VITE_DOMAIN_URL=https://vitrown.abrino.cloud + +# Session Secret (REQUIRED - Change in production!) +REMIX_SECRET=your-secret-key-here + +# Optional Build Configuration +VITE_BUILD_TARGET=development + +# Support Configuration +VITE_SUPPORT_PHONE=+989223740993 diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..83f2bcf --- /dev/null +++ b/.env.production @@ -0,0 +1,21 @@ +# Production Environment Configuration +NODE_ENV=production + +# API Configuration +VITE_API_BASE_URL=https://api.prod.vitrown.com +VITE_API_SOCKET_BASE_URL=wss://api.prod.vitrown.com +VITE_API_URL=https://api.prod.vitrown.com + +# Application Environment +VITE_APP_VERSION_TYPE=production + +# Site Configuration +VITE_SITE_URL=https://vitrown.com +VITE_DOMAIN_URL=https://vitrown.com + +# Session Secret (REQUIRED - Set via secrets in CI/CD) +REMIX_SECRET=change-me-in-production + +# Build Configuration +VITE_BUILD_TARGET=production +VITE_SUPPORT_PHONE=+989223740993 \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..a804be1 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,81 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + root: true, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, + env: { + browser: true, + commonjs: true, + es6: true, + }, + ignorePatterns: ["!**/.server", "!**/.client"], + + // Base config + extends: ["eslint:recommended"], + + overrides: [ + // React + { + files: ["**/*.{js,jsx,ts,tsx}"], + plugins: ["react", "jsx-a11y"], + extends: [ + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", + "plugin:jsx-a11y/recommended", + ], + settings: { + react: { + version: "detect", + }, + formComponents: ["Form"], + linkComponents: [ + { name: "Link", linkAttribute: "to" }, + { name: "NavLink", linkAttribute: "to" }, + ], + "import/resolver": { + typescript: {}, + }, + }, + }, + + // Typescript + { + files: ["**/*.{ts,tsx}"], + plugins: ["@typescript-eslint", "import"], + parser: "@typescript-eslint/parser", + settings: { + "import/internal-regex": "^~/", + "import/resolver": { + node: { + extensions: [".ts", ".tsx"], + }, + typescript: { + alwaysTryTypes: true, + }, + }, + }, + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + ], + rules: { + "@typescript-eslint/no-unused-vars": "warn", // Change from error to warning + }, + }, + + // Node + { + files: [".eslintrc.cjs"], + env: { + node: true, + }, + }, + ], +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a452068 --- /dev/null +++ b/.gitignore @@ -0,0 +1,111 @@ +# Dependencies +node_modules/ +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# Build outputs +/build +/dist +/.next/ +/out/ + +# Environment files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE and editor files +.vscode/ +.idea/ +.cursor/ +.claude/ +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* +lerna-debug.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +/coverage +*.lcov + +# Dependency directories +jspm_packages/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Temporary folders +tmp/ +temp/ + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Vercel +.vercel + +# Turbo +.turbo + +# Local Netlify folder +.netlify diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..29809ca --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,190 @@ +# Production Deployment Guide + +## Overview + +This project uses a **Blue-Green deployment** strategy with **automatic rollback** capabilities to ensure zero-downtime deployments and quick recovery from failures. + +## Architecture + +### Blue-Green Deployment +- **Blue Service**: Currently serving production traffic +- **Green Service**: New deployment being tested +- **Traffic Switch**: Instant switchover via Traefik labels +- **Automatic Rollback**: Health check failures trigger immediate rollback + +### Key Features +- ✅ **Zero-downtime deployments** +- ✅ **Automatic health checks** +- ✅ **Instant rollback on failure** +- ✅ **Backup system with retention** +- ✅ **Build verification before traffic switch** +- ✅ **Resource limits and security** + +## Deployment Process + +### Automatic Deployment (Recommended) + +1. **Create Release Tag**: + ```bash + git tag 1.0.1-release + git push origin 1.0.1-release + ``` + +2. **Monitor Progress**: + - GitHub Actions: `https://github.com/sotoontech/Vitron-Front/actions` + - Server logs: `ssh root@prod-server 'docker-compose -f /opt/front-prod/Vitron-Front/docker-compose.yml logs -f'` + +### Deployment Steps (Automatic) + +1. **Backup**: Current deployment tagged as backup +2. **Build**: New version built in parallel (green/blue) +3. **Health Check**: 10 attempts over 100 seconds +4. **Traffic Switch**: Instant switchover via Traefik +5. **Verification**: Final check on live domain +6. **Cleanup**: Old deployment stopped and cleaned + +## Manual Operations + +### Emergency Rollback + +```bash +# SSH to production server +ssh root@prod-server + +# Go to project directory +cd /opt/front-prod/Vitron-Front + +# Rollback to latest backup +./scripts/rollback.sh + +# Or rollback to specific backup +./scripts/rollback.sh vitron-frontend-backup:20240906_150230 +``` + +### Check Status + +```bash +# Check running containers +docker ps | grep vitron + +# Check available backups +docker images | grep vitron-frontend-backup + +# Check service health +docker exec vitron-frontend-blue wget --spider http://localhost:3000/ +curl -I https://vitrown.com/ + +# Check logs +docker-compose logs -f vitron-frontend-blue +docker-compose logs -f vitron-frontend-green +``` + +### Manual Deployment + +```bash +# SSH to server +ssh root@prod-server +cd /opt/front-prod/Vitron-Front + +# Pull latest changes +git fetch --all +git checkout main +git pull origin main + +# Deploy manually +docker-compose -f docker-compose.yml up -d --build +``` + +## Environment Configuration + +### Required GitHub Secrets +- `PROD_SSH_HOST`: Production server IP/hostname +- `PROD_SSH_KEY`: SSH private key for server access +- `PROD_REMIX_SECRET`: Strong random string for session encryption + +### Environment Variables +```bash +NODE_ENV=production +VITE_API_BASE_URL=https://api.prod.vitrown.com +VITE_API_SOCKET_BASE_URL=wss://api.prod.vitrown.com +VITE_API_URL=https://api.prod.vitrown.com +VITE_SITE_URL=https://vitrown.com +VITE_DOMAIN_URL=https://vitrown.com +VITE_APP_VERSION_TYPE=production +REMIX_SECRET=[FROM_GITHUB_SECRETS] +``` + +## Troubleshooting + +### Common Issues + +1. **Build Failed** + ```bash + # Check build logs + docker-compose logs vitron-frontend-[color] + + # Manual build test + docker-compose build vitron-frontend-blue + ``` + +2. **Health Check Failed** + ```bash + # Check container status + docker exec vitron-frontend-[color] ps aux + + # Check internal health + docker exec vitron-frontend-[color] wget --spider http://localhost:3000/ + + # Check logs + docker exec vitron-frontend-[color] tail -f /var/log/nginx/error.log + ``` + +3. **Rollback Issues** + ```bash + # List available backups + docker images | grep backup + + # Manual container start + docker run -d --name manual-rollback vitron-frontend-backup:20240906_150230 + ``` + +### Health Check Endpoints +- Internal: `http://localhost:3000/` (container health) +- External: `https://vitrown.com/` (public endpoint) +- API: `https://api.prod.vitrown.com/api/health/` (backend health) + +## Security Features + +- **Non-root user**: Containers run as `remix` user (UID 1001) +- **Security headers**: X-Frame-Options, X-Content-Type-Options, etc. +- **HTTPS enforcement**: Automatic redirect from HTTP +- **Resource limits**: Memory limits prevent resource exhaustion +- **Network isolation**: Services run in isolated Docker network + +## Monitoring + +### Key Metrics to Monitor +- Container health status +- Response time on health checks +- Memory usage (limit: 512MB) +- Disk space (Docker images) +- SSL certificate expiry + +### Log Locations +- Application logs: `docker-compose logs` +- Traefik logs: Check proxy network logs +- System logs: `/var/log/syslog` on server + +## Backup Strategy + +- **Automatic**: Every deployment creates timestamped backup +- **Retention**: Last 3 backups kept automatically +- **Manual backup**: `docker tag vitron-frontend-blue vitron-frontend-backup:manual-$(date +%Y%m%d_%H%M%S)` + +## Performance Optimizations + +- **Multi-stage build**: Smaller production images +- **Asset optimization**: Terser minification, tree shaking +- **Caching**: Docker layer caching, npm ci +- **Resource limits**: Prevents memory leaks +- **Health checks**: Quick failure detection \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ded1151 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,53 @@ +# Multi-stage build for production +FROM node:20-alpine AS builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install ALL dependencies (including dev) for build +RUN npm ci && npm cache clean --force + +# Copy source code +COPY . . + +# Clear any existing build artifacts and build fresh +RUN rm -rf build node_modules/.vite node_modules/.cache + +# Build the application +RUN NODE_ENV=production npm run build + +# Production stage +FROM node:20-alpine AS production + +WORKDIR /app + +# Install only production dependencies +COPY package*.json ./ +RUN npm ci --omit=dev && npm cache clean --force + +# Copy built application from builder stage +COPY --from=builder /app/build ./build +COPY --from=builder /app/public ./public + +# Create non-root user for security +RUN addgroup -g 1001 -S nodejs && \ + adduser -S remix -u 1001 + +# Change ownership of app directory +RUN chown -R remix:nodejs /app +USER remix + +# Set production environment +ENV NODE_ENV=production + +# Expose port +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1 + +# Start production server +CMD ["npm", "start"] \ No newline at end of file diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..69bedaa --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,20 @@ +# Development Dockerfile +FROM node:20-alpine + +WORKDIR /app + +# Install dependencies +COPY package*.json ./ +RUN npm ci + +# Copy source code +COPY . . + +# Set development environment +ENV NODE_ENV=development + +# Expose port +EXPOSE 5173 + +# Start development server +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] \ No newline at end of file diff --git a/app/assets/data/consts.tsx b/app/assets/data/consts.tsx new file mode 100644 index 0000000..5c2ad7b --- /dev/null +++ b/app/assets/data/consts.tsx @@ -0,0 +1,390 @@ +export const years = [ + ...Array(104) + .fill(1) + .map((_, number) => { + return { + value: 1404 - number, + label: JSON.stringify(1404 - number), + }; + }), +]; +export const days = [ + ...Array(31) + .fill(1) + .map((_, number) => { + return { + value: number + 1, + label: JSON.stringify(number + 1), + }; + }), +]; +export const months = [ + { + value: 1, + label: "فروردین", + }, + { + value: 2, + label: "اردیبهشت", + }, + { + value: 3, + label: "خرداد", + }, + { + value: 4, + label: "تیر", + }, + { + value: 5, + label: "مرداد", + }, + { + value: 6, + label: "شهریور", + }, + { + value: 7, + label: "مهر", + }, + { + value: 8, + label: "آبان", + }, + { + value: 9, + label: "آذر", + }, + { + value: 10, + label: "دی", + }, + { + value: 11, + label: "بهمن", + }, + { + value: 12, + label: "اسفند", + }, +]; +export const SearchSuggestionsData = [ + // Products - Apparel - Men + { type: "product", title: "پیراهن مردانه آستین بلند" }, + { type: "product", title: "پیراهن مردانه آستین کوتاه" }, + { type: "product", title: "پیراهن مردانه رسمی" }, + { type: "product", title: "پیراهن مردانه اسپرت" }, + { type: "product", title: "شلوار مردانه جین" }, + { type: "product", title: "شلوار مردانه کتان" }, + { type: "product", title: "شلوار مردانه پارچه ای" }, + { type: "product", title: "شلوارک مردانه" }, + { type: "product", title: "کت تک مردانه" }, + { type: "product", title: "کت و شلوار مردانه دامادی" }, + { type: "product", title: "جلیقه مردانه" }, + { type: "product", title: "تیشرت مردانه یقه گرد" }, + { type: "product", title: "تیشرت مردانه یقه هفت" }, + { type: "product", title: "پولوشرت مردانه" }, + { type: "product", title: "هودی مردانه" }, + { type: "product", title: "سویشرت مردانه" }, + { type: "product", title: "زیرپوش مردانه" }, + { type: "product", title: "لباس خواب مردانه" }, + { type: "product", title: "جوراب کالج مردانه" }, + { type: "product", title: "جوراب ساق بلند مردانه" }, + { type: "product", title: "ست لباس ورزشی مردانه" }, + { type: "product", title: "شلوار ورزشی مردانه" }, + { type: "product", title: "کاپشن چرم مردانه" }, + { type: "product", title: "کاپشن زمستانه مردانه" }, + { type: "product", title: "بارانی مردانه" }, + { type: "product", title: "پالتو فوتر مردانه" }, + { type: "product", title: "ژاکت بافتنی مردانه" }, + { type: "product", title: "پلیور یقه اسکی مردانه" }, + + // Products - Apparel - Women + { type: "product", title: "مانتو مجلسی" }, + { type: "product", title: "مانتو اسپرت" }, + { type: "product", title: "مانتو اداری" }, + { type: "product", title: "مانتو کتی" }, + { type: "product", title: "شومیز حریر" }, + { type: "product", title: "بلوز زنانه" }, + { type: "product", title: "تونیک زنانه" }, + { type: "product", title: "سارافون زنانه" }, + { type: "product", title: "شلوار جین زنانه" }, + { type: "product", title: "شلوار پارچه ای زنانه" }, + { type: "product", title: "لگینگ زنانه" }, + { type: "product", title: "دامن کوتاه" }, + { type: "product", title: "دامن بلند" }, + { type: "product", title: "دامن مجلسی" }, + { type: "product", title: "پیراهن مجلسی زنانه" }, + { type: "product", title: "پیراهن ساحلی" }, + { type: "product", title: "لباس شب زنانه" }, + { type: "product", title: "سوتین" }, + { type: "product", title: "شورت زنانه" }, + { type: "product", title: "لباس خواب زنانه" }, + { type: "product", title: "گن زنانه" }, + { type: "product", title: "جوراب شلواری" }, + { type: "product", title: "جوراب نازک زنانه" }, + { type: "product", title: "ست لباس ورزشی زنانه" }, + { type: "product", title: "تاپ ورزشی زنانه" }, + { type: "product", title: "شلوارک ورزشی زنانه" }, + { type: "product", title: "کاپشن چرم زنانه" }, + { type: "product", title: "کاپشن زمستانه زنانه" }, + { type: "product", title: "بارانی زنانه" }, + { type: "product", title: "پالتو فوتر زنانه" }, + { type: "product", title: "پانچو" }, + { type: "product", title: "وست زنانه" }, + { type: "product", title: "روپوش پزشکی" }, + { type: "product", title: "لباس فرم اداری زنانه" }, + + // Products - Apparel - Children & Baby + { type: "product", title: "لباس نوزادی دخترانه" }, + { type: "product", title: "لباس نوزادی پسرانه" }, + { type: "product", title: "بادی نوزاد" }, + { type: "product", title: "رامپر نوزاد" }, + { type: "product", title: "ست بیمارستانی نوزاد" }, + { type: "product", title: "پیشبند نوزاد" }, + { type: "product", title: "کلاه نوزادی" }, + { type: "product", title: "تیشرت بچگانه" }, + { type: "product", title: "شلوار بچگانه" }, + { type: "product", title: "پیراهن دخترانه" }, + { type: "product", title: "سارافون دخترانه" }, + { type: "product", title: "لباس مجلسی دخترانه" }, + { type: "product", title: "کاپشن بچگانه" }, + { type: "product", title: "لباس شنا بچگانه" }, + + // Products - General Apparel + { type: "product", title: "کلاه کپ" }, + { type: "product", title: "کلاه بافتنی" }, + { type: "product", title: "شال گردن پشمی" }, + { type: "product", title: "شال گردن نخی" }, + { type: "product", title: "دستکش چرمی" }, + { type: "product", title: "دستکش بافتنی" }, + { type: "product", title: "لباس محلی" }, + { type: "product", title: "لباس سنتی" }, + + // Products - Shoes - Men + { type: "product", title: "کفش چرم مردانه" }, + { type: "product", title: "کالج مردانه" }, + { type: "product", title: "کفش ورزشی مردانه (رانینگ، فوتبال، بسکتبال)" }, + { type: "product", title: "نیم بوت مردانه" }, + { type: "product", title: "صندل طبیعت گردی مردانه" }, + { type: "product", title: "دمپایی مردانه" }, + + // Products - Shoes - Women + { type: "product", title: "کفش چرم زنانه" }, + { type: "product", title: "کالج زنانه" }, + { type: "product", title: "کفش تخت زنانه" }, + { type: "product", title: "کفش لژدار زنانه" }, + { type: "product", title: "کفش ورزشی زنانه (فیتنس، یوگا)" }, + { type: "product", title: "نیم بوت زنانه" }, + { type: "product", title: "صندل مجلسی زنانه" }, + { type: "product", title: "دمپایی زنانه" }, + { type: "product", title: "پاپوش زنانه" }, + + // Products - Shoes - Children + { type: "product", title: "کفش نوزادی" }, + { type: "product", title: "کتانی بچگانه" }, + { type: "product", title: "صندل بچگانه" }, + { type: "product", title: "بوت بچگانه" }, + + // Products - Bags - Women + { type: "product", title: "کیف مجلسی زنانه" }, + { type: "product", title: "کیف کتابی زنانه (کلاچ)" }, + { type: "product", title: "کیف کمری زنانه" }, + { type: "product", title: "ساک ورزشی زنانه" }, + + // Products - Bags - Men + { type: "product", title: "کیف سامسونت مردانه" }, + { type: "product", title: "کیف کمری مردانه" }, + { type: "product", title: "ساک ورزشی مردانه" }, + { type: "product", title: "کیف لپتاپ" }, + + // Products - Bags - General + { type: "product", title: "کوله پشتی مدرسه" }, + { type: "product", title: "کوله پشتی کوهنوردی" }, + { type: "product", title: "ساک خرید" }, + + // Products - Accessories - General + { type: "product", title: "عینک طبی" }, + { type: "product", title: "بند عینک" }, + { type: "product", title: "دکمه سردست" }, + { type: "product", title: "سنجاق سینه" }, + { type: "product", title: "سنجاق کراوات" }, + { type: "product", title: "گیره مو" }, + { type: "product", title: "تل مو" }, + { type: "product", title: "تاج عروس" }, + { type: "product", title: "پاپوش" }, + { type: "product", title: "ساسبند (بند شلوار)" }, + + // Shops - General Clothing & Department Stores + { + type: "shop", + id: "1", + title: "فروشگاه پوشاک خانواده", + image: + "https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "2", + title: "فروشگاه زنجیره ای پوشاک", + image: + "https://images.unsplash.com/photo-1483985988355-763728e1935b?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "3", + title: "مرکز خرید پوشاک", + image: + "https://images.unsplash.com/photo-1555529669-e6910eb125b6?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "4", + title: "پاساژ لباس", + image: + "https://images.unsplash.com/photo-1591085686350-798c0f9faa7f?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "5", + title: "اوت لت پوشاک", + image: + "https://images.unsplash.com/photo-1560243563-062bfc001d68?w=500&auto=format&fit=crop", + }, + + // Shops - Specific Apparel Types + { + type: "shop", + id: "6", + title: "فروشگاه لباس زیر", + image: + "https://images.unsplash.com/photo-1603217041454-99e679540093?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "7", + title: "فروشگاه لباس خواب", + image: + "https://images.unsplash.com/photo-1585519105003-c89177b34699?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "8", + title: "فروشگاه جوراب", + image: + "https://images.unsplash.com/photo-1613690150715-070f407fff94?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "9", + title: "فروشگاه تخصصی مانتو", + image: + "https://images.unsplash.com/photo-1595965781570-496f87b1d3c0?w=500&auto=format&fit=crop", + }, + { + type: "shop", + title: "فروشگاه کت و شلوار", + image: + "https://images.unsplash.com/photo-1509319117193-57bab727e09d?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "10", + title: "فروشگاه لباس کودک و نوزاد", + image: + "https://images.unsplash.com/photo-1520923642038-b42e596efc03?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "11", + title: "فروشگاه لباس بارداری", + image: + "https://images.unsplash.com/photo-1581027912560-1a0db3e9983f?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "12", + title: "فروشگاه لباس سایز بزرگ", + image: + "https://images.unsplash.com/photo-1607008028064-325720103504?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "13", + title: "فروشگاه لباس فرم و کار", + image: + "https://images.unsplash.com/photo-1529338296731-c1290a08ccc4?w=500&auto=format&fit=crop", + }, + + // Shops - Accessories & More + { + type: "shop", + id: "14", + title: "فروشگاه زیورآلات و بدلیجات", + image: + "https://images.unsplash.com/photo-1610041321327-07ac523e5804?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "15", + title: "گالری شال و روسری", + image: + "https://images.unsplash.com/photo-1563059269-3a38d1ae50a1?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "16", + title: "فروشگاه تخصصی کلاه", + image: + "https://images.unsplash.com/photo-1534215754734-18e1d14349ae?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "17", + title: "فروشگاه چمدان و کیف سفر", + image: + "https://images.unsplash.com/photo-1581403341094-189d3f0f8f15?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "18", + title: "خیاطی و مزون شخصی دوزی", + image: + "https://images.unsplash.com/photo-1579084806155-11518b23ac96?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "19", + title: "خشکشویی و خدمات لباس", + image: + "https://images.unsplash.com/photo-1580828343791-9fe69e70f611?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "20", + title: "فروشگاه پارچه", + image: + "https://images.unsplash.com/photo-1549061528-434734449753?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "21", + title: "نمایندگی برندهای معروف پوشاک (مثال: زارا، ال سی وایکیکی)", + image: + "https://images.unsplash.com/photo-1551488831-00ddcb6ac6bd?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "22", + title: "فروشگاه آنلاین مد و پوشاک", + image: + "https://images.unsplash.com/photo-1462392246756-320a5667a952?w=500&auto=format&fit=crop", + }, + { + type: "shop", + id: "23", + title: "بازار سنتی پوشاک", + image: + "https://images.unsplash.com/photo-1542359649-31e03cdde49b?w=500&auto=format&fit=crop", + }, +]; diff --git a/app/assets/icons/cart/basket.svg b/app/assets/icons/cart/basket.svg new file mode 100644 index 0000000..12814a7 --- /dev/null +++ b/app/assets/icons/cart/basket.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/cart/failure_pay.svg b/app/assets/icons/cart/failure_pay.svg new file mode 100644 index 0000000..ec9751f --- /dev/null +++ b/app/assets/icons/cart/failure_pay.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/assets/icons/cart/sucess_pay.svg b/app/assets/icons/cart/sucess_pay.svg new file mode 100644 index 0000000..59a85ad --- /dev/null +++ b/app/assets/icons/cart/sucess_pay.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/filter-dark.png b/app/assets/icons/filter-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..cbf38f239094d0c30a0db4af44059a467ec24d7a GIT binary patch literal 578 zcmeAS@N?(olHy`uVBq!ia0vp^DIm``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBx$B-Tjv*C{Z|`38J!~M-`cU`?Lt=9egD``z zgZT>%1J1<<*rss4VB5fWQz2$`<%?w5-6u1D*H4-{{gRbTfbdQ8a3+xLd~Jyt$u`__ zN#)@wi+$X{$;UcR^(-q{aV+L{xMx__w4TTKMr9E zg+}~^Kdvx6+8kb3B(G)s`%dz`a>Ms;7p_0grE;%ga-Ct+1ACR6kawnT*WzRUEF-jH;6F@HEa) z6foY&Y8@+cFXX$>*5HLRu6xVOdy+Xd*mY|3rD^wmxRqYjT9ElfeQxyXncXd2bAk-d z%KYGicm<-HCFz*rgu46#jH_GWwl0les_^Hx5}r2wTT5^C;cHP6-aCMPhiPbYIOCI9 zxno2e*=bq~Cdu&*$X7%OhPTl@(hCf3b zQ@3wi8+3WY6`_^Slu!2G7SWNke-N|1_S@8^xjp)&#w``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&di3`|y@E{-7;jBoGWEu8Hj()Muk1g1B95)yZ< zMUSjh2x8Y%T&J=W$ns*nxVL4FPzJ+?M4jq4*6YO&s=usFGubuq!lM59sSn=FVg#CP z!R{Iu^D{eBU12l#gy%nvPH&2r^GbZAr!*nhNg;{Dg{QGYQNWNz>Dgpgsb63Im8A;* zey?-)itYVlv+o|MQPdKMpBerB{Qr%TKl0btJbkBoP$u_#S^c-N{CmHDu5hS1_b2RxavV%b!_ubbavu1K0N)JjrUZZ(@YR z=D^39x3^(O-C5}Iypi(Sq)<<9rN;_>#<-P>1o&K1>I zbs}|R#GN1ge~cX!TlSa6-oM2zqjgyB4M)w+`10L%f9;*$f6IB>?YGBn*X1@X*6T6p z`El2PyT5n&&G+Ar&lbrkSTWuD^l`iPpDUlq$ + + + diff --git a/app/assets/icons/navbar/buy-filled.svg b/app/assets/icons/navbar/buy-filled.svg new file mode 100644 index 0000000..e306ac3 --- /dev/null +++ b/app/assets/icons/navbar/buy-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/buy-outline.svg b/app/assets/icons/navbar/buy-outline.svg new file mode 100644 index 0000000..c9a1b68 --- /dev/null +++ b/app/assets/icons/navbar/buy-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/document-filled.svg b/app/assets/icons/navbar/document-filled.svg new file mode 100644 index 0000000..37bca70 --- /dev/null +++ b/app/assets/icons/navbar/document-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/document-outline.svg b/app/assets/icons/navbar/document-outline.svg new file mode 100644 index 0000000..0d78584 --- /dev/null +++ b/app/assets/icons/navbar/document-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/home-filled.svg b/app/assets/icons/navbar/home-filled.svg new file mode 100644 index 0000000..c07c9ab --- /dev/null +++ b/app/assets/icons/navbar/home-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/home-outline.svg b/app/assets/icons/navbar/home-outline.svg new file mode 100644 index 0000000..243c5db --- /dev/null +++ b/app/assets/icons/navbar/home-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/products-filled.svg b/app/assets/icons/navbar/products-filled.svg new file mode 100644 index 0000000..601368a --- /dev/null +++ b/app/assets/icons/navbar/products-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/products-outline.svg b/app/assets/icons/navbar/products-outline.svg new file mode 100644 index 0000000..8add69b --- /dev/null +++ b/app/assets/icons/navbar/products-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/profile-filled.svg b/app/assets/icons/navbar/profile-filled.svg new file mode 100644 index 0000000..405e110 --- /dev/null +++ b/app/assets/icons/navbar/profile-filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/navbar/profile-outline.svg b/app/assets/icons/navbar/profile-outline.svg new file mode 100644 index 0000000..00fe4d7 --- /dev/null +++ b/app/assets/icons/navbar/profile-outline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/navbar/search-filled.svg b/app/assets/icons/navbar/search-filled.svg new file mode 100644 index 0000000..96fd1df --- /dev/null +++ b/app/assets/icons/navbar/search-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/search-outline.svg b/app/assets/icons/navbar/search-outline.svg new file mode 100644 index 0000000..1e0171d --- /dev/null +++ b/app/assets/icons/navbar/search-outline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/navbar/setting-filled.svg b/app/assets/icons/navbar/setting-filled.svg new file mode 100644 index 0000000..a599674 --- /dev/null +++ b/app/assets/icons/navbar/setting-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/navbar/setting-outline.svg b/app/assets/icons/navbar/setting-outline.svg new file mode 100644 index 0000000..2535c7e --- /dev/null +++ b/app/assets/icons/navbar/setting-outline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/product-dark.png b/app/assets/icons/product-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..6cc5e0d81cb66d43c227e2ea92c4a63ab2f51239 GIT binary patch literal 1540 zcmV+f2K)JmP)tO>Fhr^mH&gsJ!2k(j*HGQ z)QQT4AOY8EwKx^y9jf20*YOWX;kgBBG<>9MuC_7xQwJ(-vN)}9!n2JdmNAH9y5Mxf zQK!@P_V+t|Y&kdZCnmo5(BoEXXEdMBk4bod=RXe9d5}XY2nshz6%J(FbKj8?+ zLn76Hyk%8M4M8*Id5zE9UyS$mc8_*}Rg>Yz#ITfVx zKT$5C>-2N1?BKNv;U;P$B=5y#-EQ|f{hU5oNEJf*yqQn|q9_@skcDU$!cEnLH+?Dq z_d%PHJXuIpLMZQ8CY{&E=WOkIT9eQ&gmY8FboY>Q5JiORh!OgVkZpkR1otG@JTQEh zb%2))DCrHquCj*Ab)lkxp?8KY=coR9?RWym;rL`g+0cJ6vnDDLlAPV~d)^&zA%ABMbR3m5ES+(xtc!duodX|anW*;5k_PdpS9cVAz4hN z^CA2D`$Nv&ViGFge&rM|7K67Yl!nHfDgoa)k9BHu%}`W6L6sKrDT@hKtJMG_c?=#o zto?!znMdYB9al=CHuBhJ!2Q#vPpfLS(wHgSR199)nj{1YJ3Bk?c;tVM+wr&-{`cO) zY~}KrPn0uVVw}CmAq^Bp+$0A4joTSYKTr$)P@k6 z1B)Y-uzXCK{2cSKLk=net}_8kp|bBEbWX@)8JrX2TG5?6cYHFCrSrui#I|%PP6>G` zBTLlQ?Y0A!g56T3I3uKp422FUP6*kC3`N;etQWFP8HzHcSS_Rs8H%!`SSzGV8HzHb zSSdt@48^vkSSLiM48=C3SS6$i8H%S;I8phXa$t>+DrG1hOW`zOt|X}yLQIgMP*V7- z3O(s6M6tI2G0(zRuE?lt z!8COi;D-O#Jc+?}8Ogd+w;xd^C>zR2p@k(;Q*c3;TIn*1)?c4S70JSjKAN{4zyIF0 zg(nelra{hTrPDZ$F1p=rKshPA^eWEy^hpexl`hFmHY=U_zJFOXNZIP*#CTSTYpZ@@ qY$gL*K@bE%5ClOG1VIo4E&K~|ilk=u@HSrn0000q01a0LIhZ#FCF@6gB5gcYZ*zux_5O!YUs-uaRLD3Y099fJ!z3>vUv$L}+ zi;=`vFmNvO zuohH2!9v|>9l@VARE&77|U-0k{ScHoSveU=j7 zbrT*rM3x9?TnPGrLP*)oWq93$N1d-kN8>`YAakikF;9^QXnQDCel`wF{6{U~SY@grZMtD_R*$2v?Q||$z1T1Jk-SRj8>vnY-z-@h*skFN7~2TG<5<_ zFg2t}RK_`D_$c`OtE;QY^E?`@oFeO((wI0HT=n^b$q8AO!6+nT) zB-La+m*LYdpcxy=Vk5?ImCaW&LaNAcG$ADkA$7=btXfL+Lh6*^Sf!Mxg{(q`V^vb3 z6|zbhj#WsBQb-dr9Cb^HPDqn79Cb>GYGT-i3IS${;}sNmT|3PDsi!NKisjmqCINVloy4 zA;e_-PwPTV##7q9fSJj7hOdyEKkB&7NCGm76M*LPx!2iWUP83cc%J#LR3L?#(dl#; zQ}@1uTbU-SxGzRl@_JP%$lzym_gN87yoDfEd@PIV=)EB2i0v{Hl+a>EoaLM;2B1qg20mSy1$Py-Wu= z?oOv8-IrC0T5i3nS7IpcbSY_4+yspMYt`Cc>&=OIaWQ;bl9GaXMs5>Y!!QiPFbu;m e48t&tM)((5PP#I4>Bf8j0000{ literal 0 HcmV?d00001 diff --git a/app/assets/icons/product/discount-red.svg b/app/assets/icons/product/discount-red.svg new file mode 100644 index 0000000..885b4b8 --- /dev/null +++ b/app/assets/icons/product/discount-red.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/product/longArrow.svg b/app/assets/icons/product/longArrow.svg new file mode 100644 index 0000000..5a1f8b7 --- /dev/null +++ b/app/assets/icons/product/longArrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/product/tomanBlack.svg b/app/assets/icons/product/tomanBlack.svg new file mode 100644 index 0000000..923b5e8 --- /dev/null +++ b/app/assets/icons/product/tomanBlack.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/product/tomanGray.svg b/app/assets/icons/product/tomanGray.svg new file mode 100644 index 0000000..413725d --- /dev/null +++ b/app/assets/icons/product/tomanGray.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/profile/bookmark/bookmark.svg b/app/assets/icons/profile/bookmark/bookmark.svg new file mode 100644 index 0000000..59bb997 --- /dev/null +++ b/app/assets/icons/profile/bookmark/bookmark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/profile/location/location.svg b/app/assets/icons/profile/location/location.svg new file mode 100644 index 0000000..854f6bf --- /dev/null +++ b/app/assets/icons/profile/location/location.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/profile/orders/paper.svg b/app/assets/icons/profile/orders/paper.svg new file mode 100644 index 0000000..c38583d --- /dev/null +++ b/app/assets/icons/profile/orders/paper.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/assets/icons/profile/threads/messageBox.svg b/app/assets/icons/profile/threads/messageBox.svg new file mode 100644 index 0000000..8c2eb49 --- /dev/null +++ b/app/assets/icons/profile/threads/messageBox.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/sellerPlaceholder-dark.svg b/app/assets/icons/sellerPlaceholder-dark.svg new file mode 100644 index 0000000..6111972 --- /dev/null +++ b/app/assets/icons/sellerPlaceholder-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/icons/sellerPlaceholder.svg b/app/assets/icons/sellerPlaceholder.svg new file mode 100644 index 0000000..13db3af --- /dev/null +++ b/app/assets/icons/sellerPlaceholder.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/logo/SVG-01.svg b/app/assets/logo/SVG-01.svg new file mode 100644 index 0000000..bc49678 --- /dev/null +++ b/app/assets/logo/SVG-01.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/logo/SVG-02.svg b/app/assets/logo/SVG-02.svg new file mode 100644 index 0000000..06dedd2 --- /dev/null +++ b/app/assets/logo/SVG-02.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/logo/SVG-03.svg b/app/assets/logo/SVG-03.svg new file mode 100644 index 0000000..e697026 --- /dev/null +++ b/app/assets/logo/SVG-03.svg @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/assets/logo/SVG-04.svg b/app/assets/logo/SVG-04.svg new file mode 100644 index 0000000..42cb505 --- /dev/null +++ b/app/assets/logo/SVG-04.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/logo/SVG-05.svg b/app/assets/logo/SVG-05.svg new file mode 100644 index 0000000..7399592 --- /dev/null +++ b/app/assets/logo/SVG-05.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/logo/SVG-06.svg b/app/assets/logo/SVG-06.svg new file mode 100644 index 0000000..b5fbe4a --- /dev/null +++ b/app/assets/logo/SVG-06.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/logo/SVG-07.svg b/app/assets/logo/SVG-07.svg new file mode 100644 index 0000000..8b2cf1a --- /dev/null +++ b/app/assets/logo/SVG-07.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/auth.server.ts b/app/auth.server.ts new file mode 100644 index 0000000..e5ddd1a --- /dev/null +++ b/app/auth.server.ts @@ -0,0 +1,21 @@ +import { Authenticator } from "remix-auth"; +import { FormStrategy } from "remix-auth-form"; +import { sessionStorage } from "./sessions.server"; + +export const authenticator = new Authenticator(sessionStorage); + +// login +authenticator.use( + new FormStrategy(async ({ form }) => { + const phoneNumber = form.get("phoneNumber") as string; + const access_token = form.get("access_token") as string; + const refresh_token = form.get("refresh_token") as string; + return { + phoneNumber: phoneNumber?.toString(), + access_token: access_token?.toString(), + refresh_token: refresh_token?.toString(), + loggedIn: true, + }; + }), + "vitrown-login" +); diff --git a/app/components/AIPromoteModal.tsx b/app/components/AIPromoteModal.tsx new file mode 100644 index 0000000..91abea4 --- /dev/null +++ b/app/components/AIPromoteModal.tsx @@ -0,0 +1,585 @@ +import { useState, useRef, useEffect } from "react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogOverlay, +} from "./ui/dialog"; +import { Button } from "./ui/button"; +import { + Sparkles, + Upload, + ImageIcon, + X, + Download, + RotateCcw, + CheckCircle, + Share2, +} from "lucide-react"; +import { useRootData, useAuthToken } from "../hooks/use-root-data"; +import { LoginRequiredDialog } from "./LoginRequiredDialog"; +import { useMutation } from "@tanstack/react-query"; +import { getApiBaseUrl } from "~/utils/api-config"; + +interface AIPromoteModalProps { + isOpen: boolean; + onClose: () => void; + collectionId?: string; + productId?: string; +} + +export default function AIPromoteModal({ + isOpen, + onClose, + collectionId, + productId, +}: AIPromoteModalProps) { + const isProductMode = !!productId; + const { user } = useRootData(); + const token = useAuthToken(); + const [selectedImage, setSelectedImage] = useState(null); + const [imagePreview, setImagePreview] = useState(null); + const [resultImage, setResultImage] = useState(null); + const [isUploading, setIsUploading] = useState(false); + const [isLoginDialogOpen, setIsLoginDialogOpen] = useState(false); + const [loadingStep, setLoadingStep] = useState(0); + const fileInputRef = useRef(null); + + // Loading steps messages + const loadingSteps = [ + { text: "در حال آماده‌سازی لباس...", icon: "👗" }, + { text: "در حال اندازه‌گیری سایز شما...", icon: "📏" }, + { text: "در حال دوختن لباس به اندازه شما...", icon: "✂️" }, + { text: "در حال اتو کردن و مرتب کردن...", icon: "✨" }, + { text: "در حال پرو کردن روی شما...", icon: "🎯" }, + { text: "تقریباً آماده شده یخورده هم صبر کن...", icon: "⏳" }, + ]; + + // Cycle through loading steps + useEffect(() => { + if (!isUploading) { + setLoadingStep(0); + return; + } + + const interval = setInterval(() => { + setLoadingStep((prev) => { + // Stay on last step if we've gone through all + if (prev >= loadingSteps.length - 1) return prev; + return prev + 1; + }); + }, 4000); + + return () => clearInterval(interval); + }, [isUploading, loadingSteps.length]); + + // AI Try-on mutation for collection + const tryOnCollectionMutation = useMutation({ + mutationFn: async ({ + image, + collectionId, + }: { + image: Blob; + collectionId: string; + }) => { + if (!token) throw new Error("Authentication required"); + const baseUrl = getApiBaseUrl(); + + const formData = new FormData(); + formData.append("collection_id", collectionId); + formData.append("image", image); + + const response = await fetch(`${baseUrl}/api/aifit/v1/try-on/`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + }, + body: formData, + }); + + if (!response.ok) { + throw new Error(`API error: ${response.status}`); + } + + // Response is binary PNG, convert to blob URL + const blob = await response.blob(); + return URL.createObjectURL(blob); + }, + }); + + // AI Try-on mutation for product + const tryOnProductMutation = useMutation({ + mutationFn: async ({ + image, + productId, + }: { + image: Blob; + productId: string; + }) => { + if (!token) throw new Error("Authentication required"); + const baseUrl = getApiBaseUrl(); + + const formData = new FormData(); + formData.append("product_id", productId); + formData.append("image", image); + + // AI processing can take a while, set 2 minute timeout + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 120000); + + try { + const response = await fetch( + `${baseUrl}/api/aifit/v1/try-on/product/`, + { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + }, + body: formData, + signal: controller.signal, + } + ); + + clearTimeout(timeoutId); + + if (!response.ok) { + const errorText = await response.text().catch(() => ""); + console.error("API error response:", errorText); + throw new Error(`API error: ${response.status}`); + } + + // Response is binary PNG, convert to blob URL + const blob = await response.blob(); + console.log("Received blob:", blob.type, blob.size); + return URL.createObjectURL(blob); + } catch (error) { + clearTimeout(timeoutId); + if (error instanceof Error && error.name === "AbortError") { + throw new Error( + "درخواست زمان زیادی طول کشید. لطفا دوباره تلاش کنید." + ); + } + throw error; + } + }, + }); + + // Image Upload Handler + const handleImageSelect = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + // Validate file type + if (!file.type.startsWith("image/")) { + alert("لطفا یک فایل تصویری انتخاب کنید"); + return; + } + + // Validate file size (max 10MB) + if (file.size > 10 * 1024 * 1024) { + alert("حجم فایل نباید بیشتر از 10 مگابایت باشد"); + return; + } + + setSelectedImage(file); + + // Create preview + const reader = new FileReader(); + reader.onloadend = () => { + setImagePreview(reader.result as string); + }; + reader.readAsDataURL(file); + } + }; + + // Handle login required - close AI modal first, then show login + const handleLoginRequired = () => { + onClose(); // Close AI modal first + // Small delay to let the modal close animation complete + setTimeout(() => { + setIsLoginDialogOpen(true); + }, 150); + }; + + // Handle AI Promote Submit + const handleAIPromote = async () => { + // Check if user is logged in + if (!user) { + handleLoginRequired(); + return; + } + + if (!selectedImage) { + alert("لطفا ابتدا عکس خود را آپلود کنید"); + return; + } + + setIsUploading(true); + + try { + console.log("Sending AI try-on request..."); + + let result; + + if (isProductMode && productId) { + // Product try-on + result = await tryOnProductMutation.mutateAsync({ + image: selectedImage, + productId, + }); + } else if (collectionId) { + // Collection try-on + result = await tryOnCollectionMutation.mutateAsync({ + image: selectedImage, + collectionId, + }); + } else { + throw new Error("No product or collection ID provided"); + } + + console.log("AI try-on completed successfully", result); + + // Result is already a blob URL from the mutation + setResultImage(result); + + // Reset state + // handleModalClose(); + } catch (error) { + console.error("Error in AI try-on:", error); + alert("متاسفانه خطایی رخ داد. لطفا دوباره تلاش کنید."); + } finally { + setIsUploading(false); + } + }; + + // Reset modal state when closed + const handleModalClose = () => { + setSelectedImage(null); + setImagePreview(null); + setResultImage(null); + setIsUploading(false); + onClose(); + }; + + // Reset to try again + const handleTryAgain = () => { + setResultImage(null); + setSelectedImage(null); + setImagePreview(null); + }; + + // Download result image + const handleDownload = () => { + if (!resultImage) return; + const link = document.createElement("a"); + link.href = resultImage; + link.download = `ai-tryon-${Date.now()}.png`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + + // Share result image + const handleShare = async () => { + if (!resultImage) return; + + try { + // Convert blob URL to actual blob for sharing + const response = await fetch(resultImage); + const blob = await response.blob(); + const file = new File([blob], `ai-tryon-${Date.now()}.png`, { + type: "image/png", + }); + + // Build product URL for sharing + const siteUrl = + typeof window !== "undefined" + ? window.location.origin + : "https://vitrown.com"; + const productUrl = productId + ? `${siteUrl}/product/${productId}` + : siteUrl; + const shareText = `ببین این لباس رو تن من چطور شده! 👗✨\n\nاین محصول رو از ویترون ببین:\n${productUrl}`; + + // Check if Web Share API is available and supports sharing files + if (navigator.share && navigator.canShare?.({ files: [file] })) { + await navigator.share({ + title: "پرو کردن با هوش مصنوعی - ویترون", + text: shareText, + files: [file], + }); + } else { + // Fallback: Copy image to clipboard or show message + try { + await navigator.clipboard.write([ + new ClipboardItem({ "image/png": blob }), + ]); + alert( + "تصویر در کلیپ‌بورد کپی شد! حالا میتونی توی هر اپلیکیشنی Paste کنی." + ); + } catch { + // If clipboard also fails, just download + handleDownload(); + alert("تصویر دانلود شد. میتونی از گالری به اشتراک بذاری."); + } + } + } catch (error) { + console.error("Error sharing:", error); + } + }; + + return ( + <> + {/* Login Required Dialog - outside of main dialog to avoid overlap */} + + + + + + {/* Close Button */} + + + + + + {isProductMode + ? "پرو کردن محصول با هوش مصنوعی" + : "پرو کردن کالکشن با هوش مصنوعی"} + + + + + {isProductMode + ? "برای اینکه AI بتواند این محصول رو روی بدن شما نمایش بده، لطفا یک عکس قدی از خود را آپلود کنید." + : "برای اینکه AI بتواد این کالکشن رو روی بدن شما نمایش بده، لطفا یک عکس قدی از خود را آپلود کنید."} + + + +
+ {resultImage ? ( + // Result View +
+ {/* Success Badge */} +
+ +

+ پرو کردن با موفقیت انجام شد! +

+
+ + {/* Result Image */} +
+ AI Try-on Result +
+ + {/* Action Buttons */} +
+ {/* Share Button - Full Width */} + + + {/* Download & Try Again */} +
+ + +
+
+
+ ) : ( + // Upload View + <> + {/* Image Upload Area */} +
+ + + {imagePreview ? ( + // Image Preview +
+ Preview + +
+ ) : ( + // Upload Button + + )} +
+ + {/* Action Button or Loading State */} + {isUploading ? ( + // Loading State - replaces button and info boxes +
+ {/* Animated Icon */} +
+ {loadingSteps[loadingStep].icon} +
+ + {/* Loading Text */} +

+ {loadingSteps[loadingStep].text} +

+ + {/* Progress Dots */} +
+ {loadingSteps.map((_, index) => ( +
+ ))} +
+ + {/* Step Counter */} +

+ مرحله {loadingStep + 1} از {loadingSteps.length} +

+
+ ) : ( + // Normal State - Button and Info boxes + <> + + + {/* Info Box */} +
+ +

+ نکته: برای + بهترین نتیجه، عکس باید در نورپردازی مناسب و با زمینه + ساده باشه. AI با استفاده از این عکس،{" "} + {isProductMode ? "این محصول" : "محصولات کالکشن"} رو روی + بدن شما نمایش میده. +

+
+ + {/* Privacy Notice */} +
+ + + +

+ + حریم خصوصی شما مهمه: + {" "} + عکس‌های شما هیچ‌جا ذخیره نمیشه +

+
+ + )} + + )} +
+ +
+ + ); +} diff --git a/app/components/AppInput.tsx b/app/components/AppInput.tsx new file mode 100644 index 0000000..0e58399 --- /dev/null +++ b/app/components/AppInput.tsx @@ -0,0 +1,77 @@ +import { memo } from "react"; +import { Input } from "./ui/input"; +import { Skeleton } from "./ui/skeleton"; +import { LucideIcon } from "lucide-react"; + +const AppInput = memo(function AppInput({ + inputValue, + inputOnChange, + inputIcon, + inputPlaceholder, + inputDir, + disabled, + type, + inputTitle, + loading, // New prop + onKeyDown, + className, + inputRef, // New prop to forward ref to input + inputOnFocus, + inputOnBlur, + inputTitleFontSize, + iconProps, // New prop for icon customization +}: { + inputValue: string; + inputOnChange: (e: React.ChangeEvent) => void; + inputIcon?: LucideIcon | string; + inputPlaceholder: string; + type?: string; + inputDir?: string; + disabled?: boolean; + inputTitle: string; + loading?: boolean; // New prop + onKeyDown?: (e: React.KeyboardEvent) => void; + className?: string; + inputRef?: React.RefObject; // New prop type + inputOnFocus?: (e: React.FocusEvent) => void; + inputOnBlur?: (e: React.FocusEvent) => void; + inputTitleFontSize?: string; + iconProps?: { + size?: number; + color?: string; + strokeWidth?: number; + className?: string; + }; // New prop for icon customization +}) { + return ( +
+

+ {inputTitle} +

+ {loading ? ( + // Display skeleton when loading + ) : ( + + )} +
+ ); +}); + +export default AppInput; diff --git a/app/components/AppSelectBox.tsx b/app/components/AppSelectBox.tsx new file mode 100644 index 0000000..79ac6fb --- /dev/null +++ b/app/components/AppSelectBox.tsx @@ -0,0 +1,112 @@ +import { Check, Loader } from "lucide-react"; + +import { cn } from "../lib/utils"; +import { Button } from "../components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "../components/ui/command"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "../components/ui/popover"; +import { useState } from "react"; + +interface Item { + value: string | number; + label: string; +} + +interface AppSelectBoxProps { + items: Item[]; + placeholder?: string; + haveSearchbar?: boolean; + value: string; + setValue: React.Dispatch>; + loading?: boolean; // New loading prop + width?: string; // New width prop + disabled?: boolean; + title?: string; // Add title prop +} + +export default function AppSelectBox({ + haveSearchbar, + items, + placeholder, + value, + setValue, + loading = false, // Default to false + width = "w-[105px]", // Default width + disabled = false, + title, +}: AppSelectBoxProps) { + const [open, setOpen] = useState(false); + + return ( +
+ {title && ( +

{title}

+ )} + + + + + {!loading && ( // Only show dropdown content when not loading + + + {haveSearchbar && ( + + )} + + آیتمی وجود نداره + + {items.map((item: Item) => ( + { + setValue( + item.value === value ? "" : item.value.toString() + ); + setOpen(false); + }} + > + {item.label} + + + ))} + + + + + )} + +
+ ); +} diff --git a/app/components/AppTextArea.tsx b/app/components/AppTextArea.tsx new file mode 100644 index 0000000..622b23c --- /dev/null +++ b/app/components/AppTextArea.tsx @@ -0,0 +1,39 @@ +import { Skeleton } from "./ui/skeleton"; + +export default function AppTextArea({ + inputValue, + inputOnChange, + inputPlaceholder, + inputDir, + disabled, + inputTitle, + loading, +}: { + inputValue: string; + inputOnChange: (e: React.ChangeEvent) => void; + inputPlaceholder: string; + inputDir?: string; + disabled?: boolean; + inputTitle: string; + loading?: boolean; +}) { + return ( +
+

+ {inputTitle} +

+ {loading ? ( + + ) : ( +