From cc15338ad9a28a49caaf39596cd083a051cb5281 Mon Sep 17 00:00:00 2001 From: ianshaloom Date: Wed, 4 Mar 2026 09:31:18 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20initial=20commit=20=E2=80=94=20storefro?= =?UTF-8?q?nt,=20convex=20backend,=20and=20shared=20packages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the first milestone of The Pet Loft ecommerce platform: - apps/storefront: full customer-facing Next.js app with HeroUI (cart, checkout, orders, wishlist, product detail, shop, search, auth) - convex/: serverless backend with schema, queries, mutations, actions, HTTP routes, Stripe/Shippo integrations, and co-located tests - packages/types, packages/utils, packages/convex: shared workspace packages Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 33 + CLAUDE.md | 153 + README.md | 123 + apps/storefront/.env.example | 3 + apps/storefront/next-env.d.ts | 6 + apps/storefront/next.config.js | 8 + apps/storefront/package.json | 23 + apps/storefront/pet-palette.html | 571 + apps/storefront/postcss.config.js | 5 + apps/storefront/public/branding/logo.svg | 111 + .../content/illustrations/404_not_found.svg | 1 + .../storefront/public/content/news-letter.png | Bin 0 -> 258301 bytes .../public/content/newsletter-dog.png | Bin 0 -> 230708 bytes .../product-images/product-01.jpg | Bin 0 -> 126595 bytes .../product-images/product-02.jpg | Bin 0 -> 281169 bytes .../product-images/product-03.jpg | Bin 0 -> 249289 bytes .../product-images/product-04.jpg | Bin 0 -> 89267 bytes .../product-images/product-05.jpg | Bin 0 -> 180748 bytes .../product-images/product-06.jpg | Bin 0 -> 101864 bytes .../product-images/product-07.jpg | Bin 0 -> 97885 bytes .../product-images/product-08.jpg | Bin 0 -> 59806 bytes .../product-images/product-09.jpg | Bin 0 -> 102467 bytes .../product-images/product-10.jpg | Bin 0 -> 101882 bytes .../product-images/product-11.png | Bin 0 -> 324429 bytes .../product-images/product-12.jpg | Bin 0 -> 50673 bytes .../product-images/product-13.jpg | Bin 0 -> 125955 bytes .../product-images/product-14.jpg | Bin 0 -> 136172 bytes .../product-images/product-15.jpg | Bin 0 -> 154178 bytes .../product-images/product-16.jpg | Bin 0 -> 134517 bytes .../product-images/product-17.jpg | Bin 0 -> 136121 bytes .../product-images/product-18.jpg | Bin 0 -> 134776 bytes .../product-images/product-19.jpg | Bin 0 -> 134270 bytes .../product-images/product-20.jpg | Bin 0 -> 131532 bytes .../product-images/product-21.jpg | Bin 0 -> 132321 bytes .../product-images/product-22.jpg | Bin 0 -> 132745 bytes .../product-images/product-23.jpg | Bin 0 -> 127714 bytes .../product-images/product-24.jpg | Bin 0 -> 141284 bytes .../product-images/product-25.jpg | Bin 0 -> 136378 bytes .../product-images/product-26.jpg | Bin 0 -> 33371 bytes .../product-images/product-27.jpg | Bin 0 -> 132006 bytes .../product-images/product-28.jpg | Bin 0 -> 118172 bytes .../product-images/product-29.jpg | Bin 0 -> 104742 bytes .../product-images/product-30.jpg | Bin 0 -> 72109 bytes .../product-images/product-31.jpg | Bin 0 -> 90783 bytes .../product-images/product-32.jpg | Bin 0 -> 139596 bytes .../product-images/product-33.jpg | Bin 0 -> 107855 bytes .../product-images/product-34.jpg | Bin 0 -> 200659 bytes .../product-images/product-35.jpg | Bin 0 -> 171699 bytes .../product-images/product-36.jpg | Bin 0 -> 134723 bytes .../product-images/product-37.jpg | Bin 0 -> 150136 bytes .../product-images/product-38.jpg | Bin 0 -> 1159749 bytes .../product-images/product-39.jpg | Bin 0 -> 167270 bytes .../product-images/product-40.jpg | Bin 0 -> 95165 bytes .../product-images/product-41.jpg | Bin 0 -> 97204 bytes .../product-images/product-42.jpg | Bin 0 -> 48676 bytes .../product-images/product-43.jpg | Bin 0 -> 107110 bytes .../product-images/product-44.jpg | Bin 0 -> 58000 bytes .../product-images/product-45.jpg | Bin 0 -> 175724 bytes .../product-images/product-46.jpg | Bin 0 -> 111748 bytes .../product-images/product-47.jpg | Bin 0 -> 503993 bytes .../product-images/product-48.jpg | Bin 0 -> 498840 bytes .../product-images/product-49.jpg | Bin 0 -> 6167150 bytes .../product-images/product-49.png | Bin 0 -> 5809877 bytes .../product-images/product-50.jpg | Bin 0 -> 473646 bytes .../product-images/product-51.jpg | Bin 0 -> 1047806 bytes .../product-images/product-52.jpg | Bin 0 -> 972661 bytes .../product-images/product-53.jpg | Bin 0 -> 945929 bytes .../product-images/product-54.jpg | Bin 0 -> 468597 bytes .../product-images/product-55.jpg | Bin 0 -> 2206084 bytes apps/storefront/public/icons/icon_bowl.svg | 80 + apps/storefront/public/icons/icon_carrier.svg | 61 + apps/storefront/public/icons/icon_food.svg | 66 + apps/storefront/public/icons/icon_litter.svg | 36 + apps/storefront/public/icons/icon_toys.svg | 30 + apps/storefront/public/icons/icon_treats.svg | 46 + apps/storefront/public/images/cta/cta-01.webp | Bin 0 -> 100304 bytes .../storefront/public/images/cta/cta-011.webp | Bin 0 -> 85276 bytes apps/storefront/public/images/cta/cta-02.webp | Bin 0 -> 100996 bytes apps/storefront/public/images/cta/cta-03.webp | Bin 0 -> 151222 bytes apps/storefront/src/app/account/layout.tsx | 18 + .../src/app/account/orders/[orderId]/page.tsx | 19 + .../src/app/account/orders/loading.tsx | 18 + .../src/app/account/orders/page.tsx | 17 + apps/storefront/src/app/account/page.tsx | 139 + apps/storefront/src/app/cart/CartPageView.tsx | 55 + apps/storefront/src/app/cart/layout.tsx | 18 + apps/storefront/src/app/cart/loading.tsx | 13 + apps/storefront/src/app/cart/page.tsx | 10 + .../src/app/checkout/CheckoutPageView.tsx | 106 + apps/storefront/src/app/checkout/layout.tsx | 18 + apps/storefront/src/app/checkout/loading.tsx | 5 + apps/storefront/src/app/checkout/page.tsx | 7 + .../src/app/checkout/success/page.tsx | 253 + apps/storefront/src/app/globals.css | 196 + apps/storefront/src/app/layout.tsx | 58 + apps/storefront/src/app/not-found.tsx | 46 + apps/storefront/src/app/page.tsx | 26 + .../[subCategory]/[slug]/loading.tsx | 57 + .../[subCategory]/[slug]/not-found.tsx | 25 + .../[category]/[subCategory]/[slug]/page.tsx | 134 + .../shop/[category]/[subCategory]/page.tsx | 62 + apps/storefront/src/app/shop/birds/page.tsx | 11 + apps/storefront/src/app/shop/bowl/page.tsx | 11 + apps/storefront/src/app/shop/carrier/page.tsx | 11 + apps/storefront/src/app/shop/cats/page.tsx | 11 + apps/storefront/src/app/shop/dogs/page.tsx | 11 + apps/storefront/src/app/shop/food/page.tsx | 11 + apps/storefront/src/app/shop/litter/page.tsx | 11 + apps/storefront/src/app/shop/not-found.tsx | 25 + apps/storefront/src/app/shop/page.tsx | 11 + .../src/app/shop/recently-added/page.tsx | 12 + apps/storefront/src/app/shop/sale/page.tsx | 19 + .../src/app/shop/small-pets/page.tsx | 11 + .../src/app/shop/top-picks/page.tsx | 19 + apps/storefront/src/app/shop/toys/page.tsx | 11 + apps/storefront/src/app/shop/treats/page.tsx | 11 + .../src/app/sign-in/[[...sign-in]]/page.tsx | 9 + .../src/app/sign-up/[[...sign-up]]/page.tsx | 9 + apps/storefront/src/app/wishlist/layout.tsx | 18 + apps/storefront/src/app/wishlist/loading.tsx | 5 + apps/storefront/src/app/wishlist/page.tsx | 13 + .../src/components/cart/CartUIContext.tsx | 19 + .../src/components/cart/CartUIProvider.tsx | 39 + .../cart/containers/CartBottomSheet.tsx | 105 + .../cart/containers/CartPageLayout.tsx | 40 + .../cart/containers/CartSideDrawer.tsx | 104 + .../components/cart/content/CartActions.tsx | 73 + .../components/cart/content/CartContent.tsx | 105 + .../cart/content/CartEmptyState.tsx | 61 + .../components/cart/content/CartItemCard.tsx | 145 + .../components/cart/content/CartLineItems.tsx | 34 + .../cart/content/CartOrderSummary.tsx | 60 + .../cart/state/CartContentSkeleton.tsx | 73 + .../components/cart/state/CartErrorState.tsx | 35 + .../src/components/checkout/CheckoutShell.tsx | 107 + .../checkout/content/AddressForm.tsx | 306 + .../checkout/content/AddressSelector.tsx | 125 + .../content/AddressValidationFeedback.tsx | 250 + .../checkout/content/CheckoutLineItems.tsx | 277 + .../checkout/content/CheckoutOrderSummary.tsx | 43 + .../checkout/state/CheckoutErrorState.tsx | 34 + .../checkout/state/CheckoutSkeleton.tsx | 72 + .../checkout/steps/CartValidationStep.tsx | 99 + .../checkout/steps/OrderReviewStep.tsx | 341 + .../components/checkout/steps/PaymentStep.tsx | 275 + .../checkout/steps/ShippingAddressStep.tsx | 395 + .../src/components/layout/BrandLogo.tsx | 32 + .../src/components/layout/ToastProvider.tsx | 7 + .../src/components/layout/footer/Footer.tsx | 252 + .../src/components/layout/header/Header.tsx | 20 + .../layout/header/HeaderSearchBar.tsx | 212 + .../src/components/layout/header/code.html | 242 + .../layout/header/desktop/BottomNav.tsx | 191 + .../layout/header/desktop/CoreBrandBar.tsx | 107 + .../layout/header/desktop/DesktopHeader.tsx | 13 + .../header/desktop/HeaderUserAction.tsx | 78 + .../layout/header/desktop/TopUtilityBar.tsx | 74 + .../header/mobile/MobileCoreBrandBar.tsx | 97 + .../layout/header/mobile/MobileHeader.tsx | 99 + .../header/mobile/MobileHeaderUserAction.tsx | 70 + .../layout/header/mobile/MobileNavButtons.tsx | 139 + .../layout/header/mobile/MobileUtilityBar.tsx | 21 + .../src/components/layout/header/screen.png | Bin 0 -> 323537 bytes .../components/orders/OrderDetailPageView.tsx | 152 + .../src/components/orders/OrdersPageView.tsx | 51 + .../orders/actions/CancelOrderDialog.tsx | 60 + .../orders/actions/ReorderConfirmDialog.tsx | 62 + .../components/orders/detail/OrderActions.tsx | 78 + .../orders/detail/OrderAddresses.tsx | 61 + .../components/orders/detail/OrderHeader.tsx | 61 + .../orders/detail/OrderLineItems.tsx | 91 + .../orders/detail/OrderPriceSummary.tsx | 69 + .../orders/detail/OrderTrackingInfo.tsx | 108 + .../src/components/orders/list/OrderCard.tsx | 84 + .../components/orders/list/OrderCardList.tsx | 48 + .../orders/list/OrderStatusFilter.tsx | 30 + .../orders/state/OrderDetailSkeleton.tsx | 93 + .../orders/state/OrdersEmptyState.tsx | 27 + .../orders/state/OrdersErrorState.tsx | 37 + .../orders/state/OrdersSkeleton.tsx | 44 + .../product-detail/ProductDetailContent.tsx | 39 + .../ProductDetailStructuredData.tsx | 75 + .../reviews/ProductDetailReviewsPanel.tsx | 78 + .../product-detail/reviews/ReviewCard.tsx | 46 + .../product-detail/reviews/ReviewForm.tsx | 149 + .../product-detail/reviews/ReviewList.tsx | 52 + .../product-detail/reviews/ReviewSortBar.tsx | 69 + .../reviews/StarRatingDisplay.tsx | 49 + .../reviews/StarRatingInput.tsx | 38 + .../ProductDetailAttributesSection.tsx | 88 + .../ProductDetailDescriptionSection.tsx | 25 + .../sections/ProductDetailHeroSection.tsx | 595 + .../ProductDetailHeroSectionWrapper.tsx | 31 + .../sections/ProductDetailRelatedSection.tsx | 58 + .../sections/ProductDetailReviewsSection.tsx | 185 + .../sections/ProductDetailSectionsWrapper.tsx | 64 + .../sections/ProductDetailTabsSection.tsx | 76 + .../state/ProductDetailAttributesSkeleton.tsx | 26 + .../ProductDetailDescriptionSkeleton.tsx | 21 + .../state/ProductDetailErrorState.tsx | 40 + .../state/ProductDetailHeroSkeleton.tsx | 60 + .../state/ProductDetailRelatedSkeleton.tsx | 53 + .../state/ProductDetailReviewsSkeleton.tsx | 35 + .../src/components/product/ProductCard.tsx | 231 + .../components/product/ProductListTile.tsx | 219 + .../product/ProductListTileSkeleton.tsx | 23 + .../components/search/SearchEmptyState.tsx | 21 + .../components/search/SearchLoadingState.tsx | 24 + .../components/search/SearchMinCharsHint.tsx | 7 + .../components/search/SearchResultItem.tsx | 89 + .../components/search/SearchResultsPanel.tsx | 99 + .../hompepage/category/CategoryIcons.tsx | 164 + .../hompepage/category/CategorySection.tsx | 118 + .../sections/hompepage/cta/CtaSection.tsx | 142 + .../newsletter/NewsletterSection.tsx | 79 + .../recently-added/RecentlyAddedSection.tsx | 72 + .../special-offers/SpecialOffersSection.tsx | 73 + .../top-picks/TopPicsSection.tsx | 76 + .../hompepage/wishlist/WishlistSection.tsx | 89 + .../src/components/shop/PetCategoryPage.tsx | 233 + .../src/components/shop/RecentlyAddedPage.tsx | 180 + .../src/components/shop/ShopBreadcrumbBar.tsx | 46 + .../src/components/shop/ShopCategoryStrip.tsx | 33 + .../src/components/shop/ShopFilterContent.tsx | 326 + .../src/components/shop/ShopFilterModal.tsx | 101 + .../src/components/shop/ShopFilterSidebar.tsx | 48 + .../src/components/shop/ShopIndexContent.tsx | 181 + .../src/components/shop/ShopPageBanner.tsx | 23 + .../src/components/shop/ShopProductGrid.tsx | 40 + .../src/components/shop/ShopToolbar.tsx | 109 + .../shop/SubCategoryPageContent.tsx | 249 + .../src/components/shop/TagShopPage.tsx | 186 + .../src/components/shop/TopCategoryPage.tsx | 289 + .../components/shop/state/ShopEmptyState.tsx | 24 + .../components/shop/state/ShopErrorState.tsx | 34 + .../shop/state/ShopProductGridSkeleton.tsx | 48 + .../storefront/src/components/utils/code.html | 547 + .../wishlist/RemoveFromWishlistDialog.tsx | 58 + .../components/wishlist/WishlistActions.tsx | 53 + .../components/wishlist/WishlistItemCard.tsx | 147 + .../components/wishlist/WishlistItemGrid.tsx | 35 + .../components/wishlist/WishlistPageView.tsx | 99 + .../wishlist/state/WishlistEmptyState.tsx | 52 + .../wishlist/state/WishlistSignInPrompt.tsx | 52 + .../wishlist/state/WishlistSkeleton.tsx | 35 + .../src/hooks/useStoreUserEffect.ts | 31 + apps/storefront/src/lib/cart/README.md | 33 + .../src/lib/cart/addToCartErrors.ts | 45 + apps/storefront/src/lib/cart/analytics.ts | 29 + apps/storefront/src/lib/cart/constants.ts | 29 + apps/storefront/src/lib/cart/index.ts | 14 + apps/storefront/src/lib/cart/types.ts | 62 + apps/storefront/src/lib/cart/useCart.ts | 57 + .../src/lib/cart/useCartMutations.ts | 59 + .../src/lib/cart/useCartSessionId.ts | 19 + apps/storefront/src/lib/cart/useMediaQuery.ts | 21 + apps/storefront/src/lib/checkout/constants.ts | 17 + apps/storefront/src/lib/checkout/index.ts | 31 + .../lib/checkout/phase5-integration.test.ts | 284 + .../src/lib/checkout/scaffold.test.ts | 46 + .../storefront/src/lib/checkout/types.test.ts | 241 + apps/storefront/src/lib/checkout/types.ts | 143 + .../lib/checkout/useAddressMutations.test.tsx | 196 + .../src/lib/checkout/useAddressMutations.ts | 85 + .../checkout/useAddressValidation.test.tsx | 227 + .../src/lib/checkout/useAddressValidation.ts | 65 + .../src/lib/checkout/useCartValidation.ts | 37 + .../checkout/useShippingAddresses.test.tsx | 174 + .../src/lib/checkout/useShippingAddresses.ts | 44 + .../src/lib/checkout/useShippingRate.test.tsx | 268 + .../src/lib/checkout/useShippingRate.ts | 75 + apps/storefront/src/lib/orders/constants.ts | 91 + apps/storefront/src/lib/orders/index.ts | 21 + apps/storefront/src/lib/orders/types.ts | 79 + .../src/lib/orders/useOrderActions.test.tsx | 251 + .../src/lib/orders/useOrderActions.ts | 67 + .../src/lib/orders/useOrderDetail.test.tsx | 145 + .../src/lib/orders/useOrderDetail.ts | 22 + .../src/lib/orders/useOrders.test.tsx | 182 + apps/storefront/src/lib/orders/useOrders.ts | 31 + .../src/lib/product-detail/constants.ts | 65 + .../src/lib/product-detail/types.ts | 113 + apps/storefront/src/lib/search/constants.ts | 13 + apps/storefront/src/lib/search/index.ts | 9 + apps/storefront/src/lib/search/types.ts | 30 + .../src/lib/search/useClickOutside.test.tsx | 145 + .../src/lib/search/useClickOutside.ts | 26 + .../src/lib/search/useProductSearch.test.tsx | 390 + .../src/lib/search/useProductSearch.ts | 131 + .../src/lib/session/SessionCartMerge.tsx | 51 + .../src/lib/session/StoreUserSync.tsx | 8 + .../src/lib/session/constants.test.ts | 21 + apps/storefront/src/lib/session/constants.ts | 14 + .../storefront/src/lib/session/cookie.test.ts | 86 + apps/storefront/src/lib/session/cookie.ts | 76 + apps/storefront/src/lib/session/index.ts | 20 + apps/storefront/src/lib/session/types.test.ts | 31 + apps/storefront/src/lib/session/types.ts | 26 + .../src/lib/session/useCartSession.test.tsx | 88 + .../src/lib/session/useCartSession.ts | 44 + apps/storefront/src/lib/shop/constants.ts | 39 + apps/storefront/src/lib/shop/filterState.ts | 133 + apps/storefront/src/lib/shop/navCategories.ts | 93 + apps/storefront/src/lib/shop/productMapper.ts | 58 + apps/storefront/src/lib/stripe.ts | 5 + apps/storefront/src/lib/wishlist/constants.ts | 28 + apps/storefront/src/lib/wishlist/index.ts | 10 + apps/storefront/src/lib/wishlist/types.ts | 36 + .../src/lib/wishlist/useWishlist.ts | 30 + .../src/lib/wishlist/useWishlistCount.ts | 9 + .../src/lib/wishlist/useWishlistMutations.ts | 51 + apps/storefront/src/middleware.ts | 32 + .../utils/common/heading/section_heading.tsx | 35 + apps/storefront/tsconfig.json | 39 + convex/addresses.test.ts | 337 + convex/addresses.ts | 168 + convex/auth.config.ts | 10 + convex/carts.test.ts | 121 + convex/carts.ts | 237 + convex/categories.test.ts | 124 + convex/categories.ts | 120 + convex/checkout.test.ts | 447 + convex/checkout.ts | 92 + convex/checkoutActions.test.ts | 331 + convex/checkoutActions.ts | 136 + convex/http.ts | 80 + convex/model/carts.ts | 80 + convex/model/categories.ts | 11 + convex/model/checkout.test.ts | 127 + convex/model/checkout.ts | 257 + convex/model/orders.ts | 85 + convex/model/products.ts | 80 + convex/model/shippo.test.ts | 775 + convex/model/shippo.ts | 380 + convex/model/stripe.ts | 24 + convex/model/users.ts | 38 + convex/orders.test.ts | 564 + convex/orders.ts | 547 + convex/products.test.ts | 840 + convex/products.ts | 929 + convex/reviews.test.ts | 291 + convex/reviews.ts | 269 + convex/schema.ts | 291 + convex/stripeActions.test.ts | 490 + convex/stripeActions.ts | 238 + convex/users.test.ts | 134 + convex/users.ts | 140 + convex/wishlists.test.ts | 166 + convex/wishlists.ts | 183 + package-lock.json | 14444 ++++++++++++++++ package.json | 54 + packages/convex/package.json | 18 + packages/convex/src/index.ts | 1 + packages/convex/src/provider.tsx | 20 + packages/types/package.json | 10 + packages/types/src/index.ts | 218 + packages/utils/package.json | 13 + packages/utils/src/index.ts | 191 + tsconfig.json | 20 + turbo.json | 21 + vitest.config.ts | 8 + 361 files changed, 45005 insertions(+) create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 apps/storefront/.env.example create mode 100644 apps/storefront/next-env.d.ts create mode 100644 apps/storefront/next.config.js create mode 100644 apps/storefront/package.json create mode 100644 apps/storefront/pet-palette.html create mode 100644 apps/storefront/postcss.config.js create mode 100644 apps/storefront/public/branding/logo.svg create mode 100644 apps/storefront/public/content/illustrations/404_not_found.svg create mode 100644 apps/storefront/public/content/news-letter.png create mode 100644 apps/storefront/public/content/newsletter-dog.png create mode 100644 apps/storefront/public/content/place-holders/product-images/product-01.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-02.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-03.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-04.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-05.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-06.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-07.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-08.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-09.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-10.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-11.png create mode 100644 apps/storefront/public/content/place-holders/product-images/product-12.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-13.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-14.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-15.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-16.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-17.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-18.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-19.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-20.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-21.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-22.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-23.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-24.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-25.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-26.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-27.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-28.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-29.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-30.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-31.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-32.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-33.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-34.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-35.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-36.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-37.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-38.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-39.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-40.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-41.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-42.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-43.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-44.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-45.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-46.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-47.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-48.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-49.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-49.png create mode 100644 apps/storefront/public/content/place-holders/product-images/product-50.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-51.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-52.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-53.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-54.jpg create mode 100644 apps/storefront/public/content/place-holders/product-images/product-55.jpg create mode 100644 apps/storefront/public/icons/icon_bowl.svg create mode 100644 apps/storefront/public/icons/icon_carrier.svg create mode 100644 apps/storefront/public/icons/icon_food.svg create mode 100644 apps/storefront/public/icons/icon_litter.svg create mode 100644 apps/storefront/public/icons/icon_toys.svg create mode 100644 apps/storefront/public/icons/icon_treats.svg create mode 100644 apps/storefront/public/images/cta/cta-01.webp create mode 100644 apps/storefront/public/images/cta/cta-011.webp create mode 100644 apps/storefront/public/images/cta/cta-02.webp create mode 100644 apps/storefront/public/images/cta/cta-03.webp create mode 100644 apps/storefront/src/app/account/layout.tsx create mode 100644 apps/storefront/src/app/account/orders/[orderId]/page.tsx create mode 100644 apps/storefront/src/app/account/orders/loading.tsx create mode 100644 apps/storefront/src/app/account/orders/page.tsx create mode 100644 apps/storefront/src/app/account/page.tsx create mode 100644 apps/storefront/src/app/cart/CartPageView.tsx create mode 100644 apps/storefront/src/app/cart/layout.tsx create mode 100644 apps/storefront/src/app/cart/loading.tsx create mode 100644 apps/storefront/src/app/cart/page.tsx create mode 100644 apps/storefront/src/app/checkout/CheckoutPageView.tsx create mode 100644 apps/storefront/src/app/checkout/layout.tsx create mode 100644 apps/storefront/src/app/checkout/loading.tsx create mode 100644 apps/storefront/src/app/checkout/page.tsx create mode 100644 apps/storefront/src/app/checkout/success/page.tsx create mode 100644 apps/storefront/src/app/globals.css create mode 100644 apps/storefront/src/app/layout.tsx create mode 100644 apps/storefront/src/app/not-found.tsx create mode 100644 apps/storefront/src/app/page.tsx create mode 100644 apps/storefront/src/app/shop/[category]/[subCategory]/[slug]/loading.tsx create mode 100644 apps/storefront/src/app/shop/[category]/[subCategory]/[slug]/not-found.tsx create mode 100644 apps/storefront/src/app/shop/[category]/[subCategory]/[slug]/page.tsx create mode 100644 apps/storefront/src/app/shop/[category]/[subCategory]/page.tsx create mode 100644 apps/storefront/src/app/shop/birds/page.tsx create mode 100644 apps/storefront/src/app/shop/bowl/page.tsx create mode 100644 apps/storefront/src/app/shop/carrier/page.tsx create mode 100644 apps/storefront/src/app/shop/cats/page.tsx create mode 100644 apps/storefront/src/app/shop/dogs/page.tsx create mode 100644 apps/storefront/src/app/shop/food/page.tsx create mode 100644 apps/storefront/src/app/shop/litter/page.tsx create mode 100644 apps/storefront/src/app/shop/not-found.tsx create mode 100644 apps/storefront/src/app/shop/page.tsx create mode 100644 apps/storefront/src/app/shop/recently-added/page.tsx create mode 100644 apps/storefront/src/app/shop/sale/page.tsx create mode 100644 apps/storefront/src/app/shop/small-pets/page.tsx create mode 100644 apps/storefront/src/app/shop/top-picks/page.tsx create mode 100644 apps/storefront/src/app/shop/toys/page.tsx create mode 100644 apps/storefront/src/app/shop/treats/page.tsx create mode 100644 apps/storefront/src/app/sign-in/[[...sign-in]]/page.tsx create mode 100644 apps/storefront/src/app/sign-up/[[...sign-up]]/page.tsx create mode 100644 apps/storefront/src/app/wishlist/layout.tsx create mode 100644 apps/storefront/src/app/wishlist/loading.tsx create mode 100644 apps/storefront/src/app/wishlist/page.tsx create mode 100644 apps/storefront/src/components/cart/CartUIContext.tsx create mode 100644 apps/storefront/src/components/cart/CartUIProvider.tsx create mode 100644 apps/storefront/src/components/cart/containers/CartBottomSheet.tsx create mode 100644 apps/storefront/src/components/cart/containers/CartPageLayout.tsx create mode 100644 apps/storefront/src/components/cart/containers/CartSideDrawer.tsx create mode 100644 apps/storefront/src/components/cart/content/CartActions.tsx create mode 100644 apps/storefront/src/components/cart/content/CartContent.tsx create mode 100644 apps/storefront/src/components/cart/content/CartEmptyState.tsx create mode 100644 apps/storefront/src/components/cart/content/CartItemCard.tsx create mode 100644 apps/storefront/src/components/cart/content/CartLineItems.tsx create mode 100644 apps/storefront/src/components/cart/content/CartOrderSummary.tsx create mode 100644 apps/storefront/src/components/cart/state/CartContentSkeleton.tsx create mode 100644 apps/storefront/src/components/cart/state/CartErrorState.tsx create mode 100644 apps/storefront/src/components/checkout/CheckoutShell.tsx create mode 100644 apps/storefront/src/components/checkout/content/AddressForm.tsx create mode 100644 apps/storefront/src/components/checkout/content/AddressSelector.tsx create mode 100644 apps/storefront/src/components/checkout/content/AddressValidationFeedback.tsx create mode 100644 apps/storefront/src/components/checkout/content/CheckoutLineItems.tsx create mode 100644 apps/storefront/src/components/checkout/content/CheckoutOrderSummary.tsx create mode 100644 apps/storefront/src/components/checkout/state/CheckoutErrorState.tsx create mode 100644 apps/storefront/src/components/checkout/state/CheckoutSkeleton.tsx create mode 100644 apps/storefront/src/components/checkout/steps/CartValidationStep.tsx create mode 100644 apps/storefront/src/components/checkout/steps/OrderReviewStep.tsx create mode 100644 apps/storefront/src/components/checkout/steps/PaymentStep.tsx create mode 100644 apps/storefront/src/components/checkout/steps/ShippingAddressStep.tsx create mode 100644 apps/storefront/src/components/layout/BrandLogo.tsx create mode 100644 apps/storefront/src/components/layout/ToastProvider.tsx create mode 100644 apps/storefront/src/components/layout/footer/Footer.tsx create mode 100644 apps/storefront/src/components/layout/header/Header.tsx create mode 100644 apps/storefront/src/components/layout/header/HeaderSearchBar.tsx create mode 100644 apps/storefront/src/components/layout/header/code.html create mode 100644 apps/storefront/src/components/layout/header/desktop/BottomNav.tsx create mode 100644 apps/storefront/src/components/layout/header/desktop/CoreBrandBar.tsx create mode 100644 apps/storefront/src/components/layout/header/desktop/DesktopHeader.tsx create mode 100644 apps/storefront/src/components/layout/header/desktop/HeaderUserAction.tsx create mode 100644 apps/storefront/src/components/layout/header/desktop/TopUtilityBar.tsx create mode 100644 apps/storefront/src/components/layout/header/mobile/MobileCoreBrandBar.tsx create mode 100644 apps/storefront/src/components/layout/header/mobile/MobileHeader.tsx create mode 100644 apps/storefront/src/components/layout/header/mobile/MobileHeaderUserAction.tsx create mode 100644 apps/storefront/src/components/layout/header/mobile/MobileNavButtons.tsx create mode 100644 apps/storefront/src/components/layout/header/mobile/MobileUtilityBar.tsx create mode 100644 apps/storefront/src/components/layout/header/screen.png create mode 100644 apps/storefront/src/components/orders/OrderDetailPageView.tsx create mode 100644 apps/storefront/src/components/orders/OrdersPageView.tsx create mode 100644 apps/storefront/src/components/orders/actions/CancelOrderDialog.tsx create mode 100644 apps/storefront/src/components/orders/actions/ReorderConfirmDialog.tsx create mode 100644 apps/storefront/src/components/orders/detail/OrderActions.tsx create mode 100644 apps/storefront/src/components/orders/detail/OrderAddresses.tsx create mode 100644 apps/storefront/src/components/orders/detail/OrderHeader.tsx create mode 100644 apps/storefront/src/components/orders/detail/OrderLineItems.tsx create mode 100644 apps/storefront/src/components/orders/detail/OrderPriceSummary.tsx create mode 100644 apps/storefront/src/components/orders/detail/OrderTrackingInfo.tsx create mode 100644 apps/storefront/src/components/orders/list/OrderCard.tsx create mode 100644 apps/storefront/src/components/orders/list/OrderCardList.tsx create mode 100644 apps/storefront/src/components/orders/list/OrderStatusFilter.tsx create mode 100644 apps/storefront/src/components/orders/state/OrderDetailSkeleton.tsx create mode 100644 apps/storefront/src/components/orders/state/OrdersEmptyState.tsx create mode 100644 apps/storefront/src/components/orders/state/OrdersErrorState.tsx create mode 100644 apps/storefront/src/components/orders/state/OrdersSkeleton.tsx create mode 100644 apps/storefront/src/components/product-detail/ProductDetailContent.tsx create mode 100644 apps/storefront/src/components/product-detail/ProductDetailStructuredData.tsx create mode 100644 apps/storefront/src/components/product-detail/reviews/ProductDetailReviewsPanel.tsx create mode 100644 apps/storefront/src/components/product-detail/reviews/ReviewCard.tsx create mode 100644 apps/storefront/src/components/product-detail/reviews/ReviewForm.tsx create mode 100644 apps/storefront/src/components/product-detail/reviews/ReviewList.tsx create mode 100644 apps/storefront/src/components/product-detail/reviews/ReviewSortBar.tsx create mode 100644 apps/storefront/src/components/product-detail/reviews/StarRatingDisplay.tsx create mode 100644 apps/storefront/src/components/product-detail/reviews/StarRatingInput.tsx create mode 100644 apps/storefront/src/components/product-detail/sections/ProductDetailAttributesSection.tsx create mode 100644 apps/storefront/src/components/product-detail/sections/ProductDetailDescriptionSection.tsx create mode 100644 apps/storefront/src/components/product-detail/sections/ProductDetailHeroSection.tsx create mode 100644 apps/storefront/src/components/product-detail/sections/ProductDetailHeroSectionWrapper.tsx create mode 100644 apps/storefront/src/components/product-detail/sections/ProductDetailRelatedSection.tsx create mode 100644 apps/storefront/src/components/product-detail/sections/ProductDetailReviewsSection.tsx create mode 100644 apps/storefront/src/components/product-detail/sections/ProductDetailSectionsWrapper.tsx create mode 100644 apps/storefront/src/components/product-detail/sections/ProductDetailTabsSection.tsx create mode 100644 apps/storefront/src/components/product-detail/state/ProductDetailAttributesSkeleton.tsx create mode 100644 apps/storefront/src/components/product-detail/state/ProductDetailDescriptionSkeleton.tsx create mode 100644 apps/storefront/src/components/product-detail/state/ProductDetailErrorState.tsx create mode 100644 apps/storefront/src/components/product-detail/state/ProductDetailHeroSkeleton.tsx create mode 100644 apps/storefront/src/components/product-detail/state/ProductDetailRelatedSkeleton.tsx create mode 100644 apps/storefront/src/components/product-detail/state/ProductDetailReviewsSkeleton.tsx create mode 100644 apps/storefront/src/components/product/ProductCard.tsx create mode 100644 apps/storefront/src/components/product/ProductListTile.tsx create mode 100644 apps/storefront/src/components/product/ProductListTileSkeleton.tsx create mode 100644 apps/storefront/src/components/search/SearchEmptyState.tsx create mode 100644 apps/storefront/src/components/search/SearchLoadingState.tsx create mode 100644 apps/storefront/src/components/search/SearchMinCharsHint.tsx create mode 100644 apps/storefront/src/components/search/SearchResultItem.tsx create mode 100644 apps/storefront/src/components/search/SearchResultsPanel.tsx create mode 100644 apps/storefront/src/components/sections/hompepage/category/CategoryIcons.tsx create mode 100644 apps/storefront/src/components/sections/hompepage/category/CategorySection.tsx create mode 100644 apps/storefront/src/components/sections/hompepage/cta/CtaSection.tsx create mode 100644 apps/storefront/src/components/sections/hompepage/newsletter/NewsletterSection.tsx create mode 100644 apps/storefront/src/components/sections/hompepage/products-sections/recently-added/RecentlyAddedSection.tsx create mode 100644 apps/storefront/src/components/sections/hompepage/products-sections/special-offers/SpecialOffersSection.tsx create mode 100644 apps/storefront/src/components/sections/hompepage/products-sections/top-picks/TopPicsSection.tsx create mode 100644 apps/storefront/src/components/sections/hompepage/wishlist/WishlistSection.tsx create mode 100644 apps/storefront/src/components/shop/PetCategoryPage.tsx create mode 100644 apps/storefront/src/components/shop/RecentlyAddedPage.tsx create mode 100644 apps/storefront/src/components/shop/ShopBreadcrumbBar.tsx create mode 100644 apps/storefront/src/components/shop/ShopCategoryStrip.tsx create mode 100644 apps/storefront/src/components/shop/ShopFilterContent.tsx create mode 100644 apps/storefront/src/components/shop/ShopFilterModal.tsx create mode 100644 apps/storefront/src/components/shop/ShopFilterSidebar.tsx create mode 100644 apps/storefront/src/components/shop/ShopIndexContent.tsx create mode 100644 apps/storefront/src/components/shop/ShopPageBanner.tsx create mode 100644 apps/storefront/src/components/shop/ShopProductGrid.tsx create mode 100644 apps/storefront/src/components/shop/ShopToolbar.tsx create mode 100644 apps/storefront/src/components/shop/SubCategoryPageContent.tsx create mode 100644 apps/storefront/src/components/shop/TagShopPage.tsx create mode 100644 apps/storefront/src/components/shop/TopCategoryPage.tsx create mode 100644 apps/storefront/src/components/shop/state/ShopEmptyState.tsx create mode 100644 apps/storefront/src/components/shop/state/ShopErrorState.tsx create mode 100644 apps/storefront/src/components/shop/state/ShopProductGridSkeleton.tsx create mode 100644 apps/storefront/src/components/utils/code.html create mode 100644 apps/storefront/src/components/wishlist/RemoveFromWishlistDialog.tsx create mode 100644 apps/storefront/src/components/wishlist/WishlistActions.tsx create mode 100644 apps/storefront/src/components/wishlist/WishlistItemCard.tsx create mode 100644 apps/storefront/src/components/wishlist/WishlistItemGrid.tsx create mode 100644 apps/storefront/src/components/wishlist/WishlistPageView.tsx create mode 100644 apps/storefront/src/components/wishlist/state/WishlistEmptyState.tsx create mode 100644 apps/storefront/src/components/wishlist/state/WishlistSignInPrompt.tsx create mode 100644 apps/storefront/src/components/wishlist/state/WishlistSkeleton.tsx create mode 100644 apps/storefront/src/hooks/useStoreUserEffect.ts create mode 100644 apps/storefront/src/lib/cart/README.md create mode 100644 apps/storefront/src/lib/cart/addToCartErrors.ts create mode 100644 apps/storefront/src/lib/cart/analytics.ts create mode 100644 apps/storefront/src/lib/cart/constants.ts create mode 100644 apps/storefront/src/lib/cart/index.ts create mode 100644 apps/storefront/src/lib/cart/types.ts create mode 100644 apps/storefront/src/lib/cart/useCart.ts create mode 100644 apps/storefront/src/lib/cart/useCartMutations.ts create mode 100644 apps/storefront/src/lib/cart/useCartSessionId.ts create mode 100644 apps/storefront/src/lib/cart/useMediaQuery.ts create mode 100644 apps/storefront/src/lib/checkout/constants.ts create mode 100644 apps/storefront/src/lib/checkout/index.ts create mode 100644 apps/storefront/src/lib/checkout/phase5-integration.test.ts create mode 100644 apps/storefront/src/lib/checkout/scaffold.test.ts create mode 100644 apps/storefront/src/lib/checkout/types.test.ts create mode 100644 apps/storefront/src/lib/checkout/types.ts create mode 100644 apps/storefront/src/lib/checkout/useAddressMutations.test.tsx create mode 100644 apps/storefront/src/lib/checkout/useAddressMutations.ts create mode 100644 apps/storefront/src/lib/checkout/useAddressValidation.test.tsx create mode 100644 apps/storefront/src/lib/checkout/useAddressValidation.ts create mode 100644 apps/storefront/src/lib/checkout/useCartValidation.ts create mode 100644 apps/storefront/src/lib/checkout/useShippingAddresses.test.tsx create mode 100644 apps/storefront/src/lib/checkout/useShippingAddresses.ts create mode 100644 apps/storefront/src/lib/checkout/useShippingRate.test.tsx create mode 100644 apps/storefront/src/lib/checkout/useShippingRate.ts create mode 100644 apps/storefront/src/lib/orders/constants.ts create mode 100644 apps/storefront/src/lib/orders/index.ts create mode 100644 apps/storefront/src/lib/orders/types.ts create mode 100644 apps/storefront/src/lib/orders/useOrderActions.test.tsx create mode 100644 apps/storefront/src/lib/orders/useOrderActions.ts create mode 100644 apps/storefront/src/lib/orders/useOrderDetail.test.tsx create mode 100644 apps/storefront/src/lib/orders/useOrderDetail.ts create mode 100644 apps/storefront/src/lib/orders/useOrders.test.tsx create mode 100644 apps/storefront/src/lib/orders/useOrders.ts create mode 100644 apps/storefront/src/lib/product-detail/constants.ts create mode 100644 apps/storefront/src/lib/product-detail/types.ts create mode 100644 apps/storefront/src/lib/search/constants.ts create mode 100644 apps/storefront/src/lib/search/index.ts create mode 100644 apps/storefront/src/lib/search/types.ts create mode 100644 apps/storefront/src/lib/search/useClickOutside.test.tsx create mode 100644 apps/storefront/src/lib/search/useClickOutside.ts create mode 100644 apps/storefront/src/lib/search/useProductSearch.test.tsx create mode 100644 apps/storefront/src/lib/search/useProductSearch.ts create mode 100644 apps/storefront/src/lib/session/SessionCartMerge.tsx create mode 100644 apps/storefront/src/lib/session/StoreUserSync.tsx create mode 100644 apps/storefront/src/lib/session/constants.test.ts create mode 100644 apps/storefront/src/lib/session/constants.ts create mode 100644 apps/storefront/src/lib/session/cookie.test.ts create mode 100644 apps/storefront/src/lib/session/cookie.ts create mode 100644 apps/storefront/src/lib/session/index.ts create mode 100644 apps/storefront/src/lib/session/types.test.ts create mode 100644 apps/storefront/src/lib/session/types.ts create mode 100644 apps/storefront/src/lib/session/useCartSession.test.tsx create mode 100644 apps/storefront/src/lib/session/useCartSession.ts create mode 100644 apps/storefront/src/lib/shop/constants.ts create mode 100644 apps/storefront/src/lib/shop/filterState.ts create mode 100644 apps/storefront/src/lib/shop/navCategories.ts create mode 100644 apps/storefront/src/lib/shop/productMapper.ts create mode 100644 apps/storefront/src/lib/stripe.ts create mode 100644 apps/storefront/src/lib/wishlist/constants.ts create mode 100644 apps/storefront/src/lib/wishlist/index.ts create mode 100644 apps/storefront/src/lib/wishlist/types.ts create mode 100644 apps/storefront/src/lib/wishlist/useWishlist.ts create mode 100644 apps/storefront/src/lib/wishlist/useWishlistCount.ts create mode 100644 apps/storefront/src/lib/wishlist/useWishlistMutations.ts create mode 100644 apps/storefront/src/middleware.ts create mode 100644 apps/storefront/src/utils/common/heading/section_heading.tsx create mode 100644 apps/storefront/tsconfig.json create mode 100644 convex/addresses.test.ts create mode 100644 convex/addresses.ts create mode 100644 convex/auth.config.ts create mode 100644 convex/carts.test.ts create mode 100644 convex/carts.ts create mode 100644 convex/categories.test.ts create mode 100644 convex/categories.ts create mode 100644 convex/checkout.test.ts create mode 100644 convex/checkout.ts create mode 100644 convex/checkoutActions.test.ts create mode 100644 convex/checkoutActions.ts create mode 100644 convex/http.ts create mode 100644 convex/model/carts.ts create mode 100644 convex/model/categories.ts create mode 100644 convex/model/checkout.test.ts create mode 100644 convex/model/checkout.ts create mode 100644 convex/model/orders.ts create mode 100644 convex/model/products.ts create mode 100644 convex/model/shippo.test.ts create mode 100644 convex/model/shippo.ts create mode 100644 convex/model/stripe.ts create mode 100644 convex/model/users.ts create mode 100644 convex/orders.test.ts create mode 100644 convex/orders.ts create mode 100644 convex/products.test.ts create mode 100644 convex/products.ts create mode 100644 convex/reviews.test.ts create mode 100644 convex/reviews.ts create mode 100644 convex/schema.ts create mode 100644 convex/stripeActions.test.ts create mode 100644 convex/stripeActions.ts create mode 100644 convex/users.test.ts create mode 100644 convex/users.ts create mode 100644 convex/wishlists.test.ts create mode 100644 convex/wishlists.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 packages/convex/package.json create mode 100644 packages/convex/src/index.ts create mode 100644 packages/convex/src/provider.tsx create mode 100644 packages/types/package.json create mode 100644 packages/types/src/index.ts create mode 100644 packages/utils/package.json create mode 100644 packages/utils/src/index.ts create mode 100644 tsconfig.json create mode 100644 turbo.json create mode 100644 vitest.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87422e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Dependencies +node_modules +.pnp +.pnp.js + +# Build outputs +.next +dist +build +out + +# Turbo +.turbo + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Misc +.DS_Store +*.pem +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# TypeScript +*.tsbuildinfo + +# Convex +convex/_generated diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..6bd4af3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,153 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**The Pet Loft** — a full-stack ecommerce platform for pet supplies. Turborepo monorepo with two Next.js apps, a shared Convex backend, and shared packages. + +## Development Commands + +Two processes must run simultaneously for full-stack development: + +```bash +# Terminal 1 — Convex backend (must run first) +npx convex dev + +# Terminal 2 — Next.js apps +npm run dev # Both apps in parallel +npm run dev:storefront # Storefront only (port 3000) +npm run dev:admin # Admin only (port 3001) +``` + +```bash +npm run type-check # TypeScript check across all workspaces +npm run lint # ESLint via Turbo +npm run test # Vitest watch mode (edge-runtime) +npm run test:once # Run all tests once +npm run test:coverage # Coverage report +npm run build # Build both apps +``` + +To run a single test file: `npx vitest run convex/carts.test.ts` + +## Architecture + +### Monorepo Layout + +``` +apps/storefront/ # Customer store — Next.js + HeroUI (port 3000) +apps/admin/ # Staff dashboard — Next.js + ShadCN (port 3001) +convex/ # Serverless backend: schema, functions, HTTP routes, tests +packages/types/ # Shared TypeScript interfaces (Product, Order, User…) +packages/utils/ # Shared helpers: formatPrice, slugify, formatDate +packages/convex/ # ConvexClientProvider (Clerk + Convex integration) +``` + +### Backend: Convex + +All backend logic lives in `convex/`. The schema defines 11 tables: + +- **users** — Clerk-synced; roles: `customer`, `admin`, `super_admin` +- **products** / **productImages** / **productVariants** — catalog with SKUs +- **categories** — hierarchical (parent + topCategory slugs) +- **carts** — guest (`sessionId`) or authenticated (`userId`), 30-day expiry +- **orders** / **orderItems** — price/address snapshots at order time +- **addresses** — shipping/billing with validation flag +- **reviews** / **wishlists** — community features + +Business logic is extracted into `convex/model/*.ts` helpers and reused across public functions. + +### Auth Flow + +1. Clerk handles UI + JWT. `ConvexProviderWithClerk` (from `@repo/convex`) passes the JWT to Convex. +2. `convex/auth.config.ts` trusts the Clerk JWT issuer domain. +3. Convex functions access the user via `ctx.auth.getUserIdentity()`. +4. `convex/model/users.ts` exports `getCurrentUser`, `requireAdmin`, `requireOwnership`. +5. Clerk webhooks sync user changes to the `users` table via `convex/http.ts`. + +### Guest Cart & Session + +- A guest `sessionId` is generated on first load and stored in a cookie (`apps/storefront/src/lib/session/`). +- All cart/wishlist queries accept either `userId` (signed-in) or `sessionId` (guest). +- On sign-in, `SessionCartMerge` component triggers a merge mutation to fold the guest cart into the user's cart. + +### Checkout Flow + +1. Address validation — `convex/model/checkout.ts` + Convex address functions +2. Shipping rates — Shippo API via `convex/model/shippo.ts` +3. Payment intent — Stripe via `convex/stripeActions.ts` +4. Order creation — snapshots of items, addresses, and pricing captured in `convex/orders.ts` +5. Stripe webhook — handled in `convex/http.ts`, updates order payment status + +## Convex Function Conventions + +Always use the new function syntax with explicit validators: + +```typescript +import { query, mutation } from "./_generated/server"; +import { v } from "convex/values"; + +export const myFunction = query({ + args: { id: v.id("products") }, + handler: async (ctx, args) => { ... }, +}); +``` + +- Use `internalQuery` / `internalMutation` / `internalAction` for private functions. +- HTTP endpoints go in `convex/http.ts` using `httpAction`. +- Use `ctx.runQuery` / `ctx.runMutation` from actions; avoid chaining many calls (race conditions). +- Leverage index-based queries over full table scans. + +## Storefront UI Rules + +**HeroUI** is the component library for the storefront. Two mandatory overrides: + +| Situation | Use | +|-----------|-----| +| Internal navigation | `next/link` `` (never `@heroui/react` `Link`) | +| Content images | `next/image` `` (never raw ``) | + +Preserve semantic HTML (`
`, `
`, `