@akinon/projectzero 1.37.0-rc.6 → 1.37.0-rc.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @akinon/projectzero
2
2
 
3
+ ## 1.37.0-rc.7
4
+
3
5
  ## 1.37.0-rc.6
4
6
 
5
7
  ## 1.37.0-rc.5
@@ -1,5 +1,21 @@
1
1
  # projectzeronext
2
2
 
3
+ ## 1.37.0-rc.7
4
+
5
+ ### Minor Changes
6
+
7
+ - 7a4bb76: ZERO-2610:Refactor FilterItem component for better readability and efficiency
8
+
9
+ ### Patch Changes
10
+
11
+ - @akinon/next@1.37.0-rc.7
12
+ - @akinon/pz-b2b@1.37.0-rc.7
13
+ - @akinon/pz-gpay@1.37.0-rc.7
14
+ - @akinon/pz-masterpass@1.37.0-rc.7
15
+ - @akinon/pz-one-click-checkout@1.37.0-rc.7
16
+ - @akinon/pz-otp@1.37.0-rc.7
17
+ - @akinon/pz-pay-on-delivery@1.37.0-rc.7
18
+
3
19
  ## 1.37.0-rc.6
4
20
 
5
21
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectzeronext",
3
- "version": "1.37.0-rc.6",
3
+ "version": "1.37.0-rc.7",
4
4
  "private": true,
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -22,13 +22,13 @@
22
22
  "prestart": "pz-prestart"
23
23
  },
24
24
  "dependencies": {
25
- "@akinon/next": "1.37.0-rc.6",
26
- "@akinon/pz-b2b": "1.37.0-rc.6",
27
- "@akinon/pz-gpay": "1.37.0-rc.6",
28
- "@akinon/pz-masterpass": "1.37.0-rc.6",
29
- "@akinon/pz-one-click-checkout": "1.37.0-rc.6",
30
- "@akinon/pz-otp": "1.37.0-rc.6",
31
- "@akinon/pz-pay-on-delivery": "1.37.0-rc.6",
25
+ "@akinon/next": "1.37.0-rc.7",
26
+ "@akinon/pz-b2b": "1.37.0-rc.7",
27
+ "@akinon/pz-gpay": "1.37.0-rc.7",
28
+ "@akinon/pz-masterpass": "1.37.0-rc.7",
29
+ "@akinon/pz-one-click-checkout": "1.37.0-rc.7",
30
+ "@akinon/pz-otp": "1.37.0-rc.7",
31
+ "@akinon/pz-pay-on-delivery": "1.37.0-rc.7",
32
32
  "@hookform/resolvers": "2.9.0",
33
33
  "@next/third-parties": "14.1.0",
34
34
  "@react-google-maps/api": "2.17.1",
@@ -53,7 +53,7 @@
53
53
  "yup": "0.32.11"
54
54
  },
55
55
  "devDependencies": {
56
- "@akinon/eslint-plugin-projectzero": "1.37.0-rc.6",
56
+ "@akinon/eslint-plugin-projectzero": "1.37.0-rc.7",
57
57
  "@semantic-release/changelog": "6.0.2",
58
58
  "@semantic-release/exec": "6.0.3",
59
59
  "@semantic-release/git": "10.0.1",
@@ -0,0 +1,131 @@
1
+ import clsx from 'clsx';
2
+ import { useAppDispatch } from '@akinon/next/redux/hooks';
3
+ import { Facet, FacetChoice } from '@akinon/next/types';
4
+ import { Accordion, Radio, Checkbox } from '../../../components';
5
+ import { WIDGET_TYPE } from '../../../types';
6
+ import { SizeFilter } from './size-filter';
7
+ import { toggleFacet } from '@theme/redux/reducers/category';
8
+ import { commonProductAttributes } from '@theme/settings';
9
+ import { useRouter } from '@akinon/next/hooks';
10
+
11
+ const COMPONENT_TYPES = {
12
+ [WIDGET_TYPE.category]: Radio,
13
+ [WIDGET_TYPE.multiselect]: Checkbox
14
+ };
15
+
16
+ const sizeKey = commonProductAttributes.find(
17
+ (item) => item.translationKey === 'size'
18
+ ).key;
19
+
20
+ interface Props {
21
+ facet: Facet;
22
+ }
23
+
24
+ const sortByPredefinedOrder = (
25
+ aLabel: string,
26
+ bLabel: string,
27
+ order: string[]
28
+ ) => {
29
+ const aIndex = order.indexOf(aLabel);
30
+ const bIndex = order.indexOf(bLabel);
31
+
32
+ if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex;
33
+ if (aIndex !== -1) return -1;
34
+ if (bIndex !== -1) return 1;
35
+
36
+ return null;
37
+ };
38
+
39
+ const sortByNumericValue = (aLabel: string, bLabel: string) => {
40
+ const aNum = parseInt(aLabel, 10);
41
+ const bNum = parseInt(bLabel, 10);
42
+
43
+ if (!isNaN(aNum) && !isNaN(bNum)) return aNum - bNum;
44
+ if (!isNaN(aNum)) return -1;
45
+ if (!isNaN(bNum)) return 1;
46
+
47
+ return null;
48
+ };
49
+
50
+ const sortChoices = (
51
+ facetKey: string,
52
+ choices: FacetChoice[]
53
+ ): FacetChoice[] => {
54
+ if (facetKey === sizeKey) {
55
+ const order = ['xs', 's', 'm', 'l', 'xl'];
56
+
57
+ return choices.sort((a, b) => {
58
+ const aLabel = a.label.toLowerCase();
59
+ const bLabel = b.label.toLowerCase();
60
+
61
+ const orderComparison = sortByPredefinedOrder(aLabel, bLabel, order);
62
+ if (orderComparison !== null) return orderComparison;
63
+
64
+ const numericComparison = sortByNumericValue(aLabel, bLabel);
65
+ if (numericComparison !== null) return numericComparison;
66
+
67
+ return aLabel.localeCompare(bLabel);
68
+ });
69
+ }
70
+
71
+ return choices;
72
+ };
73
+
74
+ const getComponentByWidgetType = (widgetType: string, facetKey: string) => {
75
+ if (facetKey === sizeKey) {
76
+ return SizeFilter;
77
+ }
78
+ return COMPONENT_TYPES[widgetType] || COMPONENT_TYPES[WIDGET_TYPE.category];
79
+ };
80
+
81
+ export const FilterItem = ({ facet }: Props) => {
82
+ const dispatch = useAppDispatch();
83
+ const router = useRouter();
84
+
85
+ const handleSelectFilter = (choice: FacetChoice) => {
86
+ if (facet.key === 'category_ids') {
87
+ router.push(choice.url);
88
+ } else {
89
+ dispatch(toggleFacet({ facet, choice }));
90
+ }
91
+ };
92
+
93
+ const Component = getComponentByWidgetType(facet.widget_type, facet.key);
94
+ const choices = sortChoices(facet.key, [...facet.data.choices]);
95
+
96
+ return (
97
+ <Accordion
98
+ key={facet.key}
99
+ title={facet.name}
100
+ isCollapse={choices.some((choice) => choice.is_selected)}
101
+ dataTestId={`filter-${facet.name}`}
102
+ >
103
+ <div
104
+ className={clsx('flex gap-4', {
105
+ 'flex-wrap flex-row': facet.key === sizeKey,
106
+ 'flex-col': facet.key !== sizeKey
107
+ })}
108
+ >
109
+ {choices.map((choice, index) => (
110
+ <Component
111
+ key={choice.label}
112
+ data={choice}
113
+ name={facet.key}
114
+ onChange={() => facet.key !== sizeKey && handleSelectFilter(choice)}
115
+ onClick={() => facet.key === sizeKey && handleSelectFilter(choice)}
116
+ checked={choice.is_selected}
117
+ data-testid={`${choice.label.trim()}`}
118
+ >
119
+ {choice.label} (
120
+ <span
121
+ data-testid={`filter-count-${facet.name.toLowerCase()}-${index}`}
122
+ >
123
+ {choice.quantity}
124
+ </span>
125
+ )
126
+ </Component>
127
+ ))}
128
+ </div>
129
+ </Accordion>
130
+ );
131
+ };
@@ -1,26 +1,14 @@
1
1
  'use client';
2
2
 
3
- import { WIDGET_TYPE } from '@theme/types';
4
3
  import clsx from 'clsx';
5
4
 
6
- import { Accordion, Button, Checkbox, Icon, Radio } from '@theme/components';
7
- import { SizeFilter } from './size-filter';
8
-
9
- import { useLocalization, useRouter } from '@akinon/next/hooks';
10
- import { Facet, FacetChoice } from '@akinon/next/types';
5
+ import { Button, Icon } from '@theme/components';
6
+ import { useLocalization } from '@akinon/next/hooks';
11
7
  import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
12
- import {
13
- resetSelectedFacets,
14
- toggleFacet
15
- } from '@theme/redux/reducers/category';
8
+ import { resetSelectedFacets } from '@theme/redux/reducers/category';
16
9
  import CategoryActiveFilters from '@theme/views/category/category-active-filters';
17
10
  import { useMemo } from 'react';
18
- import { commonProductAttributes } from '@theme/settings';
19
-
20
- const COMPONENT_TYPES = {
21
- [WIDGET_TYPE.category]: Radio,
22
- [WIDGET_TYPE.multiselect]: Checkbox
23
- };
11
+ import { FilterItem } from './filter-item';
24
12
 
25
13
  interface Props {
26
14
  isMenuOpen: boolean;
@@ -28,31 +16,11 @@ interface Props {
28
16
  }
29
17
 
30
18
  export const Filters = (props: Props) => {
31
- const router = useRouter();
32
19
  const facets = useAppSelector((state) => state.category.facets);
33
20
  const dispatch = useAppDispatch();
34
21
  const { t } = useLocalization();
35
22
  const { isMenuOpen, setIsMenuOpen } = props;
36
23
 
37
- const handleSelectFilter = ({
38
- facet,
39
- choice
40
- }: {
41
- facet: Facet;
42
- choice: FacetChoice;
43
- }) => {
44
- if (facet.key === 'category_ids') {
45
- router.push(choice.url);
46
- } else {
47
- dispatch(
48
- toggleFacet({
49
- facet,
50
- choice
51
- })
52
- );
53
- }
54
- };
55
-
56
24
  const haveFilter = useMemo(() => {
57
25
  return (
58
26
  facets.filter(
@@ -66,10 +34,6 @@ export const Filters = (props: Props) => {
66
34
  dispatch(resetSelectedFacets());
67
35
  };
68
36
 
69
- const sizeKey = commonProductAttributes.find(
70
- (item) => item.translationKey === 'size'
71
- ).key;
72
-
73
37
  return (
74
38
  <div
75
39
  className={clsx(
@@ -88,71 +52,7 @@ export const Filters = (props: Props) => {
88
52
  <span>{t('category.filters.ready_to_wear')}</span>
89
53
  </div>
90
54
  {facets.map((facet) => {
91
- let Component = null;
92
- const choices = [...facet.data.choices];
93
-
94
- if (facet.key === sizeKey) {
95
- // If it's a size facet, use the custom size filter component
96
- Component = SizeFilter;
97
-
98
- const order = ['xs', 's', 'm', 'l', 'xl'];
99
- choices.sort((a, b) => {
100
- return (
101
- order.indexOf(a.label.toLowerCase()) -
102
- order.indexOf(b.label.toLowerCase())
103
- );
104
- });
105
- } else {
106
- Component =
107
- COMPONENT_TYPES[facet.widget_type] ||
108
- COMPONENT_TYPES[WIDGET_TYPE.category];
109
- }
110
-
111
- return (
112
- <Accordion
113
- key={facet.key}
114
- title={facet.name}
115
- isCollapse={choices.some((choice) => choice.is_selected)}
116
- dataTestId={`filter-${facet.name}`}
117
- >
118
- <div
119
- className={clsx(
120
- 'flex gap-4 flex-wrap',
121
- facet.key === sizeKey ? 'flex-row' : 'flex-col' // TODO: This condition must be refactor to a better way
122
- )}
123
- >
124
- {choices.map((choice, index) => (
125
- <Component // TODO: This dynamic component can be a hook or higher order component so it props can be standardized
126
- key={choice.label}
127
- data={choice}
128
- name={facet.key}
129
- onChange={() => {
130
- if (facet.key !== sizeKey) {
131
- // TODO: This condition must be refactor to a better way
132
- handleSelectFilter({ facet, choice });
133
- }
134
- }}
135
- onClick={() => {
136
- if (facet.key === sizeKey) {
137
- // TODO: This condition must be refactor to a better way
138
- handleSelectFilter({ facet, choice });
139
- }
140
- }}
141
- checked={choice.is_selected}
142
- data-testid={`${choice.label.trim()}`}
143
- >
144
- {choice.label} (
145
- <span
146
- data-testid={`filter-count-${facet.name.toLowerCase()}-${index}`}
147
- >
148
- {choice.quantity}
149
- </span>
150
- )
151
- </Component>
152
- ))}
153
- </div>
154
- </Accordion>
155
- );
55
+ return <FilterItem key={facet.key} facet={facet} />;
156
56
  })}
157
57
  <div className="lg:hidden">
158
58
  <CategoryActiveFilters />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/projectzero",
3
- "version": "1.37.0-rc.6",
3
+ "version": "1.37.0-rc.7",
4
4
  "private": false,
5
5
  "description": "CLI tool to manage your Project Zero Next project",
6
6
  "bin": {