@doswiftly/cli 0.1.17 → 0.1.19
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.map +1 -1
- package/dist/commands/deploy.js +43 -20
- 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 +1 -1
- package/templates/storefront-minimal/wrangler.toml +4 -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 +4 -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 +148 -35
- package/templates/storefront-nextjs-shadcn/README.md +29 -162
- package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +98 -91
- package/templates/storefront-nextjs-shadcn/app/account/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/account/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +53 -162
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/loading.tsx +60 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +36 -47
- package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +46 -29
- package/templates/storefront-nextjs-shadcn/app/account/page.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +108 -71
- 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/auth/forgot-password/page.tsx +10 -5
- package/templates/storefront-nextjs-shadcn/app/blog/[slug]/loading.tsx +17 -0
- package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +43 -2
- package/templates/storefront-nextjs-shadcn/app/blog/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +2 -1
- package/templates/storefront-nextjs-shadcn/app/cart/loading.tsx +26 -0
- package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +6 -3
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/category-products-client.tsx +56 -0
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/loading.tsx +32 -0
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +76 -59
- package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +8 -4
- package/templates/storefront-nextjs-shadcn/app/checkout/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/loading.tsx +31 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +116 -79
- package/templates/storefront-nextjs-shadcn/app/collections/[handle]/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/collections/loading.tsx +18 -0
- package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +7 -4
- 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 +46 -11
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/loading.tsx +29 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +6 -6
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +15 -61
- package/templates/storefront-nextjs-shadcn/app/products/loading.tsx +32 -0
- package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +405 -151
- package/templates/storefront-nextjs-shadcn/app/search/loading.tsx +18 -0
- package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
- package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +26 -24
- package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +9 -9
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +11 -37
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +37 -23
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +4 -3
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +22 -7
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +3 -3
- package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +2 -1
- package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +2 -12
- package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
- package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +4 -4
- package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +33 -23
- 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 +10 -19
- 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/b2b-price-display.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +69 -0
- package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +84 -0
- package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +138 -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 +3 -31
- package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +176 -123
- package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +2 -2
- 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 +19 -7
- 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 +1 -7
- 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/stores-provider.tsx +30 -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 +3 -2
- package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
- 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-icon.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/generated/graphql.ts +12779 -0
- package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +2 -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 +51 -19
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +13 -9
- 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 +32 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +687 -632
- package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +86 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +131 -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/next-env.d.ts +6 -0
- package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
- package/templates/storefront-nextjs-shadcn/package.json +12 -13
- package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
- package/templates/storefront-nextjs-shadcn/proxy.ts +3 -4
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +41 -39
- 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 +4 -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/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
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# Custom GraphQL Operations — Example File
|
|
3
|
+
# ============================================================================
|
|
4
|
+
#
|
|
5
|
+
# How to add custom queries/mutations to your storefront:
|
|
6
|
+
#
|
|
7
|
+
# 1. Create a .graphql file in this directory (e.g. graphql/my-queries.graphql)
|
|
8
|
+
# 2. Run `pnpm run codegen` to generate TypeScript types
|
|
9
|
+
# 3. Import the generated Document constant from @/generated/graphql
|
|
10
|
+
#
|
|
11
|
+
# Example usage in components:
|
|
12
|
+
#
|
|
13
|
+
# // Server Component
|
|
14
|
+
# import { request } from '@/lib/graphql/server';
|
|
15
|
+
# import { GetFeaturedProductsDocument } from '@/generated/graphql';
|
|
16
|
+
# const data = await request(GetFeaturedProductsDocument, { first: 6 });
|
|
17
|
+
#
|
|
18
|
+
# // Client Component
|
|
19
|
+
# import { useGraphQLQuery } from '@/lib/graphql/hooks';
|
|
20
|
+
# import { GetFeaturedProductsDocument } from '@/generated/graphql';
|
|
21
|
+
# const { data } = useGraphQLQuery(GetFeaturedProductsDocument, { first: 6 });
|
|
22
|
+
#
|
|
23
|
+
# To activate these examples, rename this file to custom.graphql and run codegen.
|
|
24
|
+
# ============================================================================
|
|
25
|
+
|
|
26
|
+
# Get featured products with specific fields
|
|
27
|
+
query GetFeaturedProducts($first: Int = 6) {
|
|
28
|
+
products(first: $first, query: "tag:featured") {
|
|
29
|
+
edges {
|
|
30
|
+
node {
|
|
31
|
+
id
|
|
32
|
+
handle
|
|
33
|
+
title
|
|
34
|
+
description
|
|
35
|
+
featuredImage {
|
|
36
|
+
url
|
|
37
|
+
altText
|
|
38
|
+
width
|
|
39
|
+
height
|
|
40
|
+
}
|
|
41
|
+
priceRange {
|
|
42
|
+
minVariantPrice {
|
|
43
|
+
amount
|
|
44
|
+
currencyCode
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
cursor
|
|
49
|
+
}
|
|
50
|
+
pageInfo {
|
|
51
|
+
hasNextPage
|
|
52
|
+
endCursor
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Search products with filters (category, price, attributes)
|
|
58
|
+
query SearchProductsWithFilters(
|
|
59
|
+
$query: String!
|
|
60
|
+
$first: Int = 20
|
|
61
|
+
$after: String
|
|
62
|
+
$filters: ProductFilterInput
|
|
63
|
+
) {
|
|
64
|
+
products(
|
|
65
|
+
query: $query
|
|
66
|
+
first: $first
|
|
67
|
+
after: $after
|
|
68
|
+
filters: $filters
|
|
69
|
+
) {
|
|
70
|
+
edges {
|
|
71
|
+
node {
|
|
72
|
+
id
|
|
73
|
+
handle
|
|
74
|
+
title
|
|
75
|
+
vendor
|
|
76
|
+
productType
|
|
77
|
+
featuredImage {
|
|
78
|
+
url
|
|
79
|
+
altText
|
|
80
|
+
}
|
|
81
|
+
priceRange {
|
|
82
|
+
minVariantPrice {
|
|
83
|
+
amount
|
|
84
|
+
currencyCode
|
|
85
|
+
}
|
|
86
|
+
maxVariantPrice {
|
|
87
|
+
amount
|
|
88
|
+
currencyCode
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
cursor
|
|
93
|
+
}
|
|
94
|
+
pageInfo {
|
|
95
|
+
hasNextPage
|
|
96
|
+
hasPreviousPage
|
|
97
|
+
startCursor
|
|
98
|
+
endCursor
|
|
99
|
+
}
|
|
100
|
+
totalCount
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Get collection with products (inline — useful when collection page needs custom fields)
|
|
105
|
+
query GetCollectionWithProducts(
|
|
106
|
+
$handle: String!
|
|
107
|
+
$first: Int = 20
|
|
108
|
+
$after: String
|
|
109
|
+
) {
|
|
110
|
+
collection(handle: $handle) {
|
|
111
|
+
id
|
|
112
|
+
title
|
|
113
|
+
description
|
|
114
|
+
descriptionHtml
|
|
115
|
+
image {
|
|
116
|
+
url
|
|
117
|
+
altText
|
|
118
|
+
}
|
|
119
|
+
seo {
|
|
120
|
+
title
|
|
121
|
+
description
|
|
122
|
+
}
|
|
123
|
+
products(first: $first, after: $after) {
|
|
124
|
+
edges {
|
|
125
|
+
node {
|
|
126
|
+
id
|
|
127
|
+
handle
|
|
128
|
+
title
|
|
129
|
+
description
|
|
130
|
+
featuredImage {
|
|
131
|
+
url
|
|
132
|
+
altText
|
|
133
|
+
}
|
|
134
|
+
priceRange {
|
|
135
|
+
minVariantPrice {
|
|
136
|
+
amount
|
|
137
|
+
currencyCode
|
|
138
|
+
baseAmount
|
|
139
|
+
baseCurrencyCode
|
|
140
|
+
isConverted
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
vendor
|
|
144
|
+
productType
|
|
145
|
+
tags
|
|
146
|
+
totalInventory
|
|
147
|
+
}
|
|
148
|
+
cursor
|
|
149
|
+
}
|
|
150
|
+
pageInfo {
|
|
151
|
+
hasNextPage
|
|
152
|
+
hasPreviousPage
|
|
153
|
+
startCursor
|
|
154
|
+
endCursor
|
|
155
|
+
}
|
|
156
|
+
totalCount
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export { useAuth } from "./use-auth";
|
|
6
|
+
export type { LoginResult, LogoutResult, TokenRenewResult } from "./use-auth";
|
|
7
|
+
export { useAuthSync } from "./use-auth-sync";
|
|
6
8
|
export { useCartSync } from "./use-cart-sync";
|
|
7
9
|
export { useCartActions } from "./use-cart-actions";
|
|
8
10
|
export { useFilterParams } from "./use-filter-params";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { useAuthStore, useAuthHydrated } from '@doswiftly/storefront-sdk/react';
|
|
6
|
+
import { createAuthTokenClient } from '@doswiftly/storefront-sdk';
|
|
7
|
+
|
|
8
|
+
const { clearToken } = createAuthTokenClient();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detects and fixes auth state desync between httpOnly cookie and Zustand store.
|
|
12
|
+
*
|
|
13
|
+
* When server says authenticated (cookie exists → initialIsAuthenticated: true)
|
|
14
|
+
* but store has no accessToken after persist hydration (localStorage empty/cleared),
|
|
15
|
+
* the client can't make authenticated GraphQL requests.
|
|
16
|
+
*
|
|
17
|
+
* Fix: clear the stale cookie, reset auth state, redirect to login.
|
|
18
|
+
* The user re-authenticates once, which correctly populates both cookie AND localStorage.
|
|
19
|
+
*/
|
|
20
|
+
export function useAuthSync() {
|
|
21
|
+
const router = useRouter();
|
|
22
|
+
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
|
|
23
|
+
const accessToken = useAuthStore((s) => s.accessToken);
|
|
24
|
+
const clearAuth = useAuthStore((s) => s.clearAuth);
|
|
25
|
+
const authHydrated = useAuthHydrated();
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!authHydrated) return;
|
|
29
|
+
|
|
30
|
+
// Desync: cookie exists (isAuthenticated: true from server) but no token in store
|
|
31
|
+
if (isAuthenticated && !accessToken) {
|
|
32
|
+
// Reset store state immediately
|
|
33
|
+
clearAuth();
|
|
34
|
+
// Clear stale cookie THEN redirect — must await so proxy sees cleared cookie
|
|
35
|
+
clearToken()
|
|
36
|
+
.catch(() => {})
|
|
37
|
+
.finally(() => {
|
|
38
|
+
router.push('/auth/login');
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}, [authHydrated, isAuthenticated, accessToken, clearAuth, router]);
|
|
42
|
+
}
|
|
@@ -1,310 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* useAuth — thin wrapper around SDK's useAuth with template-specific integrations.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 1. Login: Calls GraphQL mutation → Stores token in httpOnly cookie via API route
|
|
8
|
-
* 2. Logout: Calls GraphQL mutation → Clears httpOnly cookie via API route
|
|
9
|
-
* 3. Token validation: Happens on GraphQL backend (not client-side)
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* - Login/Register pages: Call login() after successful mutation
|
|
13
|
-
* - Protected pages: Use AuthGuard component (checks cookie existence)
|
|
14
|
-
* - Logout button: Call logout() to clear session
|
|
15
|
-
*
|
|
16
|
-
* @see lib/auth/cookies.ts - Cookie helper functions
|
|
17
|
-
* @see lib/auth/routes.ts - Route configuration
|
|
18
|
-
* @see proxy.ts - Server-side route protection
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```tsx
|
|
22
|
-
* function LoginForm() {
|
|
23
|
-
* const { login, isLoading, error } = useAuth();
|
|
24
|
-
*
|
|
25
|
-
* const handleSubmit = async (e) => {
|
|
26
|
-
* e.preventDefault();
|
|
27
|
-
* const result = await login(email, password);
|
|
28
|
-
* if (result.userErrors.length === 0) {
|
|
29
|
-
* // Success - redirected to /account
|
|
30
|
-
* }
|
|
31
|
-
* };
|
|
32
|
-
*
|
|
33
|
-
* return <form onSubmit={handleSubmit}>...</form>;
|
|
34
|
-
* }
|
|
35
|
-
* ```
|
|
4
|
+
* Adds:
|
|
5
|
+
* - httpOnly cookie management via API routes (onSetToken/onClearToken)
|
|
6
|
+
* - React Query cache invalidation on login/logout
|
|
36
7
|
*/
|
|
37
8
|
|
|
38
|
-
|
|
9
|
+
'use client';
|
|
39
10
|
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import { getGraphQLClient } from "@/lib/graphql/client";
|
|
44
|
-
import {
|
|
45
|
-
CustomerLoginDocument,
|
|
46
|
-
CustomerLogoutDocument,
|
|
47
|
-
CustomerTokenRenewDocument,
|
|
48
|
-
type CustomerLoginMutation,
|
|
49
|
-
type CustomerLogoutMutation,
|
|
50
|
-
type CustomerTokenRenewMutation,
|
|
51
|
-
} from "@/generated/graphql";
|
|
52
|
-
import { setAuthToken, clearAuthToken, getAuthToken } from "@/lib/auth/cookies";
|
|
53
|
-
import { redirects } from "@/lib/auth/routes";
|
|
11
|
+
import { useAuth as useSDKAuth, type LoginResult, type LogoutResult, type TokenRenewResult } from '@doswiftly/storefront-sdk/react';
|
|
12
|
+
import { createAuthTokenClient } from '@doswiftly/storefront-sdk';
|
|
13
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
54
14
|
|
|
55
|
-
|
|
56
|
-
* Login input
|
|
57
|
-
*/
|
|
58
|
-
export interface LoginInput {
|
|
59
|
-
email: string;
|
|
60
|
-
password: string;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Login result
|
|
65
|
-
*/
|
|
66
|
-
export interface LoginResult {
|
|
67
|
-
success: boolean;
|
|
68
|
-
userErrors: Array<{ message: string; field?: string[] }>;
|
|
69
|
-
accessToken?: string;
|
|
70
|
-
expiresAt?: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Logout result
|
|
75
|
-
*/
|
|
76
|
-
export interface LogoutResult {
|
|
77
|
-
success: boolean;
|
|
78
|
-
userErrors: Array<{ message: string; field?: string[] }>;
|
|
79
|
-
}
|
|
15
|
+
export type { LoginResult, LogoutResult, TokenRenewResult };
|
|
80
16
|
|
|
81
|
-
|
|
82
|
-
* Token renew result
|
|
83
|
-
*/
|
|
84
|
-
export interface TokenRenewResult {
|
|
85
|
-
success: boolean;
|
|
86
|
-
userErrors: Array<{ message: string; field?: string[] }>;
|
|
87
|
-
accessToken?: string;
|
|
88
|
-
expiresAt?: string;
|
|
89
|
-
}
|
|
17
|
+
const { setToken, clearToken } = createAuthTokenClient();
|
|
90
18
|
|
|
91
|
-
/**
|
|
92
|
-
* Authentication hook
|
|
93
|
-
*/
|
|
94
19
|
export function useAuth() {
|
|
95
|
-
const router = useRouter();
|
|
96
|
-
const searchParams = useSearchParams();
|
|
97
20
|
const queryClient = useQueryClient();
|
|
98
|
-
const client = getGraphQLClient();
|
|
99
|
-
|
|
100
|
-
const [error, setError] = useState<string | null>(null);
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Login mutation
|
|
104
|
-
*/
|
|
105
|
-
const loginMutation = useMutation({
|
|
106
|
-
mutationFn: async (input: LoginInput): Promise<LoginResult> => {
|
|
107
|
-
const data = await client.request<CustomerLoginMutation>(
|
|
108
|
-
CustomerLoginDocument,
|
|
109
|
-
{ input }
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
const { customerAccessToken, userErrors } =
|
|
113
|
-
data.customerAccessTokenCreate;
|
|
114
|
-
|
|
115
|
-
if (userErrors && userErrors.length > 0) {
|
|
116
|
-
return {
|
|
117
|
-
success: false,
|
|
118
|
-
userErrors: userErrors.map((e: any) => ({
|
|
119
|
-
message: e.message,
|
|
120
|
-
field: e.field || undefined,
|
|
121
|
-
})),
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (!customerAccessToken) {
|
|
126
|
-
return {
|
|
127
|
-
success: false,
|
|
128
|
-
userErrors: [{ message: "Failed to create access token" }],
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Store token in httpOnly cookie via API route
|
|
133
|
-
await setAuthToken(customerAccessToken.accessToken);
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
success: true,
|
|
137
|
-
userErrors: [],
|
|
138
|
-
accessToken: customerAccessToken.accessToken,
|
|
139
|
-
expiresAt: customerAccessToken.expiresAt,
|
|
140
|
-
};
|
|
141
|
-
},
|
|
142
|
-
onSuccess: (result: LoginResult) => {
|
|
143
|
-
if (result.success) {
|
|
144
|
-
// Clear any cached queries that might need authentication
|
|
145
|
-
queryClient.invalidateQueries();
|
|
146
21
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
22
|
+
return useSDKAuth({
|
|
23
|
+
onSetToken: async (token: string) => {
|
|
24
|
+
await setToken(token);
|
|
25
|
+
queryClient.invalidateQueries();
|
|
151
26
|
},
|
|
152
|
-
|
|
153
|
-
|
|
27
|
+
onClearToken: async () => {
|
|
28
|
+
await clearToken();
|
|
29
|
+
queryClient.clear();
|
|
154
30
|
},
|
|
155
31
|
});
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Logout mutation
|
|
159
|
-
*/
|
|
160
|
-
const logoutMutation = useMutation({
|
|
161
|
-
mutationFn: async (): Promise<LogoutResult> => {
|
|
162
|
-
const token = getAuthToken();
|
|
163
|
-
|
|
164
|
-
if (!token) {
|
|
165
|
-
// No token to logout
|
|
166
|
-
return {
|
|
167
|
-
success: true,
|
|
168
|
-
userErrors: [],
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const data = await client.request<CustomerLogoutMutation>(
|
|
173
|
-
CustomerLogoutDocument,
|
|
174
|
-
{ customerAccessToken: token }
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
const { userErrors } = data.customerAccessTokenDelete;
|
|
178
|
-
|
|
179
|
-
if (userErrors && userErrors.length > 0) {
|
|
180
|
-
return {
|
|
181
|
-
success: false,
|
|
182
|
-
userErrors: userErrors.map((e: any) => ({
|
|
183
|
-
message: e.message,
|
|
184
|
-
field: e.field || undefined,
|
|
185
|
-
})),
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Clear httpOnly cookie via API route
|
|
190
|
-
await clearAuthToken();
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
success: true,
|
|
194
|
-
userErrors: [],
|
|
195
|
-
};
|
|
196
|
-
},
|
|
197
|
-
onSuccess: (result: LogoutResult) => {
|
|
198
|
-
if (result.success) {
|
|
199
|
-
// Clear all cached queries
|
|
200
|
-
queryClient.clear();
|
|
201
|
-
|
|
202
|
-
// Redirect to home page
|
|
203
|
-
router.push("/");
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
onError: (err: unknown) => {
|
|
207
|
-
setError(err instanceof Error ? err.message : "Logout failed");
|
|
208
|
-
},
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Token renew mutation
|
|
213
|
-
*/
|
|
214
|
-
const renewTokenMutation = useMutation({
|
|
215
|
-
mutationFn: async (): Promise<TokenRenewResult> => {
|
|
216
|
-
const token = getAuthToken();
|
|
217
|
-
|
|
218
|
-
if (!token) {
|
|
219
|
-
return {
|
|
220
|
-
success: false,
|
|
221
|
-
userErrors: [{ message: "No token to renew" }],
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const data = await client.request<CustomerTokenRenewMutation>(
|
|
226
|
-
CustomerTokenRenewDocument,
|
|
227
|
-
{ customerAccessToken: token }
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
const { customerAccessToken, userErrors } =
|
|
231
|
-
data.customerAccessTokenRenew;
|
|
232
|
-
|
|
233
|
-
if (userErrors && userErrors.length > 0) {
|
|
234
|
-
return {
|
|
235
|
-
success: false,
|
|
236
|
-
userErrors: userErrors.map((e: any) => ({
|
|
237
|
-
message: e.message,
|
|
238
|
-
field: e.field || undefined,
|
|
239
|
-
})),
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (!customerAccessToken) {
|
|
244
|
-
return {
|
|
245
|
-
success: false,
|
|
246
|
-
userErrors: [{ message: "Failed to renew access token" }],
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Update token in httpOnly cookie via API route
|
|
251
|
-
await setAuthToken(customerAccessToken.accessToken);
|
|
252
|
-
|
|
253
|
-
return {
|
|
254
|
-
success: true,
|
|
255
|
-
userErrors: [],
|
|
256
|
-
accessToken: customerAccessToken.accessToken,
|
|
257
|
-
expiresAt: customerAccessToken.expiresAt,
|
|
258
|
-
};
|
|
259
|
-
},
|
|
260
|
-
onError: (err: unknown) => {
|
|
261
|
-
setError(err instanceof Error ? err.message : "Token renewal failed");
|
|
262
|
-
},
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Login function
|
|
267
|
-
*/
|
|
268
|
-
const login = async (email: string, password: string): Promise<LoginResult> => {
|
|
269
|
-
setError(null);
|
|
270
|
-
return loginMutation.mutateAsync({ email, password });
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Logout function
|
|
275
|
-
*/
|
|
276
|
-
const logout = async (): Promise<LogoutResult> => {
|
|
277
|
-
setError(null);
|
|
278
|
-
return logoutMutation.mutateAsync();
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Renew token function
|
|
283
|
-
*/
|
|
284
|
-
const renewToken = async (): Promise<TokenRenewResult> => {
|
|
285
|
-
setError(null);
|
|
286
|
-
return renewTokenMutation.mutateAsync();
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
return {
|
|
290
|
-
// Functions
|
|
291
|
-
login,
|
|
292
|
-
logout,
|
|
293
|
-
renewToken,
|
|
294
|
-
|
|
295
|
-
// State
|
|
296
|
-
isLoggingIn: loginMutation.isPending,
|
|
297
|
-
isLoggingOut: logoutMutation.isPending,
|
|
298
|
-
isRenewingToken: renewTokenMutation.isPending,
|
|
299
|
-
isLoading:
|
|
300
|
-
loginMutation.isPending ||
|
|
301
|
-
logoutMutation.isPending ||
|
|
302
|
-
renewTokenMutation.isPending,
|
|
303
|
-
error,
|
|
304
|
-
|
|
305
|
-
// Results
|
|
306
|
-
loginResult: loginMutation.data,
|
|
307
|
-
logoutResult: logoutMutation.data,
|
|
308
|
-
renewResult: renewTokenMutation.data,
|
|
309
|
-
};
|
|
310
32
|
}
|