@shopify/cli-hydrogen 5.0.2 → 5.1.0

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.
Files changed (243) hide show
  1. package/dist/commands/hydrogen/build.js +16 -2
  2. package/dist/commands/hydrogen/codegen-unstable.js +13 -24
  3. package/dist/commands/hydrogen/dev.js +45 -39
  4. package/dist/commands/hydrogen/env/list.js +25 -24
  5. package/dist/commands/hydrogen/env/list.test.js +46 -43
  6. package/dist/commands/hydrogen/env/pull.js +53 -25
  7. package/dist/commands/hydrogen/env/pull.test.js +123 -42
  8. package/dist/commands/hydrogen/generate/route.js +31 -132
  9. package/dist/commands/hydrogen/generate/route.test.js +34 -126
  10. package/dist/commands/hydrogen/init.js +46 -127
  11. package/dist/commands/hydrogen/init.test.js +352 -100
  12. package/dist/commands/hydrogen/link.js +70 -69
  13. package/dist/commands/hydrogen/link.test.js +72 -107
  14. package/dist/commands/hydrogen/list.js +22 -12
  15. package/dist/commands/hydrogen/list.test.js +51 -48
  16. package/dist/commands/hydrogen/login.js +31 -0
  17. package/dist/commands/hydrogen/logout.js +21 -0
  18. package/dist/commands/hydrogen/setup/css.js +79 -0
  19. package/dist/commands/hydrogen/setup/markets.js +53 -0
  20. package/dist/commands/hydrogen/setup.js +133 -0
  21. package/dist/commands/hydrogen/shortcut.js +2 -45
  22. package/dist/commands/hydrogen/shortcut.test.js +10 -37
  23. package/dist/generator-templates/assets/css-modules/package.json +6 -0
  24. package/dist/generator-templates/assets/postcss/package.json +10 -0
  25. package/dist/generator-templates/assets/postcss/postcss.config.js +8 -0
  26. package/dist/generator-templates/assets/tailwind/package.json +13 -0
  27. package/dist/generator-templates/assets/tailwind/postcss.config.js +10 -0
  28. package/dist/generator-templates/assets/tailwind/tailwind.config.js +8 -0
  29. package/dist/generator-templates/assets/tailwind/tailwind.css +3 -0
  30. package/dist/generator-templates/assets/vanilla-extract/package.json +9 -0
  31. package/dist/generator-templates/starter/.eslintignore +5 -0
  32. package/dist/generator-templates/starter/.eslintrc.js +18 -0
  33. package/dist/generator-templates/starter/.graphqlrc.yml +1 -0
  34. package/dist/generator-templates/starter/README.md +40 -0
  35. package/dist/generator-templates/starter/app/components/Aside.tsx +47 -0
  36. package/dist/generator-templates/starter/app/components/Cart.tsx +340 -0
  37. package/dist/generator-templates/starter/app/components/Footer.tsx +99 -0
  38. package/dist/generator-templates/starter/app/components/Header.tsx +178 -0
  39. package/dist/generator-templates/starter/app/components/Layout.tsx +95 -0
  40. package/dist/generator-templates/starter/app/components/Search.tsx +480 -0
  41. package/dist/generator-templates/starter/app/entry.client.tsx +12 -0
  42. package/dist/generator-templates/starter/app/entry.server.tsx +33 -0
  43. package/dist/generator-templates/starter/app/root.tsx +270 -0
  44. package/dist/generator-templates/starter/app/routes/$.tsx +7 -0
  45. package/dist/generator-templates/{routes → starter/app/routes}/[robots.txt].tsx +47 -69
  46. package/dist/generator-templates/starter/app/routes/[sitemap.xml].tsx +174 -0
  47. package/dist/generator-templates/starter/app/routes/_index.tsx +145 -0
  48. package/dist/generator-templates/starter/app/routes/account.$.tsx +9 -0
  49. package/dist/generator-templates/starter/app/routes/account.addresses.tsx +563 -0
  50. package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +309 -0
  51. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +196 -0
  52. package/dist/generator-templates/starter/app/routes/account.profile.tsx +289 -0
  53. package/dist/generator-templates/starter/app/routes/account.tsx +203 -0
  54. package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +157 -0
  55. package/dist/generator-templates/starter/app/routes/account_.login.tsx +143 -0
  56. package/dist/generator-templates/starter/app/routes/account_.logout.tsx +33 -0
  57. package/dist/generator-templates/starter/app/routes/account_.recover.tsx +124 -0
  58. package/dist/generator-templates/starter/app/routes/account_.register.tsx +207 -0
  59. package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +136 -0
  60. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +342 -0
  61. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +88 -0
  62. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +162 -0
  63. package/dist/generator-templates/starter/app/routes/blogs._index.tsx +94 -0
  64. package/dist/generator-templates/starter/app/routes/cart.tsx +104 -0
  65. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +184 -0
  66. package/dist/generator-templates/starter/app/routes/collections._index.tsx +120 -0
  67. package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +57 -0
  68. package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +94 -0
  69. package/dist/generator-templates/starter/app/routes/policies._index.tsx +63 -0
  70. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +418 -0
  71. package/dist/generator-templates/starter/app/routes/search.tsx +168 -0
  72. package/dist/generator-templates/starter/app/styles/app.css +473 -0
  73. package/dist/generator-templates/starter/app/styles/reset.css +129 -0
  74. package/dist/generator-templates/starter/app/utils.ts +46 -0
  75. package/dist/generator-templates/starter/package.json +43 -0
  76. package/dist/generator-templates/starter/public/favicon.svg +28 -0
  77. package/dist/generator-templates/starter/remix.config.js +26 -0
  78. package/dist/generator-templates/starter/remix.env.d.ts +39 -0
  79. package/dist/generator-templates/starter/server.ts +253 -0
  80. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +1906 -0
  81. package/dist/generator-templates/starter/tsconfig.json +22 -0
  82. package/dist/lib/auth.js +123 -0
  83. package/dist/lib/auth.test.js +157 -0
  84. package/dist/lib/build.js +51 -0
  85. package/dist/lib/check-version.js +3 -3
  86. package/dist/lib/check-version.test.js +24 -0
  87. package/dist/lib/codegen.js +26 -17
  88. package/dist/lib/environment-variables.js +68 -0
  89. package/dist/lib/environment-variables.test.js +147 -0
  90. package/dist/lib/file.js +41 -0
  91. package/dist/lib/file.test.js +69 -0
  92. package/dist/lib/flags.js +39 -2
  93. package/dist/lib/format-code.js +26 -0
  94. package/dist/lib/gid.js +12 -0
  95. package/dist/lib/{graphql.test.js → gid.test.js} +1 -1
  96. package/dist/lib/graphql/admin/client.js +27 -0
  97. package/dist/lib/graphql/admin/client.test.js +51 -0
  98. package/dist/lib/graphql/admin/create-storefront.js +13 -15
  99. package/dist/lib/graphql/admin/create-storefront.test.js +64 -0
  100. package/dist/lib/graphql/admin/fetch-job.js +6 -15
  101. package/dist/lib/graphql/admin/link-storefront.js +7 -11
  102. package/dist/lib/graphql/admin/link-storefront.test.js +38 -0
  103. package/dist/lib/graphql/admin/list-environments.js +2 -2
  104. package/dist/lib/graphql/admin/list-environments.test.js +44 -0
  105. package/dist/lib/graphql/admin/list-storefronts.js +7 -11
  106. package/dist/lib/graphql/admin/list-storefronts.test.js +44 -0
  107. package/dist/lib/graphql/admin/pull-variables.js +3 -3
  108. package/dist/lib/graphql/admin/pull-variables.test.js +37 -0
  109. package/dist/lib/graphql/business-platform/user-account.js +83 -0
  110. package/dist/lib/graphql/business-platform/user-account.test.js +80 -0
  111. package/dist/lib/log.js +185 -9
  112. package/dist/lib/log.test.js +92 -0
  113. package/dist/lib/mini-oxygen.js +19 -9
  114. package/dist/lib/missing-routes.js +0 -2
  115. package/dist/lib/onboarding/common.js +456 -0
  116. package/dist/lib/onboarding/index.js +2 -0
  117. package/dist/lib/onboarding/local.js +229 -0
  118. package/dist/lib/onboarding/remote.js +89 -0
  119. package/dist/lib/remix-version-interop.js +5 -5
  120. package/dist/lib/remix-version-interop.test.js +11 -1
  121. package/dist/lib/render-errors.js +13 -11
  122. package/dist/lib/setups/css/assets.js +89 -0
  123. package/dist/lib/setups/css/css-modules.js +22 -0
  124. package/dist/lib/setups/css/index.js +44 -0
  125. package/dist/lib/setups/css/postcss.js +34 -0
  126. package/dist/lib/setups/css/replacers.js +137 -0
  127. package/dist/lib/setups/css/tailwind.js +54 -0
  128. package/dist/lib/setups/css/vanilla-extract.js +22 -0
  129. package/dist/lib/setups/i18n/domains.test.js +25 -0
  130. package/dist/lib/setups/i18n/index.js +46 -0
  131. package/dist/lib/setups/i18n/replacers.js +227 -0
  132. package/dist/lib/setups/i18n/subdomains.test.js +25 -0
  133. package/dist/lib/setups/i18n/subfolders.test.js +25 -0
  134. package/dist/lib/setups/i18n/templates/domains.js +14 -0
  135. package/dist/lib/setups/i18n/templates/domains.ts +25 -0
  136. package/dist/lib/setups/i18n/templates/subdomains.js +14 -0
  137. package/dist/lib/setups/i18n/templates/subdomains.ts +24 -0
  138. package/dist/lib/setups/i18n/templates/subfolders.js +14 -0
  139. package/dist/lib/setups/i18n/templates/subfolders.ts +28 -0
  140. package/dist/lib/setups/routes/generate.js +244 -0
  141. package/dist/lib/setups/routes/generate.test.js +313 -0
  142. package/dist/lib/shell.js +52 -5
  143. package/dist/lib/shell.test.js +42 -16
  144. package/dist/lib/shopify-config.js +23 -18
  145. package/dist/lib/shopify-config.test.js +63 -73
  146. package/dist/lib/template-downloader.js +9 -7
  147. package/dist/lib/transpile-ts.js +9 -29
  148. package/dist/virtual-routes/routes/index.jsx +40 -19
  149. package/oclif.manifest.json +710 -1
  150. package/package.json +16 -16
  151. package/dist/commands/hydrogen/build.d.ts +0 -23
  152. package/dist/commands/hydrogen/check.d.ts +0 -15
  153. package/dist/commands/hydrogen/codegen-unstable.d.ts +0 -15
  154. package/dist/commands/hydrogen/dev.d.ts +0 -21
  155. package/dist/commands/hydrogen/env/list.d.ts +0 -18
  156. package/dist/commands/hydrogen/env/pull.d.ts +0 -22
  157. package/dist/commands/hydrogen/g.d.ts +0 -10
  158. package/dist/commands/hydrogen/generate/route.d.ts +0 -32
  159. package/dist/commands/hydrogen/generate/route.test.d.ts +0 -1
  160. package/dist/commands/hydrogen/generate/routes.d.ts +0 -16
  161. package/dist/commands/hydrogen/init.d.ts +0 -24
  162. package/dist/commands/hydrogen/init.test.d.ts +0 -1
  163. package/dist/commands/hydrogen/link.d.ts +0 -23
  164. package/dist/commands/hydrogen/link.test.d.ts +0 -1
  165. package/dist/commands/hydrogen/list.d.ts +0 -21
  166. package/dist/commands/hydrogen/list.test.d.ts +0 -1
  167. package/dist/commands/hydrogen/preview.d.ts +0 -17
  168. package/dist/commands/hydrogen/shortcut.d.ts +0 -9
  169. package/dist/commands/hydrogen/shortcut.test.d.ts +0 -1
  170. package/dist/commands/hydrogen/unlink.d.ts +0 -16
  171. package/dist/commands/hydrogen/unlink.test.d.ts +0 -1
  172. package/dist/create-app.d.ts +0 -1
  173. package/dist/generator-templates/routes/[sitemap.xml].tsx +0 -235
  174. package/dist/generator-templates/routes/account/login.tsx +0 -103
  175. package/dist/generator-templates/routes/account/register.tsx +0 -103
  176. package/dist/generator-templates/routes/cart.tsx +0 -81
  177. package/dist/generator-templates/routes/collections/$collectionHandle.tsx +0 -104
  178. package/dist/generator-templates/routes/collections/index.tsx +0 -102
  179. package/dist/generator-templates/routes/graphiql.tsx +0 -10
  180. package/dist/generator-templates/routes/index.tsx +0 -40
  181. package/dist/generator-templates/routes/pages/$pageHandle.tsx +0 -112
  182. package/dist/generator-templates/routes/policies/$policyHandle.tsx +0 -140
  183. package/dist/generator-templates/routes/policies/index.tsx +0 -117
  184. package/dist/generator-templates/routes/products/$productHandle.tsx +0 -92
  185. package/dist/hooks/init.d.ts +0 -5
  186. package/dist/lib/admin-session.d.ts +0 -6
  187. package/dist/lib/admin-session.js +0 -16
  188. package/dist/lib/admin-session.test.d.ts +0 -1
  189. package/dist/lib/admin-session.test.js +0 -27
  190. package/dist/lib/admin-urls.d.ts +0 -8
  191. package/dist/lib/check-lockfile.d.ts +0 -3
  192. package/dist/lib/check-lockfile.test.d.ts +0 -1
  193. package/dist/lib/check-version.d.ts +0 -16
  194. package/dist/lib/check-version.test.d.ts +0 -1
  195. package/dist/lib/codegen.d.ts +0 -26
  196. package/dist/lib/combined-environment-variables.d.ts +0 -8
  197. package/dist/lib/combined-environment-variables.js +0 -57
  198. package/dist/lib/combined-environment-variables.test.d.ts +0 -1
  199. package/dist/lib/combined-environment-variables.test.js +0 -111
  200. package/dist/lib/config.d.ts +0 -20
  201. package/dist/lib/flags.d.ts +0 -27
  202. package/dist/lib/flags.test.d.ts +0 -1
  203. package/dist/lib/graphql/admin/create-storefront.d.ts +0 -17
  204. package/dist/lib/graphql/admin/fetch-job.d.ts +0 -23
  205. package/dist/lib/graphql/admin/link-storefront.d.ts +0 -14
  206. package/dist/lib/graphql/admin/list-environments.d.ts +0 -21
  207. package/dist/lib/graphql/admin/list-storefronts.d.ts +0 -25
  208. package/dist/lib/graphql/admin/pull-variables.d.ts +0 -21
  209. package/dist/lib/graphql.d.ts +0 -21
  210. package/dist/lib/graphql.js +0 -18
  211. package/dist/lib/graphql.test.d.ts +0 -1
  212. package/dist/lib/log.d.ts +0 -6
  213. package/dist/lib/mini-oxygen.d.ts +0 -22
  214. package/dist/lib/missing-routes.d.ts +0 -8
  215. package/dist/lib/missing-routes.test.d.ts +0 -1
  216. package/dist/lib/missing-storefronts.d.ts +0 -5
  217. package/dist/lib/missing-storefronts.js +0 -18
  218. package/dist/lib/process.d.ts +0 -6
  219. package/dist/lib/pull-environment-variables.d.ts +0 -20
  220. package/dist/lib/pull-environment-variables.js +0 -57
  221. package/dist/lib/pull-environment-variables.test.d.ts +0 -1
  222. package/dist/lib/pull-environment-variables.test.js +0 -174
  223. package/dist/lib/remix-version-interop.d.ts +0 -11
  224. package/dist/lib/remix-version-interop.test.d.ts +0 -1
  225. package/dist/lib/render-errors.d.ts +0 -16
  226. package/dist/lib/shell.d.ts +0 -11
  227. package/dist/lib/shell.test.d.ts +0 -1
  228. package/dist/lib/shop.d.ts +0 -7
  229. package/dist/lib/shop.js +0 -32
  230. package/dist/lib/shop.test.d.ts +0 -1
  231. package/dist/lib/shop.test.js +0 -78
  232. package/dist/lib/shopify-config.d.ts +0 -35
  233. package/dist/lib/shopify-config.test.d.ts +0 -1
  234. package/dist/lib/string.d.ts +0 -3
  235. package/dist/lib/string.test.d.ts +0 -1
  236. package/dist/lib/template-downloader.d.ts +0 -6
  237. package/dist/lib/transpile-ts.d.ts +0 -16
  238. package/dist/lib/user-errors.d.ts +0 -9
  239. package/dist/lib/user-errors.js +0 -11
  240. package/dist/lib/virtual-routes.d.ts +0 -7
  241. package/dist/lib/virtual-routes.test.d.ts +0 -1
  242. /package/dist/{commands/hydrogen/env/list.test.d.ts → lib/setups/css/common.js} +0 -0
  243. /package/dist/{commands/hydrogen/env/pull.test.d.ts → lib/setups/i18n/mock-i18n-types.js} +0 -0
@@ -0,0 +1,309 @@
1
+ import {json, redirect, type LoaderArgs} from '@shopify/remix-oxygen';
2
+ import {Link, useLoaderData, type V2_MetaFunction} from '@remix-run/react';
3
+ import {Money, Image, flattenConnection} from '@shopify/hydrogen';
4
+ import type {OrderLineItemFullFragment} from 'storefrontapi.generated';
5
+
6
+ export const meta: V2_MetaFunction<typeof loader> = ({data}) => {
7
+ return [{title: `Order ${data?.order?.name}`}];
8
+ };
9
+
10
+ export async function loader({params, context}: LoaderArgs) {
11
+ const {session, storefront} = context;
12
+
13
+ if (!params.id) {
14
+ return redirect('/account/orders');
15
+ }
16
+
17
+ const orderId = atob(params.id);
18
+ const customerAccessToken = await session.get('customerAccessToken');
19
+
20
+ if (!customerAccessToken) {
21
+ return redirect('/account/login');
22
+ }
23
+
24
+ const {order} = await storefront.query(CUSTOMER_ORDER_QUERY, {
25
+ variables: {orderId},
26
+ });
27
+
28
+ if (!order || !('lineItems' in order)) {
29
+ throw new Response('Order not found', {status: 404});
30
+ }
31
+
32
+ const lineItems = flattenConnection(order.lineItems);
33
+ const discountApplications = flattenConnection(order.discountApplications);
34
+
35
+ const firstDiscount = discountApplications[0]?.value;
36
+
37
+ const discountValue =
38
+ firstDiscount?.__typename === 'MoneyV2' && firstDiscount;
39
+
40
+ const discountPercentage =
41
+ firstDiscount?.__typename === 'PricingPercentageValue' &&
42
+ firstDiscount?.percentage;
43
+
44
+ return json({
45
+ order,
46
+ lineItems,
47
+ discountValue,
48
+ discountPercentage,
49
+ });
50
+ }
51
+
52
+ export default function OrderRoute() {
53
+ const {order, lineItems, discountValue, discountPercentage} =
54
+ useLoaderData<typeof loader>();
55
+ return (
56
+ <div className="account-order">
57
+ <h2>Order {order.name}</h2>
58
+ <p>Placed on {new Date(order.processedAt!).toDateString()}</p>
59
+ <br />
60
+ <div>
61
+ <table>
62
+ <thead>
63
+ <tr>
64
+ <th scope="col">Product</th>
65
+ <th scope="col">Price</th>
66
+ <th scope="col">Quantity</th>
67
+ <th scope="col">Total</th>
68
+ </tr>
69
+ </thead>
70
+ <tbody>
71
+ {lineItems.map((lineItem, lineItemIndex) => (
72
+ // eslint-disable-next-line react/no-array-index-key
73
+ <OrderLineRow key={lineItemIndex} lineItem={lineItem} />
74
+ ))}
75
+ </tbody>
76
+ <tfoot>
77
+ {((discountValue && discountValue.amount) ||
78
+ discountPercentage) && (
79
+ <tr>
80
+ <th scope="row" colSpan={3}>
81
+ <p>Discounts</p>
82
+ </th>
83
+ <th scope="row">
84
+ <p>Discounts</p>
85
+ </th>
86
+ <td>
87
+ {discountPercentage ? (
88
+ <span>-{discountPercentage}% OFF</span>
89
+ ) : (
90
+ discountValue && <Money data={discountValue!} />
91
+ )}
92
+ </td>
93
+ </tr>
94
+ )}
95
+ <tr>
96
+ <th scope="row" colSpan={3}>
97
+ <p>Subtotal</p>
98
+ </th>
99
+ <th scope="row">
100
+ <p>Subtotal</p>
101
+ </th>
102
+ <td>
103
+ <Money data={order.subtotalPriceV2!} />
104
+ </td>
105
+ </tr>
106
+ <tr>
107
+ <th scope="row" colSpan={3}>
108
+ Tax
109
+ </th>
110
+ <th scope="row">
111
+ <p>Tax</p>
112
+ </th>
113
+ <td>
114
+ <Money data={order.totalTaxV2!} />
115
+ </td>
116
+ </tr>
117
+ <tr>
118
+ <th scope="row" colSpan={3}>
119
+ Total
120
+ </th>
121
+ <th scope="row">
122
+ <p>Total</p>
123
+ </th>
124
+ <td>
125
+ <Money data={order.totalPriceV2!} />
126
+ </td>
127
+ </tr>
128
+ </tfoot>
129
+ </table>
130
+ <div>
131
+ <h3>Shipping Address</h3>
132
+ {order?.shippingAddress ? (
133
+ <address>
134
+ <p>
135
+ {order.shippingAddress.firstName &&
136
+ order.shippingAddress.firstName + ' '}
137
+ {order.shippingAddress.lastName}
138
+ </p>
139
+ {order?.shippingAddress?.formatted ? (
140
+ order.shippingAddress.formatted.map((line: string) => (
141
+ <p key={line}>{line}</p>
142
+ ))
143
+ ) : (
144
+ <></>
145
+ )}
146
+ </address>
147
+ ) : (
148
+ <p>No shipping address defined</p>
149
+ )}
150
+ <h3>Status</h3>
151
+ <div>
152
+ <p>{order.fulfillmentStatus}</p>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ <br />
157
+ <p>
158
+ <a target="_blank" href={order.statusUrl} rel="noreferrer">
159
+ View Order Status →
160
+ </a>
161
+ </p>
162
+ </div>
163
+ );
164
+ }
165
+
166
+ function OrderLineRow({lineItem}: {lineItem: OrderLineItemFullFragment}) {
167
+ return (
168
+ <tr key={lineItem.variant!.id}>
169
+ <td>
170
+ <div>
171
+ <Link to={`/products/${lineItem.variant!.product!.handle}`}>
172
+ {lineItem?.variant?.image && (
173
+ <div>
174
+ <Image data={lineItem.variant.image} width={96} height={96} />
175
+ </div>
176
+ )}
177
+ </Link>
178
+ <div>
179
+ <p>{lineItem.title}</p>
180
+ <small>{lineItem.variant!.title}</small>
181
+ </div>
182
+ </div>
183
+ </td>
184
+ <td>
185
+ <Money data={lineItem.variant!.price!} />
186
+ </td>
187
+ <td>{lineItem.quantity}</td>
188
+ <td>
189
+ <Money data={lineItem.discountedTotalPrice!} />
190
+ </td>
191
+ </tr>
192
+ );
193
+ }
194
+
195
+ // NOTE: https://shopify.dev/docs/api/storefront/latest/objects/Order
196
+ const CUSTOMER_ORDER_QUERY = `#graphql
197
+ fragment OrderMoney on MoneyV2 {
198
+ amount
199
+ currencyCode
200
+ }
201
+ fragment AddressFull on MailingAddress {
202
+ address1
203
+ address2
204
+ city
205
+ company
206
+ country
207
+ countryCodeV2
208
+ firstName
209
+ formatted
210
+ id
211
+ lastName
212
+ name
213
+ phone
214
+ province
215
+ provinceCode
216
+ zip
217
+ }
218
+ fragment DiscountApplication on DiscountApplication {
219
+ value {
220
+ __typename
221
+ ... on MoneyV2 {
222
+ ...OrderMoney
223
+ }
224
+ ... on PricingPercentageValue {
225
+ percentage
226
+ }
227
+ }
228
+ }
229
+ fragment OrderLineProductVariant on ProductVariant {
230
+ id
231
+ image {
232
+ altText
233
+ height
234
+ url
235
+ id
236
+ width
237
+ }
238
+ price {
239
+ ...OrderMoney
240
+ }
241
+ product {
242
+ handle
243
+ }
244
+ sku
245
+ title
246
+ }
247
+ fragment OrderLineItemFull on OrderLineItem {
248
+ title
249
+ quantity
250
+ discountAllocations {
251
+ allocatedAmount {
252
+ ...OrderMoney
253
+ }
254
+ discountApplication {
255
+ ...DiscountApplication
256
+ }
257
+ }
258
+ originalTotalPrice {
259
+ ...OrderMoney
260
+ }
261
+ discountedTotalPrice {
262
+ ...OrderMoney
263
+ }
264
+ variant {
265
+ ...OrderLineProductVariant
266
+ }
267
+ }
268
+ fragment Order on Order {
269
+ id
270
+ name
271
+ orderNumber
272
+ statusUrl
273
+ processedAt
274
+ fulfillmentStatus
275
+ totalTaxV2 {
276
+ ...OrderMoney
277
+ }
278
+ totalPriceV2 {
279
+ ...OrderMoney
280
+ }
281
+ subtotalPriceV2 {
282
+ ...OrderMoney
283
+ }
284
+ shippingAddress {
285
+ ...AddressFull
286
+ }
287
+ discountApplications(first: 100) {
288
+ nodes {
289
+ ...DiscountApplication
290
+ }
291
+ }
292
+ lineItems(first: 100) {
293
+ nodes {
294
+ ...OrderLineItemFull
295
+ }
296
+ }
297
+ }
298
+ query Order(
299
+ $country: CountryCode
300
+ $language: LanguageCode
301
+ $orderId: ID!
302
+ ) @inContext(country: $country, language: $language) {
303
+ order: node(id: $orderId) {
304
+ ... on Order {
305
+ ...Order
306
+ }
307
+ }
308
+ }
309
+ ` as const;
@@ -0,0 +1,196 @@
1
+ import {Link, useLoaderData} from '@remix-run/react';
2
+ import {Money, Pagination, getPaginationVariables} from '@shopify/hydrogen';
3
+ import {
4
+ json,
5
+ redirect,
6
+ type LoaderArgs,
7
+ type V2_MetaFunction,
8
+ } from '@shopify/remix-oxygen';
9
+ import type {
10
+ CustomerOrdersFragment,
11
+ OrderItemFragment,
12
+ } from 'storefrontapi.generated';
13
+
14
+ export const meta: V2_MetaFunction = () => {
15
+ return [{title: 'Orders'}];
16
+ };
17
+
18
+ export async function loader({request, context}: LoaderArgs) {
19
+ const {session, storefront} = context;
20
+
21
+ const customerAccessToken = await session.get('customerAccessToken');
22
+ if (!customerAccessToken?.accessToken) {
23
+ return redirect('/account/login');
24
+ }
25
+
26
+ try {
27
+ const paginationVariables = getPaginationVariables(request, {
28
+ pageBy: 20,
29
+ });
30
+
31
+ const {customer} = await storefront.query(CUSTOMER_ORDERS_QUERY, {
32
+ variables: {
33
+ customerAccessToken: customerAccessToken.accessToken,
34
+ country: storefront.i18n.country,
35
+ language: storefront.i18n.language,
36
+ ...paginationVariables,
37
+ },
38
+ cache: storefront.CacheNone(),
39
+ });
40
+
41
+ if (!customer) {
42
+ throw new Error('Customer not found');
43
+ }
44
+
45
+ return json({customer});
46
+ } catch (error: unknown) {
47
+ if (error instanceof Error) {
48
+ return json({error: error.message}, {status: 400});
49
+ }
50
+ return json({error}, {status: 400});
51
+ }
52
+ }
53
+
54
+ export default function Orders() {
55
+ const {customer} = useLoaderData<{customer: CustomerOrdersFragment}>();
56
+ const {orders, numberOfOrders} = customer;
57
+ return (
58
+ <div className="orders">
59
+ <h2>
60
+ Orders <small>({numberOfOrders})</small>
61
+ </h2>
62
+ <br />
63
+ {orders.nodes.length ? <OrdersTable orders={orders} /> : <EmptyOrders />}
64
+ </div>
65
+ );
66
+ }
67
+
68
+ function OrdersTable({orders}: Pick<CustomerOrdersFragment, 'orders'>) {
69
+ return (
70
+ <div className="acccount-orders">
71
+ {orders?.nodes.length ? (
72
+ <Pagination connection={orders}>
73
+ {({nodes, isLoading, PreviousLink, NextLink}) => {
74
+ return (
75
+ <>
76
+ <PreviousLink>
77
+ {isLoading ? 'Loading...' : <span>↑ Load previous</span>}
78
+ </PreviousLink>
79
+ {nodes.map((order) => {
80
+ return <OrderItem key={order.id} order={order} />;
81
+ })}
82
+ <NextLink>
83
+ {isLoading ? 'Loading...' : <span>Load more ↓</span>}
84
+ </NextLink>
85
+ </>
86
+ );
87
+ }}
88
+ </Pagination>
89
+ ) : (
90
+ <EmptyOrders />
91
+ )}
92
+ </div>
93
+ );
94
+ }
95
+
96
+ function EmptyOrders() {
97
+ return (
98
+ <div>
99
+ <p>You haven&apos;t placed any orders yet.</p>
100
+ <br />
101
+ <p>
102
+ <Link to="/collections">Start Shopping →</Link>
103
+ </p>
104
+ </div>
105
+ );
106
+ }
107
+
108
+ function OrderItem({order}: {order: OrderItemFragment}) {
109
+ return (
110
+ <>
111
+ <fieldset>
112
+ <Link to={`/account/orders/${order.id}`}>
113
+ <strong>#{order.orderNumber}</strong>
114
+ </Link>
115
+ <p>{new Date(order.processedAt).toDateString()}</p>
116
+ <p>{order.financialStatus}</p>
117
+ <p>{order.fulfillmentStatus}</p>
118
+ <Money data={order.currentTotalPrice} />
119
+ <Link to={`/account/orders/${btoa(order.id)}`}>View Order →</Link>
120
+ </fieldset>
121
+ <br />
122
+ </>
123
+ );
124
+ }
125
+
126
+ const ORDER_ITEM_FRAGMENT = `#graphql
127
+ fragment OrderItem on Order {
128
+ currentTotalPrice {
129
+ amount
130
+ currencyCode
131
+ }
132
+ financialStatus
133
+ fulfillmentStatus
134
+ id
135
+ lineItems(first: 10) {
136
+ nodes {
137
+ title
138
+ variant {
139
+ image {
140
+ url
141
+ altText
142
+ height
143
+ width
144
+ }
145
+ }
146
+ }
147
+ }
148
+ orderNumber
149
+ customerUrl
150
+ statusUrl
151
+ processedAt
152
+ }
153
+ ` as const;
154
+
155
+ export const CUSTOMER_FRAGMENT = `#graphql
156
+ fragment CustomerOrders on Customer {
157
+ numberOfOrders
158
+ orders(
159
+ sortKey: PROCESSED_AT,
160
+ reverse: true,
161
+ first: $first,
162
+ last: $last,
163
+ before: $startCursor,
164
+ after: $endCursor
165
+ ) {
166
+ nodes {
167
+ ...OrderItem
168
+ }
169
+ pageInfo {
170
+ hasPreviousPage
171
+ hasNextPage
172
+ hasNextPage
173
+ endCursor
174
+ }
175
+ }
176
+ }
177
+ ${ORDER_ITEM_FRAGMENT}
178
+ ` as const;
179
+
180
+ // NOTE: https://shopify.dev/docs/api/storefront/latest/queries/customer
181
+ const CUSTOMER_ORDERS_QUERY = `#graphql
182
+ ${CUSTOMER_FRAGMENT}
183
+ query CustomerOrders(
184
+ $country: CountryCode
185
+ $customerAccessToken: String!
186
+ $endCursor: String
187
+ $first: Int
188
+ $language: LanguageCode
189
+ $last: Int
190
+ $startCursor: String
191
+ ) @inContext(country: $country, language: $language) {
192
+ customer(customerAccessToken: $customerAccessToken) {
193
+ ...CustomerOrders
194
+ }
195
+ }
196
+ ` as const;