@akinon/projectzero 2.0.0-beta.1 → 2.0.0-beta.11

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 (107) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/app-template/.env.example +5 -0
  3. package/app-template/.gitignore +2 -0
  4. package/app-template/CHANGELOG.md +251 -0
  5. package/app-template/README.md +6 -0
  6. package/app-template/config/prebuild-tests.json +5 -0
  7. package/app-template/docs/plugins.md +60 -25
  8. package/app-template/jest.config.ts +2 -2
  9. package/app-template/{next.config.mjs → next.config.ts} +6 -3
  10. package/app-template/package.json +30 -27
  11. package/app-template/postcss.config.mjs +8 -0
  12. package/app-template/public/locales/en/account.json +4 -0
  13. package/app-template/public/locales/en/common.json +10 -0
  14. package/app-template/public/locales/tr/account.json +4 -0
  15. package/app-template/public/locales/tr/common.json +10 -0
  16. package/app-template/src/__tests__/middleware-matcher.test.ts +135 -0
  17. package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/cancellation/page.tsx +99 -7
  18. package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/page.tsx +112 -47
  19. package/app-template/src/app/[commerce]/[locale]/[currency]/account/page.tsx +1 -1
  20. package/app-template/src/app/[commerce]/[locale]/[currency]/address/stores/page.tsx +2 -2
  21. package/app-template/src/app/[commerce]/[locale]/[currency]/auth/page.tsx +1 -1
  22. package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +2 -2
  23. package/app-template/src/app/[commerce]/[locale]/[currency]/error.tsx +12 -15
  24. package/app-template/src/app/[commerce]/[locale]/[currency]/forms/[pk]/generate/page.tsx +1 -1
  25. package/app-template/src/app/[commerce]/[locale]/[currency]/{pz-not-found/page.tsx → not-found.tsx} +2 -2
  26. package/app-template/src/app/[commerce]/[locale]/[currency]/orders/checkout/page.tsx +7 -4
  27. package/app-template/src/app/[commerce]/[locale]/[currency]/xml-sitemap/[node]/route.ts +47 -1
  28. package/app-template/src/assets/globals.scss +162 -34
  29. package/app-template/src/components/__tests__/badge.test.tsx +2 -2
  30. package/app-template/src/components/accordion.tsx +1 -1
  31. package/app-template/src/components/button.tsx +50 -35
  32. package/app-template/src/components/checkbox.tsx +1 -0
  33. package/app-template/src/components/file-input.tsx +44 -2
  34. package/app-template/src/components/input.tsx +3 -3
  35. package/app-template/src/components/modal.tsx +1 -1
  36. package/app-template/src/components/select.tsx +2 -2
  37. package/app-template/src/components/shimmer.tsx +1 -1
  38. package/app-template/src/components/tabs.tsx +2 -2
  39. package/app-template/src/components/types/index.ts +4 -1
  40. package/app-template/src/middleware.ts +1 -0
  41. package/app-template/src/plugins.js +2 -1
  42. package/app-template/src/redux/middlewares/category.ts +1 -1
  43. package/app-template/src/redux/reducers/category.ts +1 -1
  44. package/app-template/src/redux/store.ts +4 -3
  45. package/app-template/src/utils/convert-facet-search-params.ts +1 -1
  46. package/app-template/src/views/account/contact-form.tsx +3 -8
  47. package/app-template/src/views/account/content-header.tsx +2 -3
  48. package/app-template/src/views/account/order.tsx +11 -9
  49. package/app-template/src/views/account/orders/order-cancellation-item.tsx +5 -4
  50. package/app-template/src/views/anonymous-tracking/order-detail/index.tsx +45 -38
  51. package/app-template/src/views/basket/basket-item.tsx +1 -0
  52. package/app-template/src/views/category/category-active-filters.tsx +1 -1
  53. package/app-template/src/views/category/category-header.tsx +12 -6
  54. package/app-template/src/views/category/category-info.tsx +4 -4
  55. package/app-template/src/views/category/filters/index.tsx +2 -2
  56. package/app-template/src/views/checkout/auth.tsx +1 -1
  57. package/app-template/src/views/checkout/layout/header.tsx +1 -1
  58. package/app-template/src/views/checkout/steps/payment/index.tsx +1 -1
  59. package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +1 -1
  60. package/app-template/src/views/checkout/steps/payment/options/redirection.tsx +5 -1
  61. package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +4 -4
  62. package/app-template/src/views/checkout/steps/shipping/address-box.tsx +3 -3
  63. package/app-template/src/views/checkout/steps/shipping/addresses.tsx +1 -1
  64. package/app-template/src/views/checkout/summary.tsx +2 -2
  65. package/app-template/src/views/header/action-menu.tsx +11 -4
  66. package/app-template/src/views/header/band.tsx +2 -2
  67. package/app-template/src/views/header/mini-basket.tsx +15 -4
  68. package/app-template/src/views/header/mobile-menu.tsx +6 -6
  69. package/app-template/src/views/header/navbar.tsx +1 -1
  70. package/app-template/src/views/header/pwa-back-button.tsx +1 -1
  71. package/app-template/src/views/header/search/index.tsx +16 -4
  72. package/app-template/src/views/header/search/results.tsx +1 -1
  73. package/app-template/src/views/header/user-menu.tsx +3 -1
  74. package/app-template/src/views/installment-options/index.tsx +1 -1
  75. package/app-template/src/views/login/index.tsx +30 -6
  76. package/app-template/src/views/otp-login/index.tsx +12 -14
  77. package/app-template/src/views/product/price-wrapper.tsx +7 -2
  78. package/app-template/src/views/product/product-info.tsx +35 -5
  79. package/app-template/src/views/product/slider.tsx +1 -1
  80. package/app-template/src/views/product-pointer-banner-item.tsx +1 -1
  81. package/app-template/src/views/register/index.tsx +29 -4
  82. package/app-template/src/views/sales-contract-modal/index.tsx +17 -17
  83. package/app-template/src/widgets/footer-info.tsx +1 -1
  84. package/app-template/src/widgets/footer-menu.tsx +1 -1
  85. package/app-template/src/widgets/footer-subscription/index.tsx +1 -1
  86. package/app-template/src/widgets/home-stories-eng.tsx +1 -1
  87. package/app-template/tailwind.config.js +2 -134
  88. package/codemods/sentry-9/index.js +30 -0
  89. package/codemods/sentry-9/remove-sentry-configs.js +14 -0
  90. package/codemods/sentry-9/remove-sentry-dependency.js +25 -0
  91. package/codemods/sentry-9/replace-error-page.js +32 -0
  92. package/codemods/update-tailwind-config/index.js +30 -0
  93. package/codemods/update-tailwind-config/transform.js +102 -0
  94. package/commands/codemod.ts +17 -0
  95. package/commands/index.ts +3 -1
  96. package/commands/plugins.ts +24 -30
  97. package/dist/codemods/sentry-9/templates/error.js +14 -0
  98. package/dist/commands/codemod.js +15 -0
  99. package/dist/commands/index.js +3 -1
  100. package/dist/commands/plugins.js +23 -20
  101. package/package.json +3 -2
  102. package/app-template/postcss.config.js +0 -6
  103. package/app-template/sentry.client.config.ts +0 -16
  104. package/app-template/sentry.edge.config.ts +0 -3
  105. package/app-template/sentry.properties +0 -4
  106. package/app-template/sentry.server.config.ts +0 -3
  107. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
@@ -4,7 +4,10 @@ import { UsePaginationType } from '@akinon/next/hooks/use-pagination';
4
4
 
5
5
  export interface ButtonProps
6
6
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
7
- appearance?: 'filled' | 'outlined' | 'ghost';
7
+ appearance?: 'filled' | 'outlined' | 'ghost' | 'link' | string;
8
+ size?: 'sm' | 'md' | 'lg' | 'xl';
9
+ href?: string;
10
+ target?: '_blank' | '_self' | '_parent' | '_top';
8
11
  }
9
12
 
10
13
  export interface PaginationProps {
@@ -9,6 +9,7 @@ import { NextMiddleware, NextResponse } from 'next/server';
9
9
  */
10
10
 
11
11
  export const config = {
12
+ // For .xml.gz type sitemap urls, update '/(.*sitemap\\.xml)' with this regex '/(.*sitemap\\.xml|sitemap/.*.xml(?:.gz)?)/',
12
13
  matcher: [
13
14
  '/((?!api|_next|[\\w-\\/*]+\\.\\w+).*)',
14
15
  '/(.*sitemap\\.xml)',
@@ -13,5 +13,6 @@ module.exports = [
13
13
  'pz-b2b',
14
14
  'pz-akifast',
15
15
  'pz-saved-card',
16
- 'pz-tabby-extension'
16
+ 'pz-tabby-extension',
17
+ 'pz-tamara-extension'
17
18
  ];
@@ -4,7 +4,7 @@ import { setSelectedFacets } from '@theme/redux/reducers/category';
4
4
 
5
5
  const getSelectedFacets = (facets: Array<Facet>) => {
6
6
  return facets
7
- .map((facet) => {
7
+ ?.map((facet) => {
8
8
  return {
9
9
  ...facet,
10
10
  data: {
@@ -32,7 +32,7 @@ const categorySlice = createSlice({
32
32
  toggleFacet(state, action) {
33
33
  const facets = JSON.parse(JSON.stringify(state.facets));
34
34
 
35
- state.selectedFacets = facets.map((facet) => {
35
+ state.selectedFacets = facets?.map((facet) => {
36
36
  if (facet.key === action.payload.facet.key) {
37
37
  facet.data.choices = facet.data.choices
38
38
  .map((choice) => {
@@ -6,6 +6,7 @@ import {
6
6
  Action,
7
7
  AnyAction,
8
8
  configureStore,
9
+ Middleware,
9
10
  Store,
10
11
  ThunkAction,
11
12
  ThunkDispatch
@@ -13,20 +14,20 @@ import {
13
14
  import categoryReducer from '@theme/redux/reducers/category';
14
15
  import categoryMiddleware from '@theme/redux/middlewares/category';
15
16
 
17
+ const _middlewares: Middleware[] = [...middlewares, categoryMiddleware];
18
+
16
19
  const _reducers = {
17
20
  ...reducers,
18
21
  category: categoryReducer
19
22
  };
20
23
 
21
- const _middlewares = [...middlewares, categoryMiddleware];
22
-
23
24
  export const makeStore = (): Store<{
24
25
  [key in keyof typeof _reducers]: ReturnType<typeof _reducers[key]>;
25
26
  }> =>
26
27
  configureStore({
27
28
  reducer: _reducers,
28
29
  middleware: (getDefaultMiddleware) =>
29
- getDefaultMiddleware().concat([..._middlewares])
30
+ getDefaultMiddleware().concat(_middlewares)
30
31
  });
31
32
 
32
33
  export type AppStore = ReturnType<typeof makeStore>;
@@ -1,7 +1,7 @@
1
1
  import { Facet } from '@akinon/next/types';
2
2
 
3
3
  const convertFacetSearchParams = (facets: Facet[]) => {
4
- const _facets: string[][] = facets.flatMap((facet) => {
4
+ const _facets: string[][] = facets?.flatMap((facet) => {
5
5
  return [
6
6
  ...facet.data.choices.map((choice) => {
7
7
  return [facet.search_key, choice.value as string];
@@ -242,7 +242,7 @@ const ContactForm = () => {
242
242
  <span className="text-secondary">*</span>
243
243
  </label>
244
244
  <textarea
245
- className="border-gray-500 border w-full text-xs p-2.5 focus-visible:outline-none focus:border-black hover:border-black"
245
+ className="border-gray-500 border w-full text-xs p-2.5 focus-visible:outline-hidden focus:border-black hover:border-black"
246
246
  rows={7}
247
247
  name="message"
248
248
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -255,13 +255,8 @@ const ContactForm = () => {
255
255
  <label className="text-xs text-gray-800 mb-2 block">
256
256
  {t('account.contact.form.file.title')}
257
257
  </label>
258
- <FileInput
259
- name="file"
260
- title="file"
261
- className="w-full mb-5"
262
- {...register('file')}
263
- />
264
- <Button type="submit" className="w-full font-medium">
258
+ <FileInput name="file" title="file" {...register('file')} />
259
+ <Button type="submit" className="w-full font-medium mt-4">
265
260
  {t('account.contact.form.submit_button')}
266
261
  </Button>
267
262
  </form>
@@ -31,15 +31,14 @@ export const ContentHeader = (props: Props) => {
31
31
  </h3>
32
32
  <Select
33
33
  onChange={handleChange}
34
- className="w-full mb-4 md:mb-0 md:w-56 md:mr-4 text-xs"
34
+ className="w-full mb-4 bg-white md:mb-0 md:w-56 md:mr-4 text-xs"
35
35
  options={orders}
36
36
  data-testid="account-orders-header-select"
37
37
  ></Select>
38
38
  <Button
39
39
  className={clsx(
40
40
  'w-full md:w-56',
41
- isButtonDisabled &&
42
- 'hover:bg-black hover:text-white disabled:opacity-75'
41
+ isButtonDisabled && 'hover:bg-black/75 hover:text-white'
43
42
  )}
44
43
  onClick={handleClick}
45
44
  data-testid="account-orders-header-button"
@@ -13,7 +13,7 @@ export const Order = (props) => {
13
13
  .reverse()
14
14
  .join(' ');
15
15
 
16
- const orderStatus = getOrderStatus(props.status.value.toString(), t);
16
+ const orderStatus = getOrderStatus(props?.status?.value?.toString(), t);
17
17
 
18
18
  return (
19
19
  <div className="border border-gray px-5 py-4 text-sm mb-4 last:mb-0">
@@ -58,30 +58,32 @@ export const Order = (props) => {
58
58
 
59
59
  <div className="w-full flex flex-col justify-between mt-6 mb-2 order-3 md:items-center md:flex-row md:order-none md:gap-20 lg:gap-40">
60
60
  <ul className="flex flex-wrap gap-3.5 mb-6 items-center md:mb-0">
61
- {props.orderitem_set.slice(0, 3).map((item, index) => (
61
+ {props?.orderitem_set?.slice(0, 3).map((item, index) => (
62
62
  // TODO: Static image will change (TR)
63
- <li className="flex-shrink-0" key={index}>
63
+ <li className="shrink-0" key={index}>
64
64
  <Image
65
- src={item.product.image ? item.product.image : '/noimage.jpg'}
66
- alt={item.product.name}
65
+ src={
66
+ item?.product?.image ? item.product.image : '/noimage.jpg'
67
+ }
68
+ alt={item?.product?.name}
67
69
  width={64}
68
70
  height={96}
69
71
  />
70
72
  </li>
71
73
  ))}
72
- {props.orderitem_set.length > 3 && (
73
- <div>+ {props.orderitem_set.length - 3}</div>
74
+ {props?.orderitem_set?.length > 3 && (
75
+ <div>+ {props?.orderitem_set?.length - 3}</div>
74
76
  )}
75
77
  </ul>
76
78
 
77
- <span className={orderStatus.className}>{orderStatus.label}</span>
79
+ <span className={orderStatus?.className}>{orderStatus?.label}</span>
78
80
  </div>
79
81
 
80
82
  <div
81
83
  className="order-2 flex items-end md:order-none"
82
84
  data-testid="account-orders-count"
83
85
  >
84
- {props.orderitem_set.length} {t('account.my_orders.order.items')}
86
+ {props?.orderitem_set?.length} {t('account.my_orders.order.items')}
85
87
  </div>
86
88
  </div>
87
89
  </div>
@@ -8,7 +8,8 @@ export const OrderCancellationItem = ({
8
8
  item,
9
9
  value,
10
10
  onChange,
11
- selectOption
11
+ selectOption,
12
+ fileInput
12
13
  }) => {
13
14
  const { t } = useLocalization();
14
15
  const checkboxStatus =
@@ -38,14 +39,13 @@ export const OrderCancellationItem = ({
38
39
  <div className="flex flex-wrap justify-between border-gray border-b mb-4 pb-3">
39
40
  <div className="flex gap-3 mb-5 lg:mb-0">
40
41
  {checkboxStatus && (
41
- // TODO: Static image will change (TR)
42
42
  <Checkbox
43
43
  className="m-auto"
44
44
  data-testid="account-orders-return-checkbox"
45
45
  onClick={handleClick}
46
46
  />
47
47
  )}
48
- <div className="flex-shrink-0">
48
+ <div className="shrink-0">
49
49
  <Link href={item.product.absolute_url}>
50
50
  <Image
51
51
  src={item.product.image ? item.product.image : '/noimage.jpg'}
@@ -80,7 +80,7 @@ export const OrderCancellationItem = ({
80
80
  </div>
81
81
  </div>
82
82
  </div>
83
- <div className="flex flex-wrap justify-between w-full items-start lg:items-center lg:w-auto">
83
+ <div className="flex flex-wrap justify-between w-full items-start lg:items-center lg:w-80 gap-4">
84
84
  <div className="w-full flex flex-col lg:items-center lg:flex-row">
85
85
  {item.active_cancellation_request?.easy_return?.code && (
86
86
  <div className="flex items-center">
@@ -93,6 +93,7 @@ export const OrderCancellationItem = ({
93
93
 
94
94
  {selectOption}
95
95
  </div>
96
+ <div>{fileInput}</div>
96
97
  </div>
97
98
  </div>
98
99
  );
@@ -22,14 +22,15 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
22
22
  const groupedOrder = [];
23
23
 
24
24
  if (order) {
25
- const groupedData = order.orderitem_set.reduce((groups, item) => {
26
- const { tracking_number } = item;
27
- if (!groups[tracking_number]) {
28
- groups[tracking_number] = [];
29
- }
30
- groups[tracking_number].push(item);
31
- return groups;
32
- }, {});
25
+ const groupedData =
26
+ order?.orderitem_set?.reduce((groups, item) => {
27
+ const { tracking_number } = item;
28
+ if (!groups[tracking_number]) {
29
+ groups[tracking_number] = [];
30
+ }
31
+ groups[tracking_number].push(item);
32
+ return groups;
33
+ }, {}) || {};
33
34
 
34
35
  const result = Object.values(groupedData);
35
36
 
@@ -67,8 +68,8 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
67
68
 
68
69
  <div>
69
70
  <span className="text-base font-bold">
70
- {order.orderitem_set.length}{' '}
71
- {t('account.my_orders.detail.products')} {groupedOrder.length}{' '}
71
+ {order?.orderitem_set?.length}{' '}
72
+ {t('account.my_orders.detail.products')} {groupedOrder?.length}{' '}
72
73
  {t('account.my_orders.detail.packages')}
73
74
  </span>
74
75
  </div>
@@ -78,17 +79,17 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
78
79
  className="break-words"
79
80
  >
80
81
  <span>{t('account.my_orders.detail.delivery_address')}</span>:{' '}
81
- {order?.shipping_address.line}{' '}
82
- {order?.shipping_address.district.name}{' '}
83
- {order?.shipping_address.township.name}{' '}
84
- {order?.shipping_address.city.name}
82
+ {order?.shipping_address?.line}{' '}
83
+ {order?.shipping_address?.district?.name}{' '}
84
+ {order?.shipping_address?.township?.name}{' '}
85
+ {order?.shipping_address?.city?.name}
85
86
  </div>
86
87
  </OrderDetailHeader>
87
88
 
88
89
  <div>
89
90
  {groupedOrder.map((group, i) => {
90
91
  const orderStatus = getOrderStatus(
91
- group[0].status.value.toString(),
92
+ group[0]?.status?.value?.toString(),
92
93
  t
93
94
  );
94
95
 
@@ -112,8 +113,8 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
112
113
  <div className="flex justify-between items-center lg:gap-x-12">
113
114
  <div className="text-base">{orderStatus.label}</div>
114
115
 
115
- {group[0].tracking_number && group[0].tracking_url && (
116
- <Link href={group[0].tracking_url}>
116
+ {group[0]?.tracking_number && group[0]?.tracking_url && (
117
+ <Link href={group[0]?.tracking_url}>
117
118
  <Button className="px-7" appearance="filled">
118
119
  {t('account.my_orders.detail.track_shipment')}
119
120
  </Button>
@@ -125,7 +126,7 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
125
126
  <div className="px-4 lg:px-7">
126
127
  {group.map((item, index) => {
127
128
  const itemStatus = getOrderStatus(
128
- item.status.value.toString(),
129
+ item?.status?.value?.toString(),
129
130
  t
130
131
  );
131
132
 
@@ -135,18 +136,18 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
135
136
  key={index}
136
137
  >
137
138
  <div className="flex gap-3 mb-5 lg:mb-0">
138
- <div className="flex-shrink-0">
139
+ <div className="shrink-0">
139
140
  <Link
140
141
  className="block"
141
- href={item.product.absolute_url}
142
+ href={item?.product?.absolute_url}
142
143
  >
143
144
  <Image
144
145
  src={
145
- item.product.image
146
+ item?.product?.image
146
147
  ? item.product.image
147
148
  : '/noimage.jpg'
148
149
  }
149
- alt={item.product.name}
150
+ alt={item?.product?.name}
150
151
  width={112}
151
152
  height={150}
152
153
  />
@@ -155,27 +156,32 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
155
156
 
156
157
  <div className="flex flex-col justify-between lg:max-w-48">
157
158
  <div className="text-sm">
158
- <Link href={item.product.absolute_url}>
159
- {item.product.name}
159
+ <Link href={item?.product?.absolute_url}>
160
+ {item?.product?.name}
160
161
  </Link>
161
162
  </div>
162
163
 
163
164
  <div className="text-gray-900 text-xs">
164
- {item.product.attributes.filterable_color && (
165
+ {item?.product?.attributes
166
+ ?.filterable_color && (
165
167
  <div>
166
168
  <span>
167
169
  {t('account.my_orders.detail.color')}
168
170
  </span>
169
- : {item.product.attributes.filterable_color}
171
+ :{' '}
172
+ {
173
+ item?.product?.attributes
174
+ ?.filterable_color
175
+ }
170
176
  </div>
171
177
  )}
172
178
 
173
- {item.product.attributes.size && (
179
+ {item?.product?.attributes?.size && (
174
180
  <div>
175
181
  <span>
176
182
  {t('account.my_orders.detail.size')}
177
183
  </span>
178
- :{item.product.attributes.size}
184
+ :{item?.product?.attributes?.size}
179
185
  </div>
180
186
  )}
181
187
 
@@ -183,7 +189,7 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
183
189
  <span>
184
190
  {t('account.my_orders.detail.code')}
185
191
  </span>
186
- : {item.product.base_code}
192
+ : {item?.product?.base_code}
187
193
  </div>
188
194
  </div>
189
195
  </div>
@@ -202,7 +208,7 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
202
208
 
203
209
  {(item.is_cancellable || item.is_refundable) &&
204
210
  order.is_cancellable &&
205
- item.status.value == '400' && (
211
+ item?.status?.value == '400' && (
206
212
  <div className="lg:ml-24">
207
213
  <Link
208
214
  href={`${ROUTES.ACCOUNT_ORDERS}/${order.id}/cancellation`}
@@ -256,29 +262,29 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
256
262
  <div className="flex justify-between text-sm text-black-700 mb-2">
257
263
  <p>
258
264
  <span>{t('account.my_orders.detail.subtotal')}</span> (
259
- {order?.orderitem_set.length}{' '}
265
+ {order?.orderitem_set?.length}{' '}
260
266
  <span>{t('account.my_orders.detail.items')}</span>)
261
267
  </p>
262
268
 
263
269
  <Price
264
270
  className="font-normal min-w-max"
265
271
  value={
266
- parseFloat(order.amount_without_discount) -
267
- parseFloat(order.shipping_amount)
272
+ parseFloat(order?.amount_without_discount || '0') -
273
+ parseFloat(order?.shipping_amount || '0')
268
274
  }
269
275
  />
270
276
  </div>
271
277
 
272
- {order.discountitem_set &&
273
- order.discountitem_set.map((item, index) => (
278
+ {order?.discountitem_set &&
279
+ order?.discountitem_set?.map((item, index) => (
274
280
  <div
275
281
  className="flex justify-between text-sm text-black-700 mb-2"
276
282
  key={index}
277
283
  >
278
- <p>{item.name}</p>
284
+ <p>{item?.name}</p>
279
285
  <Price
280
286
  className="font-normal min-w-max"
281
- value={item.amount}
287
+ value={item?.amount}
282
288
  useNegative
283
289
  />
284
290
  </div>
@@ -291,7 +297,8 @@ export const AnonymousTrackingOrderDetail = ({ order }) => {
291
297
  className="font-normal min-w-max"
292
298
  data-testid="account-orders-detail-total"
293
299
  value={
294
- parseFloat(order.amount) - parseFloat(order.shipping_amount)
300
+ parseFloat(order?.amount || '0') -
301
+ parseFloat(order?.shipping_amount || '0')
295
302
  }
296
303
  />
297
304
  </div>
@@ -87,6 +87,7 @@ export const BasketItem = (props: Props) => {
87
87
  <li
88
88
  key={basketItem.id}
89
89
  className="flex border-b border-gray-200 py-3 relative"
90
+ data-testid="basket-item"
90
91
  >
91
92
  <div className="w-20 lg:w-[105px] mr-4 shrink-0">
92
93
  <Link href={basketItem.product.absolute_url} passHref>
@@ -69,7 +69,7 @@ const CategoryActiveFilters = () => {
69
69
 
70
70
  return (
71
71
  <div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-2 mb-4">
72
- {facets.map((facet) =>
72
+ {facets?.map((facet) =>
73
73
  facet?.data?.choices
74
74
  ?.filter((choice) => choice.is_selected)
75
75
  ?.map(
@@ -91,12 +91,18 @@ export const CategoryHeader = (props: Props) => {
91
91
  value: String(value)
92
92
  });
93
93
  }}
94
- className={clsx(
95
- 'cursor-pointer',
96
- Number(layoutSize) === value ? 'fill-black' : 'fill-gray-500'
97
- )}
94
+ className="cursor-pointer"
98
95
  >
99
- <Icon key={value} name={icon} size={16} />
96
+ <Icon
97
+ key={value}
98
+ name={icon}
99
+ size={16}
100
+ className={
101
+ Number(layoutSize) === value
102
+ ? 'text-black'
103
+ : 'text-gray-500'
104
+ }
105
+ />
100
106
  </a>
101
107
  ))}
102
108
  </div>
@@ -115,7 +121,7 @@ export const CategoryHeader = (props: Props) => {
115
121
  </Button>
116
122
  <Select
117
123
  options={sortOptions}
118
- value={sortOptions.find(({ is_selected }) => is_selected).value}
124
+ value={sortOptions?.find(({ is_selected }) => is_selected)?.value}
119
125
  data-testid="list-sorter"
120
126
  onChange={(e) => {
121
127
  handleSelectFilter({
@@ -79,7 +79,7 @@ export default function ListPage(props: ListPageProps) {
79
79
  className={clsx(
80
80
  'transition-opacity duration-300 ease-linear lg:hidden',
81
81
  isMenuOpen
82
- ? 'fixed bg-black bg-opacity-60 inset-0 z-10 opacity-100'
82
+ ? 'fixed bg-black/60 inset-0 z-10 opacity-100'
83
83
  : 'opacity-0'
84
84
  )}
85
85
  ></div>
@@ -102,7 +102,7 @@ export default function ListPage(props: ListPageProps) {
102
102
  </div>
103
103
  )}
104
104
 
105
- {data.products.length === 0 && page > 1 && <LoaderSpinner />}
105
+ {data.products?.length === 0 && page > 1 && <LoaderSpinner />}
106
106
 
107
107
  <div
108
108
  className={clsx('grid gap-x-4 gap-y-12 grid-cols-2', {
@@ -111,7 +111,7 @@ export default function ListPage(props: ListPageProps) {
111
111
  'lg:grid-cols-3': layoutSize === 3
112
112
  })}
113
113
  >
114
- {data.products.map((product, index) => (
114
+ {data?.products?.map((product, index) => (
115
115
  <ProductItem
116
116
  key={product.pk}
117
117
  product={product}
@@ -121,7 +121,7 @@ export default function ListPage(props: ListPageProps) {
121
121
  />
122
122
  ))}
123
123
  </div>
124
- {data.products.length > 0 && (
124
+ {data?.products?.length > 0 && (
125
125
  <Pagination
126
126
  total={data.pagination.total_count}
127
127
  limit={data.pagination.page_size}
@@ -23,7 +23,7 @@ export const Filters = (props: Props) => {
23
23
  const [isPending, startTransition] = useTransition();
24
24
 
25
25
  const haveFilter = useMemo(() => {
26
- return facets.some((facet) =>
26
+ return facets?.some((facet) =>
27
27
  facet?.data?.choices?.some((choice) => choice.is_selected)
28
28
  );
29
29
  }, [facets]);
@@ -50,7 +50,7 @@ export const Filters = (props: Props) => {
50
50
  <span>{t('category.filters.ready_to_wear')}</span>
51
51
  </div>
52
52
 
53
- {facets.map((facet) => {
53
+ {facets?.map((facet) => {
54
54
  return (
55
55
  <FilterItem
56
56
  key={facet.key}
@@ -24,7 +24,7 @@ const CheckoutAuth = () => {
24
24
 
25
25
  return (
26
26
  <div className="flex flex-col w-full my-5 lg:flex-row">
27
- <div className="flex-1 flex-shrink-0">
27
+ <div className="flex-1 shrink-0">
28
28
  <Login />
29
29
  <div className="text-center text-sm text-gray-600 uppercase mt-5">
30
30
  <span>
@@ -6,7 +6,7 @@ import { Image } from '@akinon/next/components/image';
6
6
 
7
7
  const CheckoutHeader = () => {
8
8
  return (
9
- <div className="relative border-b border-gray-100 shadow">
9
+ <div className="relative border-b border-gray-100 shadow-sm">
10
10
  <header
11
11
  className={clsx(['py-8', 'px-4', 'mx-auto', 'container', 'md:px-0'])}
12
12
  >
@@ -18,7 +18,7 @@ const PaymentStep = () => {
18
18
  'pointer-events-none opacity-30': isPaymentStepBusy
19
19
  })}
20
20
  >
21
- <div className="w-full mt-4 flex justify-start border border-gray-400 -mb-px z-10 md:mt-0 md:border-r-0 md:border-b-0 md:w-auto order-2 md:order-none">
21
+ <div className="w-full mt-4 flex justify-start border border-gray-400 -mb-px z-10 md:mt-0 md:border-r-0 md:border-b-0 md:w-auto order-2 md:order-none overflow-x-auto">
22
22
  <PaymentOptionButtons />
23
23
  </div>
24
24
  <div className="w-full border border-solid border-gray-400 bg-white">
@@ -319,7 +319,7 @@ const CheckoutCreditCard = () => {
319
319
  size={16}
320
320
  className="leading-none ml-2"
321
321
  />
322
- <div className="hidden group-hover:block absolute right-0 bottom-5 w-[11rem] lg:w-[21rem] lg:left-auto lg:right-auto border-2">
322
+ <div className="hidden group-hover:block absolute right-0 bottom-5 w-[11rem] lg:w-[21rem] lg:left-auto lg:right-auto border-2 border-gray-200">
323
323
  {/* TODO: Fix this */}
324
324
  <Image
325
325
  src="/cvv.jpg"
@@ -65,6 +65,7 @@ export default function RedirectionPayment() {
65
65
 
66
66
  <Checkbox
67
67
  className="px-4 md:px-0"
68
+ data-testid="checkout-agreement"
68
69
  {...register('agreement')}
69
70
  error={errors.agreement}
70
71
  >
@@ -88,7 +89,10 @@ export default function RedirectionPayment() {
88
89
  </div>
89
90
  )}
90
91
 
91
- <Button className={twMerge('w-full md:w-36 px-4 md:px-0')}>
92
+ <Button
93
+ data-testid="checkout-submit-button"
94
+ className={twMerge('w-full md:w-36 px-4 md:px-0')}
95
+ >
92
96
  {payment_option.name}
93
97
  </Button>
94
98
  </form>
@@ -44,7 +44,7 @@ const PaymentOptionButtons = () => {
44
44
  {displayedPaymentOptions.map((option) => (
45
45
  <label
46
46
  key={`payment-option-${option.pk}`}
47
- className="border px-4 py-3 mt-3 flex h-12"
47
+ className="border border-gray-200 px-4 py-3 mt-3 flex h-12"
48
48
  onClick={scrollToTop}
49
49
  >
50
50
  <Radio
@@ -69,10 +69,10 @@ const PaymentOptionButtons = () => {
69
69
  onClick={() => onClickHandler(option)}
70
70
  className={clsx(
71
71
  'flex items-center justify-center border-r border-b border-solid',
72
- 'border-gray-400 text-xs uppercase text-black-800 font-medium',
73
- 'text-opacity-60 bg-white h-11 px-5 transition-colors sm:h-15 sm:px-8 sm:py-8 hover:text-secondary',
72
+ 'border-gray-400 text-xs uppercase text-black-800/60 font-medium',
73
+ 'bg-white h-11 px-5 transition-colors sm:h-15 sm:px-8 sm:py-8 hover:text-secondary',
74
74
  {
75
- 'text-opacity-100 border-b-transparent':
75
+ 'text-black-800/100 border-b-transparent':
76
76
  preOrder?.payment_option?.pk === option.pk
77
77
  }
78
78
  )}