@doswiftly/cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +357 -0
- package/bin/doswiftly.js +2 -0
- package/dist/commands/auth-github.d.ts +6 -0
- package/dist/commands/auth-github.d.ts.map +1 -0
- package/dist/commands/auth-github.js +89 -0
- package/dist/commands/auth-github.js.map +1 -0
- package/dist/commands/auth-token.d.ts +12 -0
- package/dist/commands/auth-token.d.ts.map +1 -0
- package/dist/commands/auth-token.js +43 -0
- package/dist/commands/auth-token.js.map +1 -0
- package/dist/commands/auth.d.ts +22 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +348 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/check.d.ts +5 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +234 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +104 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/deploy.d.ts +37 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +580 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +8 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +83 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +363 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/domain.d.ts +13 -0
- package/dist/commands/domain.d.ts.map +1 -0
- package/dist/commands/domain.js +128 -0
- package/dist/commands/domain.js.map +1 -0
- package/dist/commands/env.d.ts +25 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +228 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1028 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inspect.d.ts +12 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +162 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/migrate.d.ts +18 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +355 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/preview.d.ts +29 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +199 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/proxy.d.ts +9 -0
- package/dist/commands/proxy.d.ts.map +1 -0
- package/dist/commands/proxy.js +37 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/commands/sdk.d.ts +5 -0
- package/dist/commands/sdk.d.ts.map +1 -0
- package/dist/commands/sdk.js +82 -0
- package/dist/commands/sdk.js.map +1 -0
- package/dist/commands/template.d.ts +107 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +1309 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/types.d.ts +5 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +82 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +103 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/upgrade.d.ts +18 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +55 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/verify.d.ts +5 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +232 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +60 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/config/types.d.ts +173 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +48 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +416 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-url.d.ts +14 -0
- package/dist/lib/api-url.d.ts.map +1 -0
- package/dist/lib/api-url.js +24 -0
- package/dist/lib/api-url.js.map +1 -0
- package/dist/lib/api.d.ts +67 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +36 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/config.d.ts +39 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +195 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/env-storage.d.ts +140 -0
- package/dist/lib/env-storage.d.ts.map +1 -0
- package/dist/lib/env-storage.js +464 -0
- package/dist/lib/env-storage.js.map +1 -0
- package/dist/lib/errors.d.ts +61 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +204 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/i18n.d.ts +99 -0
- package/dist/lib/i18n.d.ts.map +1 -0
- package/dist/lib/i18n.js +184 -0
- package/dist/lib/i18n.js.map +1 -0
- package/dist/lib/logger.d.ts +95 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +168 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/package-manager.d.ts +91 -0
- package/dist/lib/package-manager.d.ts.map +1 -0
- package/dist/lib/package-manager.js +205 -0
- package/dist/lib/package-manager.js.map +1 -0
- package/dist/lib/proxy-server.d.ts +24 -0
- package/dist/lib/proxy-server.d.ts.map +1 -0
- package/dist/lib/proxy-server.js +173 -0
- package/dist/lib/proxy-server.js.map +1 -0
- package/dist/lib/select-with-back.d.ts +34 -0
- package/dist/lib/select-with-back.d.ts.map +1 -0
- package/dist/lib/select-with-back.js +94 -0
- package/dist/lib/select-with-back.js.map +1 -0
- package/dist/lib/shared-api-client.d.ts +40 -0
- package/dist/lib/shared-api-client.d.ts.map +1 -0
- package/dist/lib/shared-api-client.js +92 -0
- package/dist/lib/shared-api-client.js.map +1 -0
- package/dist/lib/wizard-engine.d.ts +128 -0
- package/dist/lib/wizard-engine.d.ts.map +1 -0
- package/dist/lib/wizard-engine.js +168 -0
- package/dist/lib/wizard-engine.js.map +1 -0
- package/package.json +85 -0
- package/templates/storefront-minimal/.env.example +10 -0
- package/templates/storefront-minimal/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-minimal/app/globals.css +18 -0
- package/templates/storefront-minimal/app/layout.tsx +26 -0
- package/templates/storefront-minimal/app/page.tsx +93 -0
- package/templates/storefront-minimal/lib/graphql-client.ts +23 -0
- package/templates/storefront-minimal/next.config.ts +15 -0
- package/templates/storefront-minimal/open-next.config.ts +3 -0
- package/templates/storefront-minimal/package.json +30 -0
- package/templates/storefront-minimal/postcss.config.mjs +5 -0
- package/templates/storefront-minimal/tailwind.config.ts +14 -0
- package/templates/storefront-minimal/tsconfig.json +27 -0
- package/templates/storefront-minimal/wrangler.toml +9 -0
- package/templates/storefront-nextjs/.env.example +68 -0
- package/templates/storefront-nextjs/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-nextjs/.github/workflows/deploy.yml +25 -0
- package/templates/storefront-nextjs/.github/workflows/preview.yml +22 -0
- package/templates/storefront-nextjs/README.md +520 -0
- package/templates/storefront-nextjs/app/account/orders/page.tsx +216 -0
- package/templates/storefront-nextjs/app/account/page.tsx +167 -0
- package/templates/storefront-nextjs/app/auth/login/page.tsx +135 -0
- package/templates/storefront-nextjs/app/auth/register/page.tsx +228 -0
- package/templates/storefront-nextjs/app/cart/page.tsx +263 -0
- package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +200 -0
- package/templates/storefront-nextjs/app/categories/page.tsx +58 -0
- package/templates/storefront-nextjs/app/checkout/page.tsx +351 -0
- package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +158 -0
- package/templates/storefront-nextjs/app/collections/page.tsx +61 -0
- package/templates/storefront-nextjs/app/globals.css +98 -0
- package/templates/storefront-nextjs/app/layout.tsx +39 -0
- package/templates/storefront-nextjs/app/page.tsx +136 -0
- package/templates/storefront-nextjs/app/products/[slug]/page.tsx +119 -0
- package/templates/storefront-nextjs/app/products/page.tsx +107 -0
- package/templates/storefront-nextjs/app/search/page.tsx +127 -0
- package/templates/storefront-nextjs/components/auth/auth-guard.tsx +94 -0
- package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +77 -0
- package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +29 -0
- package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +217 -0
- package/templates/storefront-nextjs/components/commerce/pagination.tsx +62 -0
- package/templates/storefront-nextjs/components/commerce/product-actions.tsx +135 -0
- package/templates/storefront-nextjs/components/commerce/product-filters.tsx +109 -0
- package/templates/storefront-nextjs/components/commerce/product-price.tsx +375 -0
- package/templates/storefront-nextjs/components/commerce/search-input.tsx +178 -0
- package/templates/storefront-nextjs/components/commerce/sort-select.tsx +64 -0
- package/templates/storefront-nextjs/components/commerce/variant-selector.tsx +210 -0
- package/templates/storefront-nextjs/components/layout/footer.tsx +107 -0
- package/templates/storefront-nextjs/components/layout/header.tsx +104 -0
- package/templates/storefront-nextjs/components/providers.tsx +62 -0
- package/templates/storefront-nextjs/lib/auth/routes.ts +52 -0
- package/templates/storefront-nextjs/lib/currency.tsx +140 -0
- package/templates/storefront-nextjs/lib/format.ts +159 -0
- package/templates/storefront-nextjs/lib/graphql-queries.ts +629 -0
- package/templates/storefront-nextjs/lib/hooks.ts +30 -0
- package/templates/storefront-nextjs/middleware.ts +80 -0
- package/templates/storefront-nextjs/next.config.ts +37 -0
- package/templates/storefront-nextjs/open-next.config.ts +3 -0
- package/templates/storefront-nextjs/package.dev.json +30 -0
- package/templates/storefront-nextjs/package.json +32 -0
- package/templates/storefront-nextjs/package.json.template +32 -0
- package/templates/storefront-nextjs/postcss.config.mjs +8 -0
- package/templates/storefront-nextjs/tailwind.config.ts +111 -0
- package/templates/storefront-nextjs/tsconfig.json +27 -0
- package/templates/storefront-nextjs/wrangler.toml +9 -0
- package/templates/storefront-nextjs-shadcn/.env.example +68 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +109 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +25 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +22 -0
- package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +282 -0
- package/templates/storefront-nextjs-shadcn/CLAUDE.md +96 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +190 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +263 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +135 -0
- package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +142 -0
- package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +448 -0
- package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +307 -0
- package/templates/storefront-nextjs-shadcn/README.md +195 -0
- package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +245 -0
- package/templates/storefront-nextjs-shadcn/app/about/page.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +215 -0
- package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +484 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +128 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/tracking/page.tsx +206 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +80 -0
- package/templates/storefront-nextjs-shadcn/app/account/page.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +195 -0
- package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +87 -0
- package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +125 -0
- package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +131 -0
- package/templates/storefront-nextjs-shadcn/app/auth/login/page.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/app/auth/register/page.tsx +20 -0
- package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +323 -0
- package/templates/storefront-nextjs-shadcn/app/blog/page.tsx +159 -0
- package/templates/storefront-nextjs-shadcn/app/brands/[slug]/page.tsx +170 -0
- package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +73 -0
- package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +165 -0
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +78 -0
- package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +75 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +1752 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/success/[orderId]/page.tsx +256 -0
- package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +74 -0
- package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +75 -0
- package/templates/storefront-nextjs-shadcn/app/contact/page.tsx +114 -0
- package/templates/storefront-nextjs-shadcn/app/error.tsx +90 -0
- package/templates/storefront-nextjs-shadcn/app/globals.css +125 -0
- package/templates/storefront-nextjs-shadcn/app/layout.tsx +57 -0
- package/templates/storefront-nextjs-shadcn/app/not-found.tsx +68 -0
- package/templates/storefront-nextjs-shadcn/app/page.tsx +21 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +246 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +343 -0
- package/templates/storefront-nextjs-shadcn/app/products/page.tsx +25 -0
- package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +192 -0
- package/templates/storefront-nextjs-shadcn/app/returns/page.tsx +77 -0
- package/templates/storefront-nextjs-shadcn/app/robots.ts +53 -0
- package/templates/storefront-nextjs-shadcn/app/search/page.tsx +16 -0
- package/templates/storefront-nextjs-shadcn/app/search/search-client.tsx +47 -0
- package/templates/storefront-nextjs-shadcn/app/shipping/page.tsx +62 -0
- package/templates/storefront-nextjs-shadcn/app/sitemap.ts +144 -0
- package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +179 -0
- package/templates/storefront-nextjs-shadcn/codegen.ts +51 -0
- package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +348 -0
- package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +144 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +258 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +132 -0
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +188 -0
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +305 -0
- package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +240 -0
- package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +177 -0
- package/templates/storefront-nextjs-shadcn/components/blog/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +119 -0
- package/templates/storefront-nextjs-shadcn/components/brand/brand-grid.tsx +64 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +140 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +48 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +112 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +84 -0
- package/templates/storefront-nextjs-shadcn/components/cart/index.ts +17 -0
- package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +121 -0
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +162 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/index.ts +25 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +187 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +160 -0
- package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +154 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +225 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +62 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +158 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +174 -0
- package/templates/storefront-nextjs-shadcn/components/commerce/variant-selector.tsx +210 -0
- package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +97 -0
- package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +187 -0
- package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +151 -0
- package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +166 -0
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +245 -0
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +246 -0
- package/templates/storefront-nextjs-shadcn/components/discount/index.ts +19 -0
- package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +113 -0
- package/templates/storefront-nextjs-shadcn/components/error/index.ts +7 -0
- package/templates/storefront-nextjs-shadcn/components/filters/attribute-filter.tsx +153 -0
- package/templates/storefront-nextjs-shadcn/components/filters/checkbox-group-filter.tsx +167 -0
- package/templates/storefront-nextjs-shadcn/components/filters/color-swatch-filter.tsx +176 -0
- package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +220 -0
- package/templates/storefront-nextjs-shadcn/components/filters/index.ts +36 -0
- package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +193 -0
- package/templates/storefront-nextjs-shadcn/components/filters/toggle-filter.tsx +132 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +321 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +309 -0
- package/templates/storefront-nextjs-shadcn/components/gift-card/index.ts +24 -0
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +72 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +85 -0
- package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/components/home/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +108 -0
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +133 -0
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +341 -0
- package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +128 -0
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +147 -0
- package/templates/storefront-nextjs-shadcn/components/layout/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +211 -0
- package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +95 -0
- package/templates/storefront-nextjs-shadcn/components/layout/theme-switcher.tsx +192 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/index.ts +11 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +93 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +177 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +250 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +217 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-badge.tsx +106 -0
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +131 -0
- package/templates/storefront-nextjs-shadcn/components/order/delivery-estimate.tsx +196 -0
- package/templates/storefront-nextjs-shadcn/components/order/index.ts +11 -0
- package/templates/storefront-nextjs-shadcn/components/order/order-tracking.tsx +200 -0
- package/templates/storefront-nextjs-shadcn/components/order/shipment-card.tsx +407 -0
- package/templates/storefront-nextjs-shadcn/components/order/tracking-status.tsx +222 -0
- package/templates/storefront-nextjs-shadcn/components/order/tracking-timeline.tsx +205 -0
- package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +161 -0
- package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +250 -0
- package/templates/storefront-nextjs-shadcn/components/product/discount-badge.tsx +196 -0
- package/templates/storefront-nextjs-shadcn/components/product/index.ts +41 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +147 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +217 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-gallery.tsx +143 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +83 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +155 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +158 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-quantity-selector.tsx +111 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +238 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +58 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +169 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +220 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +338 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-summary.tsx +143 -0
- package/templates/storefront-nextjs-shadcn/components/product/sale-countdown.tsx +166 -0
- package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +213 -0
- package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +57 -0
- package/templates/storefront-nextjs-shadcn/components/product/stock-indicator.tsx +91 -0
- package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +103 -0
- package/templates/storefront-nextjs-shadcn/components/providers/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/providers/query-provider.tsx +260 -0
- package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +13 -0
- package/templates/storefront-nextjs-shadcn/components/returns/index.ts +26 -0
- package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +608 -0
- package/templates/storefront-nextjs-shadcn/components/returns/return-status-card.tsx +554 -0
- package/templates/storefront-nextjs-shadcn/components/search/index.ts +8 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +140 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +58 -0
- package/templates/storefront-nextjs-shadcn/components/search/search-suggestions.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/components/seo/index.ts +12 -0
- package/templates/storefront-nextjs-shadcn/components/seo/json-ld.tsx +56 -0
- package/templates/storefront-nextjs-shadcn/components/seo/product-json-ld.ts +167 -0
- package/templates/storefront-nextjs-shadcn/components/shipping/index.ts +16 -0
- package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +337 -0
- package/templates/storefront-nextjs-shadcn/components/ui/accordion.tsx +153 -0
- package/templates/storefront-nextjs-shadcn/components/ui/alert.tsx +59 -0
- package/templates/storefront-nextjs-shadcn/components/ui/badge.tsx +34 -0
- package/templates/storefront-nextjs-shadcn/components/ui/button.tsx +51 -0
- package/templates/storefront-nextjs-shadcn/components/ui/card.tsx +77 -0
- package/templates/storefront-nextjs-shadcn/components/ui/checkbox.tsx +30 -0
- package/templates/storefront-nextjs-shadcn/components/ui/dialog.tsx +137 -0
- package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +207 -0
- package/templates/storefront-nextjs-shadcn/components/ui/index.ts +67 -0
- package/templates/storefront-nextjs-shadcn/components/ui/input.tsx +65 -0
- package/templates/storefront-nextjs-shadcn/components/ui/label.tsx +26 -0
- package/templates/storefront-nextjs-shadcn/components/ui/pagination.tsx +205 -0
- package/templates/storefront-nextjs-shadcn/components/ui/radio-group.tsx +44 -0
- package/templates/storefront-nextjs-shadcn/components/ui/select.tsx +160 -0
- package/templates/storefront-nextjs-shadcn/components/ui/separator.tsx +28 -0
- package/templates/storefront-nextjs-shadcn/components/ui/skeleton.tsx +20 -0
- package/templates/storefront-nextjs-shadcn/components/ui/spinner.tsx +82 -0
- package/templates/storefront-nextjs-shadcn/components/ui/tabs.tsx +119 -0
- package/templates/storefront-nextjs-shadcn/components/ui/toast.tsx +96 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +148 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +47 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +165 -0
- package/templates/storefront-nextjs-shadcn/components.json +19 -0
- package/templates/storefront-nextjs-shadcn/generated/.gitkeep +2 -0
- package/templates/storefront-nextjs-shadcn/graphql/.gitkeep +31 -0
- package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +168 -0
- package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +160 -0
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +9 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +310 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +286 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +110 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.test.ts +173 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.ts +298 -0
- package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +220 -0
- package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +57 -0
- package/templates/storefront-nextjs-shadcn/lib/config.ts +46 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +254 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/README.md +464 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +328 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +295 -0
- package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +27 -0
- package/templates/storefront-nextjs-shadcn/lib/format.test.ts +397 -0
- package/templates/storefront-nextjs-shadcn/lib/format.ts +226 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +109 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +1183 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +267 -0
- package/templates/storefront-nextjs-shadcn/lib/hooks.ts +30 -0
- package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +89 -0
- package/templates/storefront-nextjs-shadcn/lib/utils.ts +6 -0
- package/templates/storefront-nextjs-shadcn/next.config.ts +47 -0
- package/templates/storefront-nextjs-shadcn/open-next.config.ts +3 -0
- package/templates/storefront-nextjs-shadcn/package.dev.json +30 -0
- package/templates/storefront-nextjs-shadcn/package.json +60 -0
- package/templates/storefront-nextjs-shadcn/package.json.template +46 -0
- package/templates/storefront-nextjs-shadcn/postcss.config.mjs +8 -0
- package/templates/storefront-nextjs-shadcn/proxy.ts +80 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/apple-pay.svg +8 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/bank-transfer.svg +10 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/blik.svg +6 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/cash-on-delivery.svg +11 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/google-pay.svg +11 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/mastercard.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/paypal.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/payu.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/przelewy24.svg +7 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/stripe.svg +4 -0
- package/templates/storefront-nextjs-shadcn/public/icons/payment/visa.svg +5 -0
- package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +66 -0
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +56 -0
- package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +184 -0
- package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +103 -0
- package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +291 -0
- package/templates/storefront-nextjs-shadcn/tailwind.config.ts +111 -0
- package/templates/storefront-nextjs-shadcn/tsconfig.json +27 -0
- package/templates/storefront-nextjs-shadcn/wrangler.toml +9 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Route: Set Authentication Token
|
|
3
|
+
*
|
|
4
|
+
* Sets the customer access token in an httpOnly cookie.
|
|
5
|
+
* This provides security by preventing client-side JavaScript from accessing the token.
|
|
6
|
+
*
|
|
7
|
+
* Security Features:
|
|
8
|
+
* 1. httpOnly cookie - Cannot be accessed via JavaScript (XSS protection)
|
|
9
|
+
* 2. Secure flag - Only sent over HTTPS in production
|
|
10
|
+
* 3. SameSite=Lax - CSRF protection
|
|
11
|
+
* 4. Origin validation - Only accepts requests from same origin
|
|
12
|
+
* 5. Content-Type validation - Only accepts JSON
|
|
13
|
+
*
|
|
14
|
+
* @see lib/auth/cookies.ts - Cookie configuration
|
|
15
|
+
* @see hooks/use-auth.ts - Client-side usage
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* // Client-side usage (via setAuthToken helper)
|
|
20
|
+
* await setAuthToken(customerAccessToken.accessToken);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
25
|
+
import { AUTH_COOKIE_CONFIG } from "@/lib/auth/cookies";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Request body schema
|
|
29
|
+
*/
|
|
30
|
+
interface SetTokenRequest {
|
|
31
|
+
token: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* POST /api/auth/set-token
|
|
36
|
+
*
|
|
37
|
+
* Sets the authentication token in an httpOnly cookie.
|
|
38
|
+
*
|
|
39
|
+
* @param request - Next.js request object
|
|
40
|
+
* @returns Response with Set-Cookie header
|
|
41
|
+
*/
|
|
42
|
+
export async function POST(request: NextRequest) {
|
|
43
|
+
try {
|
|
44
|
+
// 1. CSRF Protection: Validate origin
|
|
45
|
+
const origin = request.headers.get("origin");
|
|
46
|
+
const host = request.headers.get("host");
|
|
47
|
+
|
|
48
|
+
// Only allow requests from same origin
|
|
49
|
+
if (origin && !origin.includes(host || "")) {
|
|
50
|
+
return NextResponse.json(
|
|
51
|
+
{ error: "Invalid origin" },
|
|
52
|
+
{ status: 403 }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 2. Validate Content-Type
|
|
57
|
+
const contentType = request.headers.get("content-type");
|
|
58
|
+
if (!contentType?.includes("application/json")) {
|
|
59
|
+
return NextResponse.json(
|
|
60
|
+
{ error: "Content-Type must be application/json" },
|
|
61
|
+
{ status: 400 }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 3. Parse and validate request body
|
|
66
|
+
let body: SetTokenRequest;
|
|
67
|
+
try {
|
|
68
|
+
body = await request.json();
|
|
69
|
+
} catch {
|
|
70
|
+
return NextResponse.json(
|
|
71
|
+
{ error: "Invalid JSON body" },
|
|
72
|
+
{ status: 400 }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const { token } = body;
|
|
77
|
+
|
|
78
|
+
if (!token || typeof token !== "string" || token.trim() === "") {
|
|
79
|
+
return NextResponse.json(
|
|
80
|
+
{ error: "Token is required and must be a non-empty string" },
|
|
81
|
+
{ status: 400 }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 4. Create response with Set-Cookie header
|
|
86
|
+
const response = NextResponse.json(
|
|
87
|
+
{ success: true, message: "Token set successfully" },
|
|
88
|
+
{ status: 200 }
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// 5. Set httpOnly cookie
|
|
92
|
+
response.cookies.set({
|
|
93
|
+
name: AUTH_COOKIE_CONFIG.name,
|
|
94
|
+
value: token,
|
|
95
|
+
maxAge: AUTH_COOKIE_CONFIG.maxAge,
|
|
96
|
+
path: AUTH_COOKIE_CONFIG.path,
|
|
97
|
+
sameSite: AUTH_COOKIE_CONFIG.sameSite,
|
|
98
|
+
secure: AUTH_COOKIE_CONFIG.secure,
|
|
99
|
+
httpOnly: AUTH_COOKIE_CONFIG.httpOnly,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return response;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error("Error setting auth token:", error);
|
|
105
|
+
return NextResponse.json(
|
|
106
|
+
{ error: "Internal server error" },
|
|
107
|
+
{ status: 500 }
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* OPTIONS /api/auth/set-token
|
|
114
|
+
*
|
|
115
|
+
* Handle preflight requests for CORS.
|
|
116
|
+
*/
|
|
117
|
+
export async function OPTIONS() {
|
|
118
|
+
return new NextResponse(null, {
|
|
119
|
+
status: 204,
|
|
120
|
+
headers: {
|
|
121
|
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
122
|
+
"Access-Control-Allow-Headers": "Content-Type",
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { useMutation } from "@tanstack/react-query";
|
|
6
|
+
import { getGraphQLClient } from "@/lib/graphql/client";
|
|
7
|
+
import { CustomerPasswordRecoverDocument } from "@/generated/graphql";
|
|
8
|
+
import { Button } from "@/components/ui/button";
|
|
9
|
+
import { Input } from "@/components/ui/input";
|
|
10
|
+
import { ArrowLeft } from "lucide-react";
|
|
11
|
+
|
|
12
|
+
export default function ForgotPasswordPage() {
|
|
13
|
+
const [email, setEmail] = useState("");
|
|
14
|
+
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
15
|
+
const [error, setError] = useState("");
|
|
16
|
+
|
|
17
|
+
const client = getGraphQLClient();
|
|
18
|
+
|
|
19
|
+
const recoverMutation = useMutation({
|
|
20
|
+
mutationFn: async (email: string) => {
|
|
21
|
+
return client.request(CustomerPasswordRecoverDocument, { email });
|
|
22
|
+
},
|
|
23
|
+
onSuccess: () => {
|
|
24
|
+
setIsSubmitted(true);
|
|
25
|
+
},
|
|
26
|
+
onError: () => {
|
|
27
|
+
setError("An error occurred. Please try again.");
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
setError("");
|
|
34
|
+
recoverMutation.mutate(email);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (isSubmitted) {
|
|
38
|
+
return (
|
|
39
|
+
<div className="container mx-auto px-4 py-16">
|
|
40
|
+
<div className="mx-auto max-w-md text-center">
|
|
41
|
+
<div className="mb-8">
|
|
42
|
+
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-100">
|
|
43
|
+
<svg
|
|
44
|
+
className="h-8 w-8 text-green-600"
|
|
45
|
+
fill="none"
|
|
46
|
+
viewBox="0 0 24 24"
|
|
47
|
+
stroke="currentColor"
|
|
48
|
+
>
|
|
49
|
+
<path
|
|
50
|
+
strokeLinecap="round"
|
|
51
|
+
strokeLinejoin="round"
|
|
52
|
+
strokeWidth={2}
|
|
53
|
+
d="M5 13l4 4L19 7"
|
|
54
|
+
/>
|
|
55
|
+
</svg>
|
|
56
|
+
</div>
|
|
57
|
+
<h1 className="text-3xl font-bold text-foreground">Check Your Email</h1>
|
|
58
|
+
<p className="mt-2 text-muted-foreground">
|
|
59
|
+
We've sent password reset instructions to {email}
|
|
60
|
+
</p>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div className="space-y-4">
|
|
64
|
+
<p className="text-sm text-muted-foreground">
|
|
65
|
+
Didn't receive the email? Check your spam folder or try again.
|
|
66
|
+
</p>
|
|
67
|
+
<Button variant="outline" onClick={() => setIsSubmitted(false)}>
|
|
68
|
+
Try Another Email
|
|
69
|
+
</Button>
|
|
70
|
+
<div>
|
|
71
|
+
<Link
|
|
72
|
+
href="/auth/login"
|
|
73
|
+
className="text-sm text-primary hover:underline"
|
|
74
|
+
>
|
|
75
|
+
Back to Sign In
|
|
76
|
+
</Link>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div className="container mx-auto px-4 py-16">
|
|
86
|
+
<div className="mx-auto max-w-md">
|
|
87
|
+
<Link
|
|
88
|
+
href="/auth/login"
|
|
89
|
+
className="mb-8 inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground"
|
|
90
|
+
>
|
|
91
|
+
<ArrowLeft className="h-4 w-4" />
|
|
92
|
+
Back to Sign In
|
|
93
|
+
</Link>
|
|
94
|
+
|
|
95
|
+
<div className="mb-8">
|
|
96
|
+
<h1 className="text-3xl font-bold text-foreground">Reset Password</h1>
|
|
97
|
+
<p className="mt-2 text-muted-foreground">
|
|
98
|
+
Enter your email address and we'll send you instructions to reset your password.
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<form onSubmit={handleSubmit} className="space-y-6">
|
|
103
|
+
{error && (
|
|
104
|
+
<div className="rounded-lg border border-destructive bg-destructive/10 p-4 text-sm text-destructive">
|
|
105
|
+
{error}
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
|
|
109
|
+
<div className="space-y-2">
|
|
110
|
+
<label htmlFor="email" className="text-sm font-medium text-foreground">
|
|
111
|
+
Email
|
|
112
|
+
</label>
|
|
113
|
+
<Input
|
|
114
|
+
id="email"
|
|
115
|
+
type="email"
|
|
116
|
+
value={email}
|
|
117
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
118
|
+
placeholder="you@example.com"
|
|
119
|
+
required
|
|
120
|
+
autoComplete="email"
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<Button type="submit" className="w-full" disabled={recoverMutation.isPending}>
|
|
125
|
+
{recoverMutation.isPending ? "Sending..." : "Send Reset Instructions"}
|
|
126
|
+
</Button>
|
|
127
|
+
</form>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Suspense } from "react";
|
|
4
|
+
import { LoginForm } from "@/components/auth/login-form";
|
|
5
|
+
import { Spinner } from "@/components/ui/spinner";
|
|
6
|
+
|
|
7
|
+
export default function LoginPage() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="container mx-auto px-4 py-16">
|
|
10
|
+
<div className="mx-auto max-w-md">
|
|
11
|
+
<div className="mb-8 text-center">
|
|
12
|
+
<h1 className="text-3xl font-bold text-foreground">Sign In</h1>
|
|
13
|
+
<p className="mt-2 text-muted-foreground">
|
|
14
|
+
Sign in to your account to continue
|
|
15
|
+
</p>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<Suspense fallback={<Spinner />}>
|
|
19
|
+
<LoginForm />
|
|
20
|
+
</Suspense>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { RegisterForm } from "@/components/auth/register-form";
|
|
4
|
+
|
|
5
|
+
export default function RegisterPage() {
|
|
6
|
+
return (
|
|
7
|
+
<div className="container mx-auto px-4 py-16">
|
|
8
|
+
<div className="mx-auto max-w-md">
|
|
9
|
+
<div className="mb-8 text-center">
|
|
10
|
+
<h1 className="text-3xl font-bold text-foreground">Create Account</h1>
|
|
11
|
+
<p className="mt-2 text-muted-foreground">
|
|
12
|
+
Sign up to start shopping
|
|
13
|
+
</p>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<RegisterForm />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blog Post Detail Page
|
|
3
|
+
*
|
|
4
|
+
* Displays full blog post content with author info and related posts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Metadata } from 'next';
|
|
8
|
+
import Image from 'next/image';
|
|
9
|
+
import Link from 'next/link';
|
|
10
|
+
import { notFound } from 'next/navigation';
|
|
11
|
+
import { Calendar, Clock, Eye, User, ChevronLeft, Share2 } from 'lucide-react';
|
|
12
|
+
import { Breadcrumbs } from '@/components/layout/breadcrumbs';
|
|
13
|
+
import { Badge } from '@/components/ui/badge';
|
|
14
|
+
import { Button } from '@/components/ui/button';
|
|
15
|
+
import { Card, CardContent } from '@/components/ui/card';
|
|
16
|
+
import { Separator } from '@/components/ui/separator';
|
|
17
|
+
import { BlogCard } from '@/components/blog/blog-card';
|
|
18
|
+
import { BlogSidebar } from '@/components/blog/blog-sidebar';
|
|
19
|
+
|
|
20
|
+
// Mock data - replace with actual GraphQL fetch
|
|
21
|
+
const mockPost = {
|
|
22
|
+
id: '1',
|
|
23
|
+
title: 'Jak wybrać idealny produkt dla siebie',
|
|
24
|
+
slug: 'jak-wybrac-idealny-produkt',
|
|
25
|
+
excerpt: 'Praktyczny poradnik, który pomoże Ci podjąć najlepszą decyzję zakupową.',
|
|
26
|
+
content: `
|
|
27
|
+
<h2>Wprowadzenie</h2>
|
|
28
|
+
<p>Wybór odpowiedniego produktu może być trudny, szczególnie gdy rynek oferuje tak wiele opcji. W tym artykule przedstawimy praktyczne wskazówki, które pomogą Ci podjąć najlepszą decyzję.</p>
|
|
29
|
+
|
|
30
|
+
<h2>Krok 1: Określ swoje potrzeby</h2>
|
|
31
|
+
<p>Zanim zaczniesz przeglądać oferty, zastanów się, czego naprawdę potrzebujesz. Sporządź listę funkcji, które są dla Ciebie niezbędne.</p>
|
|
32
|
+
|
|
33
|
+
<h2>Krok 2: Ustal budżet</h2>
|
|
34
|
+
<p>Określenie górnego limitu wydatków pomoże Ci zawęzić wybór i uniknąć impulsywnych zakupów.</p>
|
|
35
|
+
|
|
36
|
+
<h2>Krok 3: Przeczytaj opinie</h2>
|
|
37
|
+
<p>Opinie innych klientów to cenne źródło informacji. Zwróć uwagę zarówno na pozytywne, jak i negatywne recenzje.</p>
|
|
38
|
+
|
|
39
|
+
<h2>Podsumowanie</h2>
|
|
40
|
+
<p>Świadomy wybór produktu wymaga czasu i researchu, ale dzięki temu unikniesz rozczarowania i będziesz zadowolony z zakupu.</p>
|
|
41
|
+
`,
|
|
42
|
+
contentType: 'html',
|
|
43
|
+
featuredImage: null,
|
|
44
|
+
author: {
|
|
45
|
+
id: '1',
|
|
46
|
+
name: 'Jan Kowalski',
|
|
47
|
+
bio: 'Ekspert ds. produktów z 10-letnim doświadczeniem w branży.',
|
|
48
|
+
avatar: null,
|
|
49
|
+
},
|
|
50
|
+
category: {
|
|
51
|
+
id: '1',
|
|
52
|
+
name: 'Poradniki',
|
|
53
|
+
slug: 'poradniki',
|
|
54
|
+
},
|
|
55
|
+
tags: [
|
|
56
|
+
{ id: '1', name: 'Tips', slug: 'tips', postCount: 5 },
|
|
57
|
+
{ id: '2', name: 'Tutorial', slug: 'tutorial', postCount: 3 },
|
|
58
|
+
],
|
|
59
|
+
status: 'PUBLISHED',
|
|
60
|
+
publishedAt: '2024-01-15T10:00:00Z',
|
|
61
|
+
readingTime: 5,
|
|
62
|
+
viewCount: 120,
|
|
63
|
+
commentCount: 8,
|
|
64
|
+
allowComments: true,
|
|
65
|
+
isFeatured: true,
|
|
66
|
+
seo: {
|
|
67
|
+
title: 'Jak wybrać idealny produkt - Poradnik',
|
|
68
|
+
description: 'Praktyczny poradnik zakupowy. Dowiedz się, jak wybrać najlepszy produkt.',
|
|
69
|
+
},
|
|
70
|
+
createdAt: '2024-01-10T10:00:00Z',
|
|
71
|
+
updatedAt: '2024-01-15T10:00:00Z',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const mockRelatedPosts = [
|
|
75
|
+
{
|
|
76
|
+
id: '2',
|
|
77
|
+
title: 'Trendy na rok 2024',
|
|
78
|
+
slug: 'trendy-2024',
|
|
79
|
+
excerpt: 'Odkryj, co będzie modne w nadchodzącym roku.',
|
|
80
|
+
featuredImage: null,
|
|
81
|
+
author: {
|
|
82
|
+
id: '1',
|
|
83
|
+
name: 'Jan Kowalski',
|
|
84
|
+
avatar: null,
|
|
85
|
+
},
|
|
86
|
+
category: {
|
|
87
|
+
id: '2',
|
|
88
|
+
name: 'Trendy',
|
|
89
|
+
slug: 'trendy',
|
|
90
|
+
},
|
|
91
|
+
publishedAt: '2024-01-10T10:00:00Z',
|
|
92
|
+
readingTime: 8,
|
|
93
|
+
viewCount: 250,
|
|
94
|
+
isFeatured: false,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
interface BlogPostPageProps {
|
|
99
|
+
params: Promise<{ slug: string }>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function generateMetadata({
|
|
103
|
+
params,
|
|
104
|
+
}: BlogPostPageProps): Promise<Metadata> {
|
|
105
|
+
const { slug } = await params;
|
|
106
|
+
// In production, fetch post data here
|
|
107
|
+
const post = mockPost;
|
|
108
|
+
|
|
109
|
+
if (!post) {
|
|
110
|
+
return {
|
|
111
|
+
title: 'Nie znaleziono wpisu',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
title: post.seo?.title || post.title,
|
|
117
|
+
description: post.seo?.description || post.excerpt,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export default async function BlogPostPage({ params }: BlogPostPageProps) {
|
|
122
|
+
const { slug } = await params;
|
|
123
|
+
|
|
124
|
+
// In production, fetch post data here
|
|
125
|
+
const post = mockPost;
|
|
126
|
+
const relatedPosts = mockRelatedPosts;
|
|
127
|
+
|
|
128
|
+
if (!post || post.slug !== slug) {
|
|
129
|
+
notFound();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const formatDate = (dateString: string) => {
|
|
133
|
+
return new Intl.DateTimeFormat('pl-PL', {
|
|
134
|
+
day: 'numeric',
|
|
135
|
+
month: 'long',
|
|
136
|
+
year: 'numeric',
|
|
137
|
+
}).format(new Date(dateString));
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<article className="container py-8">
|
|
142
|
+
<Breadcrumbs
|
|
143
|
+
items={[
|
|
144
|
+
{ label: 'Strona główna', href: '/' },
|
|
145
|
+
{ label: 'Blog', href: '/blog' },
|
|
146
|
+
...(post.category
|
|
147
|
+
? [{ label: post.category.name, href: `/blog/category/${post.category.slug}` }]
|
|
148
|
+
: []),
|
|
149
|
+
{ label: post.title },
|
|
150
|
+
]}
|
|
151
|
+
/>
|
|
152
|
+
|
|
153
|
+
{/* Back Link */}
|
|
154
|
+
<Link
|
|
155
|
+
href="/blog"
|
|
156
|
+
className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground mt-4 mb-6"
|
|
157
|
+
>
|
|
158
|
+
<ChevronLeft className="h-4 w-4 mr-1" />
|
|
159
|
+
Wróć do bloga
|
|
160
|
+
</Link>
|
|
161
|
+
|
|
162
|
+
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
|
163
|
+
{/* Main Content */}
|
|
164
|
+
<div className="lg:col-span-3">
|
|
165
|
+
{/* Header */}
|
|
166
|
+
<header className="mb-8">
|
|
167
|
+
{post.category && (
|
|
168
|
+
<Link
|
|
169
|
+
href={`/blog/category/${post.category.slug}`}
|
|
170
|
+
className="inline-block mb-4"
|
|
171
|
+
>
|
|
172
|
+
<Badge variant="secondary">{post.category.name}</Badge>
|
|
173
|
+
</Link>
|
|
174
|
+
)}
|
|
175
|
+
|
|
176
|
+
<h1 className="text-4xl font-bold tracking-tight mb-4">
|
|
177
|
+
{post.title}
|
|
178
|
+
</h1>
|
|
179
|
+
|
|
180
|
+
{post.excerpt && (
|
|
181
|
+
<p className="text-xl text-muted-foreground mb-6">{post.excerpt}</p>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
{/* Meta */}
|
|
185
|
+
<div className="flex flex-wrap items-center gap-4 text-sm text-muted-foreground">
|
|
186
|
+
<div className="flex items-center gap-2">
|
|
187
|
+
{post.author.avatar ? (
|
|
188
|
+
<Image
|
|
189
|
+
src={post.author.avatar.url}
|
|
190
|
+
alt={post.author.name}
|
|
191
|
+
width={32}
|
|
192
|
+
height={32}
|
|
193
|
+
className="rounded-full"
|
|
194
|
+
/>
|
|
195
|
+
) : (
|
|
196
|
+
<div className="w-8 h-8 rounded-full bg-muted flex items-center justify-center">
|
|
197
|
+
<User className="h-4 w-4" />
|
|
198
|
+
</div>
|
|
199
|
+
)}
|
|
200
|
+
<span className="font-medium text-foreground">
|
|
201
|
+
{post.author.name}
|
|
202
|
+
</span>
|
|
203
|
+
</div>
|
|
204
|
+
{post.publishedAt && (
|
|
205
|
+
<div className="flex items-center gap-1">
|
|
206
|
+
<Calendar className="h-4 w-4" />
|
|
207
|
+
<span>{formatDate(post.publishedAt)}</span>
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
<div className="flex items-center gap-1">
|
|
211
|
+
<Clock className="h-4 w-4" />
|
|
212
|
+
<span>{post.readingTime} min czytania</span>
|
|
213
|
+
</div>
|
|
214
|
+
<div className="flex items-center gap-1">
|
|
215
|
+
<Eye className="h-4 w-4" />
|
|
216
|
+
<span>{post.viewCount} wyświetleń</span>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</header>
|
|
220
|
+
|
|
221
|
+
{/* Featured Image */}
|
|
222
|
+
{post.featuredImage && (
|
|
223
|
+
<div className="relative aspect-[16/9] mb-8 rounded-lg overflow-hidden bg-muted">
|
|
224
|
+
<Image
|
|
225
|
+
src={post.featuredImage.url}
|
|
226
|
+
alt={post.featuredImage.altText || post.title}
|
|
227
|
+
fill
|
|
228
|
+
className="object-cover"
|
|
229
|
+
priority
|
|
230
|
+
/>
|
|
231
|
+
</div>
|
|
232
|
+
)}
|
|
233
|
+
|
|
234
|
+
{/* Content */}
|
|
235
|
+
<div
|
|
236
|
+
className="prose prose-lg max-w-none dark:prose-invert mb-8"
|
|
237
|
+
dangerouslySetInnerHTML={{ __html: post.content }}
|
|
238
|
+
/>
|
|
239
|
+
|
|
240
|
+
{/* Tags */}
|
|
241
|
+
{post.tags.length > 0 && (
|
|
242
|
+
<div className="flex flex-wrap gap-2 mb-8">
|
|
243
|
+
{post.tags.map((tag) => (
|
|
244
|
+
<Link key={tag.id} href={`/blog/tag/${tag.slug}`}>
|
|
245
|
+
<Badge variant="outline" className="hover:bg-muted">
|
|
246
|
+
#{tag.name}
|
|
247
|
+
</Badge>
|
|
248
|
+
</Link>
|
|
249
|
+
))}
|
|
250
|
+
</div>
|
|
251
|
+
)}
|
|
252
|
+
|
|
253
|
+
<Separator className="my-8" />
|
|
254
|
+
|
|
255
|
+
{/* Author Box */}
|
|
256
|
+
<Card className="mb-8">
|
|
257
|
+
<CardContent className="p-6">
|
|
258
|
+
<div className="flex items-start gap-4">
|
|
259
|
+
{post.author.avatar ? (
|
|
260
|
+
<Image
|
|
261
|
+
src={post.author.avatar.url}
|
|
262
|
+
alt={post.author.name}
|
|
263
|
+
width={64}
|
|
264
|
+
height={64}
|
|
265
|
+
className="rounded-full"
|
|
266
|
+
/>
|
|
267
|
+
) : (
|
|
268
|
+
<div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center flex-shrink-0">
|
|
269
|
+
<User className="h-8 w-8 text-muted-foreground" />
|
|
270
|
+
</div>
|
|
271
|
+
)}
|
|
272
|
+
<div>
|
|
273
|
+
<h3 className="font-semibold">O autorze</h3>
|
|
274
|
+
<p className="font-medium text-lg">{post.author.name}</p>
|
|
275
|
+
{post.author.bio && (
|
|
276
|
+
<p className="text-muted-foreground mt-2">{post.author.bio}</p>
|
|
277
|
+
)}
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
</CardContent>
|
|
281
|
+
</Card>
|
|
282
|
+
|
|
283
|
+
{/* Related Posts */}
|
|
284
|
+
{relatedPosts.length > 0 && (
|
|
285
|
+
<section>
|
|
286
|
+
<h2 className="text-2xl font-bold mb-6">Powiązane artykuły</h2>
|
|
287
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
288
|
+
{relatedPosts.map((relatedPost) => (
|
|
289
|
+
<BlogCard key={relatedPost.id} post={relatedPost} />
|
|
290
|
+
))}
|
|
291
|
+
</div>
|
|
292
|
+
</section>
|
|
293
|
+
)}
|
|
294
|
+
</div>
|
|
295
|
+
|
|
296
|
+
{/* Sidebar */}
|
|
297
|
+
<div className="lg:col-span-1">
|
|
298
|
+
<div className="sticky top-24">
|
|
299
|
+
{/* Share */}
|
|
300
|
+
<Card className="mb-6">
|
|
301
|
+
<CardContent className="p-4">
|
|
302
|
+
<h3 className="font-semibold mb-3 flex items-center gap-2">
|
|
303
|
+
<Share2 className="h-4 w-4" />
|
|
304
|
+
Udostępnij
|
|
305
|
+
</h3>
|
|
306
|
+
<div className="flex gap-2">
|
|
307
|
+
<Button variant="outline" size="sm" className="flex-1">
|
|
308
|
+
Facebook
|
|
309
|
+
</Button>
|
|
310
|
+
<Button variant="outline" size="sm" className="flex-1">
|
|
311
|
+
Twitter
|
|
312
|
+
</Button>
|
|
313
|
+
</div>
|
|
314
|
+
</CardContent>
|
|
315
|
+
</Card>
|
|
316
|
+
|
|
317
|
+
<BlogSidebar recentPosts={relatedPosts.slice(0, 3)} />
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
</article>
|
|
322
|
+
);
|
|
323
|
+
}
|