@doswiftly/cli 0.1.18 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -323
- package/dist/commands/check.js +1 -1
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/deploy.d.ts +20 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +249 -17
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +4 -4
- package/dist/commands/sdk.js +5 -5
- package/dist/commands/sdk.js.map +1 -1
- package/dist/commands/template.js +4 -4
- package/dist/commands/template.js.map +1 -1
- package/dist/commands/types.js +5 -5
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/verify.js +2 -2
- package/dist/commands/verify.js.map +1 -1
- package/dist/lib/package-manager.d.ts +1 -1
- package/dist/lib/package-manager.js +1 -1
- package/package.json +4 -4
- package/templates/storefront-minimal/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-minimal/wrangler.toml +11 -0
- package/templates/storefront-nextjs/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-nextjs/README.md +16 -12
- package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
- package/templates/storefront-nextjs/app/account/page.tsx +2 -2
- package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
- package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
- package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
- package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
- package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
- package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
- package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
- package/templates/storefront-nextjs/app/page.tsx +1 -1
- package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-nextjs/app/products/page.tsx +2 -2
- package/templates/storefront-nextjs/app/search/page.tsx +1 -1
- package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
- package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
- package/templates/storefront-nextjs/components/providers.tsx +1 -1
- package/templates/storefront-nextjs/lib/currency.tsx +3 -3
- package/templates/storefront-nextjs/lib/format.ts +1 -1
- package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
- package/templates/storefront-nextjs/package.dev.json +1 -1
- package/templates/storefront-nextjs/package.json +1 -1
- package/templates/storefront-nextjs/package.json.template +1 -1
- package/templates/storefront-nextjs/wrangler.toml +11 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +10 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
- package/templates/storefront-nextjs-shadcn/CLAUDE.md +172 -35
- package/templates/storefront-nextjs-shadcn/README.md +29 -162
- package/templates/storefront-nextjs-shadcn/app/{about → [locale]/about}/page.tsx +17 -14
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/addresses/page.tsx +226 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/error.tsx +46 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loyalty/page.tsx +89 -193
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/loading.tsx +60 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/page.tsx +119 -0
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/tracking/page.tsx +27 -25
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/page.tsx +101 -0
- package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/page.tsx +9 -7
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/settings/page.tsx +208 -0
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/forgot-password/page.tsx +24 -17
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/login/page.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/register/page.tsx +5 -2
- package/templates/storefront-nextjs-shadcn/app/[locale]/blog/[slug]/loading.tsx +17 -0
- package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/page.tsx +44 -3
- package/templates/storefront-nextjs-shadcn/app/[locale]/blog/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/page.tsx +2 -1
- package/templates/storefront-nextjs-shadcn/app/[locale]/cart/loading.tsx +26 -0
- package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/page.tsx +20 -13
- package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/category-products-client.tsx +58 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/loading.tsx +32 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/page.tsx +95 -0
- package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/page.tsx +21 -12
- package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/loading.tsx +31 -0
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/page.tsx +334 -253
- package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/success/[orderId]/page.tsx +36 -34
- package/templates/storefront-nextjs-shadcn/app/[locale]/collections/[handle]/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/page.tsx +6 -4
- package/templates/storefront-nextjs-shadcn/app/[locale]/collections/loading.tsx +18 -0
- package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/page.tsx +20 -12
- package/templates/storefront-nextjs-shadcn/app/{contact → [locale]/contact}/page.tsx +24 -21
- package/templates/storefront-nextjs-shadcn/app/{error.tsx → [locale]/error.tsx} +13 -8
- package/templates/storefront-nextjs-shadcn/app/[locale]/layout.tsx +92 -0
- package/templates/storefront-nextjs-shadcn/app/{not-found.tsx → [locale]/not-found.tsx} +13 -18
- package/templates/storefront-nextjs-shadcn/app/{page.tsx → [locale]/page.tsx} +8 -4
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/loading.tsx +29 -0
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/page.tsx +17 -14
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/product-client.tsx +18 -62
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/loading.tsx +32 -0
- package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/page.tsx +6 -3
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/products-client.tsx +450 -0
- package/templates/storefront-nextjs-shadcn/app/[locale]/search/loading.tsx +18 -0
- package/templates/storefront-nextjs-shadcn/app/{wishlist → [locale]/wishlist}/page.tsx +27 -28
- package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
- package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
- package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
- package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
- package/templates/storefront-nextjs-shadcn/app/layout.tsx +8 -35
- package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
- package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +25 -20
- package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +11 -10
- package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +17 -13
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +42 -30
- package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +18 -16
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +37 -58
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +85 -66
- package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +10 -6
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +8 -6
- package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +38 -20
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +15 -25
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +7 -5
- package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +6 -4
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +10 -9
- package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
- package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +9 -6
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +22 -12
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +18 -15
- package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +53 -28
- package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +7 -5
- package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +19 -15
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +13 -10
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +10 -6
- package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +3 -13
- package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +12 -8
- package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +13 -8
- package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
- package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +10 -8
- package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +41 -16
- package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +24 -23
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +52 -34
- package/templates/storefront-nextjs-shadcn/components/layout/language-switcher.tsx +54 -0
- package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +33 -30
- package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +27 -24
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
- package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +32 -42
- package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
- package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
- package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +6 -14
- package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +4 -2
- package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +72 -0
- package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +87 -0
- package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +140 -0
- package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
- package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +11 -37
- package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +179 -124
- package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +3 -7
- package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
- package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +44 -19
- package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
- package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +26 -34
- package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
- package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
- package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
- package/templates/storefront-nextjs-shadcn/components/providers/language-sync-provider.tsx +27 -0
- package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +63 -0
- package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
- package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +59 -72
- package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
- package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +12 -9
- package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +23 -12
- package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
- package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
- package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
- package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
- package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +4 -2
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +2 -10
- package/templates/storefront-nextjs-shadcn/generated/graphql.ts +13387 -0
- package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +3 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +34 -229
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-di.ts +67 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +16 -12
- package/templates/storefront-nextjs-shadcn/i18n/navigation.ts +12 -0
- package/templates/storefront-nextjs-shadcn/i18n/request.ts +17 -0
- package/templates/storefront-nextjs-shadcn/i18n/routing.ts +17 -0
- package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
- package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
- package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +33 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +720 -632
- package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +88 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +132 -182
- package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
- package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
- package/templates/storefront-nextjs-shadcn/messages/en.json +869 -0
- package/templates/storefront-nextjs-shadcn/messages/pl.json +869 -0
- package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
- package/templates/storefront-nextjs-shadcn/next.config.ts +6 -5
- package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
- package/templates/storefront-nextjs-shadcn/package.json +14 -14
- package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
- package/templates/storefront-nextjs-shadcn/proxy.ts +115 -47
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +24 -56
- package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
- package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
- package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
- package/templates/storefront-nextjs-shadcn/wrangler.toml +11 -0
- package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
- package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
- package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
- package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
- package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
- package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
- package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
- package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
- package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +0 -215
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +0 -128
- package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +0 -80
- package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +0 -171
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +0 -78
- package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +0 -192
- package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
- package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
- package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
- package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
- package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
- package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
- package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
- package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
- package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
- package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
- package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
- package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +0 -103
- /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/[slug]/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{returns → [locale]/returns}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/page.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/search-client.tsx +0 -0
- /package/templates/storefront-nextjs-shadcn/app/{shipping → [locale]/shipping}/page.tsx +0 -0
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useTranslations } from "next-intl";
|
|
2
|
+
import { Link } from "@/i18n/navigation";
|
|
2
3
|
import { FileQuestion, Home, ShoppingBag, Search } from "lucide-react";
|
|
3
4
|
import { Button } from "@/components/ui/button";
|
|
4
5
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* 404 Not Found Page
|
|
8
|
-
*
|
|
9
|
-
* Displayed when a page or resource is not found.
|
|
10
|
-
* Provides helpful navigation back to the site.
|
|
11
|
-
*
|
|
12
|
-
* Requirements: 11.1
|
|
8
|
+
* 404 Not Found Page (locale-aware)
|
|
13
9
|
*/
|
|
14
10
|
export default function NotFound() {
|
|
11
|
+
const t = useTranslations("notFound");
|
|
12
|
+
|
|
15
13
|
return (
|
|
16
14
|
<div className="container mx-auto flex min-h-[60vh] items-center justify-center px-4 py-16">
|
|
17
15
|
<Card className="w-full max-w-md text-center">
|
|
@@ -19,46 +17,43 @@ export default function NotFound() {
|
|
|
19
17
|
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-muted">
|
|
20
18
|
<FileQuestion className="h-8 w-8 text-muted-foreground" />
|
|
21
19
|
</div>
|
|
22
|
-
<CardTitle className="text-6xl font-bold">
|
|
20
|
+
<CardTitle className="text-6xl font-bold">{t("title")}</CardTitle>
|
|
23
21
|
<CardDescription className="text-lg">
|
|
24
|
-
|
|
22
|
+
{t("description")}
|
|
25
23
|
</CardDescription>
|
|
26
24
|
</CardHeader>
|
|
27
25
|
<CardContent className="space-y-6">
|
|
28
26
|
<p className="text-muted-foreground">
|
|
29
|
-
|
|
30
|
-
Mogła zostać przeniesiona lub usunięta.
|
|
27
|
+
{t("message")}
|
|
31
28
|
</p>
|
|
32
29
|
|
|
33
|
-
{/* Navigation options */}
|
|
34
30
|
<div className="flex flex-col gap-3">
|
|
35
31
|
<Button asChild size="lg" className="w-full">
|
|
36
32
|
<Link href="/">
|
|
37
33
|
<Home className="mr-2 h-4 w-4" />
|
|
38
|
-
|
|
34
|
+
{t("home")}
|
|
39
35
|
</Link>
|
|
40
36
|
</Button>
|
|
41
37
|
<div className="flex gap-3">
|
|
42
38
|
<Button asChild variant="outline" size="lg" className="flex-1">
|
|
43
39
|
<Link href="/products">
|
|
44
40
|
<ShoppingBag className="mr-2 h-4 w-4" />
|
|
45
|
-
|
|
41
|
+
{t("products")}
|
|
46
42
|
</Link>
|
|
47
43
|
</Button>
|
|
48
44
|
<Button asChild variant="outline" size="lg" className="flex-1">
|
|
49
45
|
<Link href="/collections">
|
|
50
46
|
<Search className="mr-2 h-4 w-4" />
|
|
51
|
-
|
|
47
|
+
{t("collections")}
|
|
52
48
|
</Link>
|
|
53
49
|
</Button>
|
|
54
50
|
</div>
|
|
55
51
|
</div>
|
|
56
52
|
|
|
57
|
-
{/* Help text */}
|
|
58
53
|
<p className="text-sm text-muted-foreground">
|
|
59
|
-
|
|
54
|
+
{t("needHelp")}{" "}
|
|
60
55
|
<Link href="/contact" className="text-primary underline underline-offset-4">
|
|
61
|
-
|
|
56
|
+
{t("contactUs")}
|
|
62
57
|
</Link>
|
|
63
58
|
</p>
|
|
64
59
|
</CardContent>
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import { getTranslations } from "next-intl/server";
|
|
1
2
|
import { HeroSection } from "@/components/home/hero-section";
|
|
2
3
|
import { FeaturedProducts } from "@/components/home/featured-products";
|
|
3
4
|
import { CategoryGrid } from "@/components/home/category-grid";
|
|
4
5
|
import { NewsletterSignup } from "@/components/home/newsletter-signup";
|
|
5
6
|
import type { Metadata } from "next";
|
|
6
7
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
9
|
+
const t = await getTranslations("home");
|
|
10
|
+
return {
|
|
11
|
+
title: "Home | " + (process.env.NEXT_PUBLIC_SITE_NAME || "My Store"),
|
|
12
|
+
description: t("metaDescription"),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
11
15
|
|
|
12
16
|
export default function HomePage() {
|
|
13
17
|
return (
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { Link } from "@/i18n/navigation";
|
|
5
|
+
import { AlertCircle } from "lucide-react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
8
|
+
|
|
9
|
+
export default function ProductError({
|
|
10
|
+
error,
|
|
11
|
+
reset,
|
|
12
|
+
}: {
|
|
13
|
+
error: Error & { digest?: string };
|
|
14
|
+
reset: () => void;
|
|
15
|
+
}) {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
console.error("[Product Error]", error);
|
|
18
|
+
}, [error]);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="container mx-auto flex min-h-[400px] items-center justify-center px-4 py-16">
|
|
22
|
+
<Card className="max-w-md">
|
|
23
|
+
<CardHeader>
|
|
24
|
+
<CardTitle className="flex items-center gap-2 text-destructive">
|
|
25
|
+
<AlertCircle className="h-5 w-5" />
|
|
26
|
+
Product not available
|
|
27
|
+
</CardTitle>
|
|
28
|
+
</CardHeader>
|
|
29
|
+
<CardContent className="space-y-4">
|
|
30
|
+
<p className="text-sm text-muted-foreground">
|
|
31
|
+
We couldn't load this product. It may have been removed or is temporarily unavailable.
|
|
32
|
+
</p>
|
|
33
|
+
<div className="flex gap-3">
|
|
34
|
+
<Button onClick={reset}>Try again</Button>
|
|
35
|
+
<Button variant="outline" asChild>
|
|
36
|
+
<Link href="/products">Browse products</Link>
|
|
37
|
+
</Button>
|
|
38
|
+
</div>
|
|
39
|
+
</CardContent>
|
|
40
|
+
</Card>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
2
|
+
|
|
3
|
+
export default function ProductLoading() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="container mx-auto px-4 py-8">
|
|
6
|
+
<Skeleton className="mb-6 h-5 w-64" />
|
|
7
|
+
<div className="grid gap-8 lg:grid-cols-2">
|
|
8
|
+
{/* Image gallery */}
|
|
9
|
+
<Skeleton className="aspect-square w-full rounded-lg" />
|
|
10
|
+
{/* Product info */}
|
|
11
|
+
<div className="space-y-4">
|
|
12
|
+
<Skeleton className="h-8 w-3/4" />
|
|
13
|
+
<Skeleton className="h-6 w-32" />
|
|
14
|
+
<Skeleton className="h-4 w-full" />
|
|
15
|
+
<Skeleton className="h-4 w-5/6" />
|
|
16
|
+
<div className="space-y-2 pt-4">
|
|
17
|
+
<Skeleton className="h-5 w-20" />
|
|
18
|
+
<div className="flex gap-2">
|
|
19
|
+
{[...Array(4)].map((_, i) => (
|
|
20
|
+
<Skeleton key={i} className="h-10 w-16 rounded-md" />
|
|
21
|
+
))}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<Skeleton className="mt-6 h-12 w-full rounded-md" />
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/page.tsx
RENAMED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Metadata } from "next";
|
|
2
2
|
import { notFound } from "next/navigation";
|
|
3
|
+
import { getTranslations } from "next-intl/server";
|
|
3
4
|
import { Breadcrumbs } from "@/components/layout/breadcrumbs";
|
|
4
5
|
import { ProductClient } from "./product-client";
|
|
5
6
|
import { fetchProduct, fetchProducts } from "@/lib/graphql/server";
|
|
7
|
+
import type { ProductQuery } from "@/generated/graphql";
|
|
6
8
|
|
|
7
9
|
// ============================================================================
|
|
8
10
|
// METADATA GENERATION
|
|
@@ -25,22 +27,23 @@ export async function generateMetadata({
|
|
|
25
27
|
params: Promise<{ slug: string }>;
|
|
26
28
|
}): Promise<Metadata> {
|
|
27
29
|
try {
|
|
30
|
+
const t = await getTranslations("productPage");
|
|
28
31
|
const resolvedParams = await params;
|
|
29
|
-
|
|
32
|
+
|
|
30
33
|
if (!resolvedParams?.slug) {
|
|
31
34
|
return {
|
|
32
|
-
title: "
|
|
33
|
-
description: "
|
|
35
|
+
title: t("title"),
|
|
36
|
+
description: t("viewDetails"),
|
|
34
37
|
};
|
|
35
38
|
}
|
|
36
|
-
|
|
39
|
+
|
|
37
40
|
const data = await fetchProduct(resolvedParams.slug);
|
|
38
41
|
const product = data?.product;
|
|
39
42
|
|
|
40
43
|
if (!product) {
|
|
41
44
|
return {
|
|
42
|
-
title: "
|
|
43
|
-
description:
|
|
45
|
+
title: t("notFound"),
|
|
46
|
+
description: t("notFoundDescription"),
|
|
44
47
|
};
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -87,9 +90,10 @@ export async function generateMetadata({
|
|
|
87
90
|
};
|
|
88
91
|
} catch (error) {
|
|
89
92
|
console.error("Error generating metadata:", error);
|
|
93
|
+
const t = await getTranslations("productPage");
|
|
90
94
|
return {
|
|
91
|
-
title: "
|
|
92
|
-
description: "
|
|
95
|
+
title: t("title"),
|
|
96
|
+
description: t("viewDetails"),
|
|
93
97
|
};
|
|
94
98
|
}
|
|
95
99
|
}
|
|
@@ -115,7 +119,7 @@ export async function generateStaticParams() {
|
|
|
115
119
|
sortKey: "BEST_SELLING",
|
|
116
120
|
});
|
|
117
121
|
|
|
118
|
-
return products.map((product
|
|
122
|
+
return products.map((product) => ({
|
|
119
123
|
slug: product.handle,
|
|
120
124
|
}));
|
|
121
125
|
} catch (error) {
|
|
@@ -199,7 +203,9 @@ export default async function ProductPage({
|
|
|
199
203
|
*
|
|
200
204
|
* @see https://schema.org/Product
|
|
201
205
|
*/
|
|
202
|
-
|
|
206
|
+
type Product = NonNullable<ProductQuery['product']>;
|
|
207
|
+
|
|
208
|
+
function generateProductJsonLd(product: Product) {
|
|
203
209
|
const firstImage = product.images?.[0];
|
|
204
210
|
const price = product.priceRange?.minVariantPrice;
|
|
205
211
|
const firstVariant = product.variants?.[0];
|
|
@@ -223,10 +229,7 @@ function generateProductJsonLd(product: any) {
|
|
|
223
229
|
availability: firstVariant?.available
|
|
224
230
|
? "https://schema.org/InStock"
|
|
225
231
|
: "https://schema.org/OutOfStock",
|
|
226
|
-
url:
|
|
227
|
-
typeof window !== "undefined"
|
|
228
|
-
? window.location.href
|
|
229
|
-
: `/products/${product.handle}`,
|
|
232
|
+
url: `/products/${product.handle}`,
|
|
230
233
|
},
|
|
231
234
|
sku: product.id,
|
|
232
235
|
productID: product.id,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
|
+
import { useTranslations } from "next-intl";
|
|
4
5
|
import { useProduct } from "@/lib/graphql/hooks";
|
|
5
|
-
import { useCurrencyStore } from "
|
|
6
|
+
import { useCurrencyStore } from "@doswiftly/storefront-sdk/react";
|
|
6
7
|
import { ProductGallery } from "@/components/product/product-gallery";
|
|
7
8
|
import { ProductPrice } from "@/components/product/product-price";
|
|
8
9
|
import { ProductVariantSelector } from "@/components/product/product-variant-selector";
|
|
@@ -13,58 +14,12 @@ import { SimilarProducts } from "@/components/product/similar-products";
|
|
|
13
14
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
|
14
15
|
import { Badge } from "@/components/ui/badge";
|
|
15
16
|
import { Gift } from "lucide-react";
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
export interface Product {
|
|
19
|
-
id: string;
|
|
20
|
-
handle: string;
|
|
21
|
-
title: string;
|
|
22
|
-
description?: string | null;
|
|
23
|
-
vendor?: string | null;
|
|
24
|
-
productType?: string | null;
|
|
25
|
-
type?: string | null;
|
|
26
|
-
collectRecipientInfo?: boolean;
|
|
27
|
-
tags?: string[];
|
|
28
|
-
images: Array<{
|
|
29
|
-
url: string;
|
|
30
|
-
altText?: string | null;
|
|
31
|
-
}>;
|
|
32
|
-
variants: Array<{
|
|
33
|
-
id: string;
|
|
34
|
-
title: string;
|
|
35
|
-
available: boolean;
|
|
36
|
-
selectedOptions: Array<{
|
|
37
|
-
name: string;
|
|
38
|
-
value: string;
|
|
39
|
-
}>;
|
|
40
|
-
price: {
|
|
41
|
-
amount: string;
|
|
42
|
-
currencyCode: string;
|
|
43
|
-
};
|
|
44
|
-
compareAtPrice?: {
|
|
45
|
-
amount: string;
|
|
46
|
-
currencyCode: string;
|
|
47
|
-
} | null;
|
|
48
|
-
image?: {
|
|
49
|
-
url: string;
|
|
50
|
-
altText?: string | null;
|
|
51
|
-
} | null;
|
|
52
|
-
}>;
|
|
53
|
-
priceRange: {
|
|
54
|
-
minVariantPrice: {
|
|
55
|
-
amount: string;
|
|
56
|
-
currencyCode: string;
|
|
57
|
-
};
|
|
58
|
-
maxVariantPrice: {
|
|
59
|
-
amount: string;
|
|
60
|
-
currencyCode: string;
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
}
|
|
17
|
+
import type { ProductQuery } from "@/generated/graphql";
|
|
18
|
+
import type { ProductCardFields, ProductDetailFields } from "@/lib/graphql/fragments";
|
|
64
19
|
|
|
65
20
|
export interface ProductClientProps {
|
|
66
|
-
product:
|
|
67
|
-
similarProducts?:
|
|
21
|
+
product: ProductDetailFields;
|
|
22
|
+
similarProducts?: ProductCardFields[];
|
|
68
23
|
}
|
|
69
24
|
|
|
70
25
|
/**
|
|
@@ -81,12 +36,13 @@ export interface ProductClientProps {
|
|
|
81
36
|
* Requirements: 2.2, 3.2, 6.4
|
|
82
37
|
*/
|
|
83
38
|
export function ProductClient({ product: initialProduct, similarProducts = [] }: ProductClientProps) {
|
|
39
|
+
const t = useTranslations("productPage");
|
|
84
40
|
const [selectedVariant, setSelectedVariant] = useState(initialProduct.variants[0]);
|
|
85
41
|
const [quantity, setQuantity] = useState(1);
|
|
86
42
|
|
|
87
43
|
// Get user's preferred currency from store
|
|
88
|
-
const currency = useCurrencyStore((s
|
|
89
|
-
const isHydrated = useCurrencyStore((s
|
|
44
|
+
const currency = useCurrencyStore((s) => s.currency);
|
|
45
|
+
const isHydrated = useCurrencyStore((s) => s.isLoaded);
|
|
90
46
|
|
|
91
47
|
// Check if SSR currency matches user's preferred currency
|
|
92
48
|
const ssrCurrency = initialProduct.priceRange?.minVariantPrice?.currencyCode;
|
|
@@ -99,7 +55,7 @@ export function ProductClient({ product: initialProduct, similarProducts = [] }:
|
|
|
99
55
|
enabled: isHydrated,
|
|
100
56
|
// Only use SSR data as placeholder if currency matches
|
|
101
57
|
// Otherwise, don't show stale data from wrong currency
|
|
102
|
-
placeholderData: currencyMatches ? { product: initialProduct } : undefined,
|
|
58
|
+
placeholderData: currencyMatches ? { product: initialProduct } as ProductQuery : undefined,
|
|
103
59
|
// Don't use stale data - always refetch when currency changes
|
|
104
60
|
staleTime: 0,
|
|
105
61
|
});
|
|
@@ -111,10 +67,10 @@ export function ProductClient({ product: initialProduct, similarProducts = [] }:
|
|
|
111
67
|
// IMPORTANT: This useEffect must be called BEFORE any conditional returns
|
|
112
68
|
// to comply with React Hooks rules (hooks must be called in the same order every render)
|
|
113
69
|
useEffect(() => {
|
|
114
|
-
if (product
|
|
70
|
+
if (product && product.variants.length > 0) {
|
|
115
71
|
// Try to maintain the same variant by matching ID
|
|
116
72
|
const matchingVariant = product.variants.find(
|
|
117
|
-
(v
|
|
73
|
+
(v) => v.id === selectedVariant.id
|
|
118
74
|
);
|
|
119
75
|
if (matchingVariant) {
|
|
120
76
|
setSelectedVariant(matchingVariant);
|
|
@@ -318,13 +274,13 @@ export function ProductClient({ product: initialProduct, similarProducts = [] }:
|
|
|
318
274
|
<TabsContent value="shipping" className="mt-6">
|
|
319
275
|
<div className="space-y-4 text-sm">
|
|
320
276
|
<p className="text-muted-foreground">
|
|
321
|
-
Free shipping on orders over $
|
|
322
|
-
Standard delivery takes
|
|
277
|
+
Free shipping on orders over $50.
|
|
278
|
+
Standard delivery takes 3-5 business days.
|
|
323
279
|
</p>
|
|
324
280
|
<ul className="list-inside list-disc space-y-2 text-muted-foreground">
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
281
|
+
<li>Express shipping available at checkout</li>
|
|
282
|
+
<li>International shipping available</li>
|
|
283
|
+
<li>30-day return policy</li>
|
|
328
284
|
</ul>
|
|
329
285
|
</div>
|
|
330
286
|
</TabsContent>
|
|
@@ -334,7 +290,7 @@ export function ProductClient({ product: initialProduct, similarProducts = [] }:
|
|
|
334
290
|
{similarProducts.length > 0 && (
|
|
335
291
|
<SimilarProducts
|
|
336
292
|
products={similarProducts}
|
|
337
|
-
title="
|
|
293
|
+
title={t("youMightAlsoLike")}
|
|
338
294
|
columns={4}
|
|
339
295
|
/>
|
|
340
296
|
)}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
2
|
+
|
|
3
|
+
export default function ProductsLoading() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="container mx-auto px-4 py-8">
|
|
6
|
+
<Skeleton className="mb-6 h-10 w-48" />
|
|
7
|
+
<div className="flex gap-8">
|
|
8
|
+
{/* Filters sidebar */}
|
|
9
|
+
<div className="hidden w-64 shrink-0 lg:block">
|
|
10
|
+
<Skeleton className="mb-4 h-8 w-32" />
|
|
11
|
+
<div className="space-y-3">
|
|
12
|
+
{[...Array(5)].map((_, i) => (
|
|
13
|
+
<Skeleton key={i} className="h-6 w-full" />
|
|
14
|
+
))}
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
{/* Product grid */}
|
|
18
|
+
<div className="flex-1">
|
|
19
|
+
<div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
|
|
20
|
+
{[...Array(12)].map((_, i) => (
|
|
21
|
+
<div key={i} className="space-y-3">
|
|
22
|
+
<Skeleton className="aspect-square w-full rounded-lg" />
|
|
23
|
+
<Skeleton className="h-4 w-3/4" />
|
|
24
|
+
<Skeleton className="h-4 w-1/2" />
|
|
25
|
+
</div>
|
|
26
|
+
))}
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { Suspense } from "react";
|
|
2
|
+
import { getTranslations } from "next-intl/server";
|
|
2
3
|
import { Breadcrumbs } from "@/components/layout/breadcrumbs";
|
|
3
4
|
import { Spinner } from "@/components/ui/spinner";
|
|
4
5
|
import { ProductsClient } from "./products-client";
|
|
5
6
|
|
|
6
|
-
export default function ProductsPage() {
|
|
7
|
+
export default async function ProductsPage() {
|
|
8
|
+
const t = await getTranslations("product");
|
|
9
|
+
|
|
7
10
|
return (
|
|
8
11
|
<div className="container mx-auto px-4 py-8">
|
|
9
12
|
{/* Breadcrumbs */}
|
|
@@ -11,9 +14,9 @@ export default function ProductsPage() {
|
|
|
11
14
|
|
|
12
15
|
{/* Page Header */}
|
|
13
16
|
<div className="mb-8">
|
|
14
|
-
<h1 className="text-3xl font-bold text-foreground">
|
|
17
|
+
<h1 className="text-3xl font-bold text-foreground">{t("allProducts")}</h1>
|
|
15
18
|
<p className="mt-2 text-muted-foreground">
|
|
16
|
-
|
|
19
|
+
{t("browseCollection")}
|
|
17
20
|
</p>
|
|
18
21
|
</div>
|
|
19
22
|
|