@akinon/pz-basket-gift-pack 1.0.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.
- package/.gitattributes +15 -0
- package/.prettierrc +13 -0
- package/README.md +35 -0
- package/package.json +18 -0
- package/src/endpoints.ts +47 -0
- package/src/index.tsx +283 -0
package/.gitattributes
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
*.js text eol=lf
|
|
2
|
+
*.jsx text eol=lf
|
|
3
|
+
*.ts text eol=lf
|
|
4
|
+
*.tsx text eol=lf
|
|
5
|
+
*.json text eol=lf
|
|
6
|
+
*.md text eol=lf
|
|
7
|
+
|
|
8
|
+
.eslintignore text eol=lf
|
|
9
|
+
.eslintrc text eol=lf
|
|
10
|
+
.gitignore text eol=lf
|
|
11
|
+
.prettierrc text eol=lf
|
|
12
|
+
.yarnrc text eol=lf
|
|
13
|
+
|
|
14
|
+
* text=auto
|
|
15
|
+
|
package/.prettierrc
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bracketSameLine": false,
|
|
3
|
+
"tabWidth": 2,
|
|
4
|
+
"singleQuote": true,
|
|
5
|
+
"jsxSingleQuote": false,
|
|
6
|
+
"bracketSpacing": true,
|
|
7
|
+
"semi": true,
|
|
8
|
+
"useTabs": false,
|
|
9
|
+
"arrowParens": "always",
|
|
10
|
+
"endOfLine": "lf",
|
|
11
|
+
"proseWrap": "never",
|
|
12
|
+
"trailingComma": "none"
|
|
13
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# pz-basket-gift-pack
|
|
2
|
+
|
|
3
|
+
### Install the npm package
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# For latest version
|
|
7
|
+
yarn add git+ssh://git@bitbucket.org:akinonteam/pz-basket-gift-pack.git
|
|
8
|
+
|
|
9
|
+
# For specific version
|
|
10
|
+
yarn add git+ssh://git@bitbucket.org:akinonteam/pz-basket-gift-pack.git#0e7cde5
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Next Config Configuration
|
|
14
|
+
|
|
15
|
+
##### apps/web/next.config.js**
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const withTM = require("next-transpile-modules")(["pz-basket-gift-pack"]);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Example Usage
|
|
22
|
+
##### File Path: src/views/basket/basket-item.tsx
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import { BasketGiftPackage } from 'pz-basket-gift-pack';
|
|
27
|
+
|
|
28
|
+
<BasketGiftPackage basketItem={basketItem}/>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Props
|
|
32
|
+
|
|
33
|
+
| Properties | Type | Description |
|
|
34
|
+
| ---------- | -------- | -------------------- |
|
|
35
|
+
| basketItem | `object` | Product information. |
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@akinon/pz-basket-gift-pack",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "src/index.tsx",
|
|
6
|
+
"peerDependencies": {
|
|
7
|
+
"react": "^18.0.0",
|
|
8
|
+
"react-dom": "^18.0.0"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/node": "^18.7.8",
|
|
12
|
+
"@types/react": "^18.0.17",
|
|
13
|
+
"@types/react-dom": "^18.0.6",
|
|
14
|
+
"react": "^18.2.0",
|
|
15
|
+
"react-dom": "^18.2.0",
|
|
16
|
+
"typescript": "^4.7.4"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/endpoints.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Basket } from "@akinon/next/types";
|
|
2
|
+
import { buildClientRequestUrl } from "@akinon/next/utils";
|
|
3
|
+
import { api } from "@akinon/next/data/client/api";
|
|
4
|
+
|
|
5
|
+
export const basketApi = api.injectEndpoints({
|
|
6
|
+
endpoints: (build) => ({
|
|
7
|
+
addGiftPackage: build.mutation<Basket, { message: string; id: number }>({
|
|
8
|
+
query: ({ id, message }) => ({
|
|
9
|
+
url: buildClientRequestUrl("/baskets/basket_items", {
|
|
10
|
+
useTrailingSlash: false,
|
|
11
|
+
contentType: "application/json",
|
|
12
|
+
}),
|
|
13
|
+
method: "PATCH",
|
|
14
|
+
body: {
|
|
15
|
+
attributes: {
|
|
16
|
+
gift_note: message,
|
|
17
|
+
},
|
|
18
|
+
id,
|
|
19
|
+
is_all: true,
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
transformResponse: (response: { basket: Basket }) => response.basket,
|
|
23
|
+
invalidatesTags: ["Basket"],
|
|
24
|
+
}),
|
|
25
|
+
removeGiftPackage: build.mutation<Basket, { id: number }>({
|
|
26
|
+
query: ({ id }) => ({
|
|
27
|
+
url: buildClientRequestUrl("/baskets/basket_items", {
|
|
28
|
+
contentType: "application/json",
|
|
29
|
+
}),
|
|
30
|
+
method: "PATCH",
|
|
31
|
+
body: {
|
|
32
|
+
attributes: {
|
|
33
|
+
gift_note: "",
|
|
34
|
+
},
|
|
35
|
+
id,
|
|
36
|
+
is_all: true,
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
transformResponse: (response: { basket: Basket }) => response.basket,
|
|
40
|
+
invalidatesTags: ["Basket"],
|
|
41
|
+
}),
|
|
42
|
+
}),
|
|
43
|
+
overrideExisting: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export const { useAddGiftPackageMutation, useRemoveGiftPackageMutation } =
|
|
47
|
+
basketApi;
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useAddGiftPackageMutation,
|
|
3
|
+
useRemoveGiftPackageMutation
|
|
4
|
+
} from './endpoints';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
import { twMerge } from 'tailwind-merge';
|
|
7
|
+
import { useForm, SubmitHandler } from 'react-hook-form';
|
|
8
|
+
import { yupResolver } from '@hookform/resolvers/yup';
|
|
9
|
+
import * as yup from 'yup';
|
|
10
|
+
import { useEffect, useState } from 'react';
|
|
11
|
+
import { Button } from '@akinon/next/components/button';
|
|
12
|
+
import { Icon } from '@akinon/next/components/icon';
|
|
13
|
+
import { BasketItem as BasketItemType } from '@akinon/next/types';
|
|
14
|
+
|
|
15
|
+
interface GiftPackForm {
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
basketItem: BasketItemType;
|
|
21
|
+
translations?: {
|
|
22
|
+
placeholderInput?: string;
|
|
23
|
+
warningMessage?: string;
|
|
24
|
+
addNote?: string;
|
|
25
|
+
addGiftNote?: string;
|
|
26
|
+
changeNote?: string;
|
|
27
|
+
giftPackAdded?: string;
|
|
28
|
+
addGiftPack?: string;
|
|
29
|
+
removeGiftPack?: string;
|
|
30
|
+
charactersLength?: string;
|
|
31
|
+
removeNote?: string;
|
|
32
|
+
save?: string;
|
|
33
|
+
close?: string;
|
|
34
|
+
};
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const BasketGiftPack = ({
|
|
39
|
+
basketItem,
|
|
40
|
+
translations,
|
|
41
|
+
className
|
|
42
|
+
}: Props) => {
|
|
43
|
+
const defaultTranslations = {
|
|
44
|
+
placeholderInput:
|
|
45
|
+
'You can leave this area empty. However it will be a gift package without note.',
|
|
46
|
+
warningMessage: 'Ensure this field has no more than 160 characters.',
|
|
47
|
+
addNote: 'Add Note',
|
|
48
|
+
addGiftNote: 'Add Gift Note',
|
|
49
|
+
changeNote: 'Change Note',
|
|
50
|
+
giftPackAdded: 'Gift Pack Added',
|
|
51
|
+
addGiftPack: 'Add Gift Pack',
|
|
52
|
+
removeGiftPack: 'Remove Gift Pack',
|
|
53
|
+
charactersLength: 'characters left',
|
|
54
|
+
removeNote: 'Remove Note',
|
|
55
|
+
save: 'SAVE',
|
|
56
|
+
close: 'Close'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const _translations = {
|
|
60
|
+
...defaultTranslations,
|
|
61
|
+
...translations
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const [addGiftPackage] = useAddGiftPackageMutation();
|
|
65
|
+
const [removeGiftPackage] = useRemoveGiftPackageMutation();
|
|
66
|
+
const [hasGiftPack, setHasGiftPack] = useState(false);
|
|
67
|
+
const [hasNote, setHasNote] = useState({
|
|
68
|
+
title: _translations.addNote,
|
|
69
|
+
state: false
|
|
70
|
+
});
|
|
71
|
+
const [textAreaCount, setTextAreaCount] = useState(0);
|
|
72
|
+
const [note, setNote] = useState('');
|
|
73
|
+
const defaultMessage = 'NO-GIFT-NOTE';
|
|
74
|
+
|
|
75
|
+
const giftPackFormSchema = () =>
|
|
76
|
+
yup.object().shape({
|
|
77
|
+
message: yup.string().max(160, _translations.warningMessage)
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const {
|
|
81
|
+
register,
|
|
82
|
+
watch,
|
|
83
|
+
handleSubmit,
|
|
84
|
+
formState: { errors }
|
|
85
|
+
} = useForm<GiftPackForm>({
|
|
86
|
+
resolver: yupResolver(giftPackFormSchema())
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const onSubmit: SubmitHandler<GiftPackForm> = async (data) => {
|
|
90
|
+
addGiftPackage({ id: basketItem.id, message: data.message })
|
|
91
|
+
.unwrap()
|
|
92
|
+
.then(() => {
|
|
93
|
+
setHasNote((prevstate) => ({ ...prevstate, state: false }));
|
|
94
|
+
changeNoteTitle(data.message);
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const addGiftPack = () => {
|
|
99
|
+
addGiftPackage({ id: basketItem.id, message: defaultMessage })
|
|
100
|
+
.unwrap()
|
|
101
|
+
.then(() => {
|
|
102
|
+
setHasGiftPack(true);
|
|
103
|
+
setHasNote((prevstate) => ({ ...prevstate, state: false }));
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const removeGiftPack = () => {
|
|
108
|
+
removeGiftPackage({ id: basketItem.id });
|
|
109
|
+
setHasGiftPack(false);
|
|
110
|
+
setHasNote((prevstate) => ({ ...prevstate, state: false }));
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const removeNote = () => {
|
|
114
|
+
addGiftPackage({ id: basketItem.id, message: defaultMessage });
|
|
115
|
+
setHasNote((prevstate) => ({ ...prevstate, state: false }));
|
|
116
|
+
changeNoteTitle(defaultMessage);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const changeNoteTitle = (value) => {
|
|
120
|
+
if (value !== defaultMessage) {
|
|
121
|
+
setHasNote((prevstate) => ({
|
|
122
|
+
...prevstate,
|
|
123
|
+
title: _translations.changeNote
|
|
124
|
+
}));
|
|
125
|
+
} else {
|
|
126
|
+
setHasNote((prevstate) => ({
|
|
127
|
+
...prevstate,
|
|
128
|
+
title: _translations.addNote
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
for (const [key, value] of Object.entries(basketItem.attributes)) {
|
|
135
|
+
if (value === '') {
|
|
136
|
+
setHasGiftPack(false);
|
|
137
|
+
setHasNote((prevstate) => ({ ...prevstate, state: false }));
|
|
138
|
+
setNote('');
|
|
139
|
+
setTextAreaCount(0);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (key) {
|
|
144
|
+
changeNoteTitle(value);
|
|
145
|
+
setNote(value);
|
|
146
|
+
setHasGiftPack(true);
|
|
147
|
+
setTextAreaCount(value?.length);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}, [basketItem.attributes]);
|
|
151
|
+
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
const subscription = watch(({ message }) => {
|
|
154
|
+
setNote(message);
|
|
155
|
+
setTextAreaCount(message.length);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return () => subscription.unsubscribe();
|
|
159
|
+
}, [watch]);
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<div
|
|
163
|
+
className={twMerge(
|
|
164
|
+
clsx(
|
|
165
|
+
'mt-4 sm:mt-0',
|
|
166
|
+
{
|
|
167
|
+
'sm:mt-8': hasNote.state
|
|
168
|
+
},
|
|
169
|
+
className
|
|
170
|
+
)
|
|
171
|
+
)}
|
|
172
|
+
>
|
|
173
|
+
<div className="flex gap-4">
|
|
174
|
+
<div className="flex items-center gap-2">
|
|
175
|
+
<Icon
|
|
176
|
+
name="giftbox"
|
|
177
|
+
size={15}
|
|
178
|
+
className={clsx(hasGiftPack ? 'text-[#e85150]' : 'text-[#000000]')}
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
{hasGiftPack ? (
|
|
182
|
+
<span className="text-[0.75rem]">
|
|
183
|
+
{_translations.giftPackAdded}
|
|
184
|
+
</span>
|
|
185
|
+
) : (
|
|
186
|
+
<Button
|
|
187
|
+
appearance="ghost"
|
|
188
|
+
className="text-[#000000] cursor-pointer underline border-0 px-0 py-0 text-[0.75rem] h-auto hover:bg-transparent hover:text-[#000000]"
|
|
189
|
+
onClick={addGiftPack}
|
|
190
|
+
>
|
|
191
|
+
{_translations.addGiftPack}
|
|
192
|
+
</Button>
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
<Button
|
|
196
|
+
appearance="ghost"
|
|
197
|
+
className={clsx(
|
|
198
|
+
'text-[#000000] underline cursor-pointer border-0 px-0 py-0 text-[0.75rem] h-auto hover:bg-transparent hover:text-[#000000]',
|
|
199
|
+
{
|
|
200
|
+
hidden: !hasGiftPack
|
|
201
|
+
}
|
|
202
|
+
)}
|
|
203
|
+
onClick={() =>
|
|
204
|
+
setHasNote((prevstate) => ({ ...prevstate, state: true }))
|
|
205
|
+
}
|
|
206
|
+
>
|
|
207
|
+
{hasNote.title}
|
|
208
|
+
</Button>
|
|
209
|
+
|
|
210
|
+
<Button
|
|
211
|
+
appearance="ghost"
|
|
212
|
+
className={clsx(
|
|
213
|
+
'text-[#000000] cursor-pointer border-0 px-0 py-0 text-[0.75rem] underline h-auto hover:bg-transparent hover:text-[#000000]',
|
|
214
|
+
{
|
|
215
|
+
hidden: !hasGiftPack
|
|
216
|
+
}
|
|
217
|
+
)}
|
|
218
|
+
onClick={removeGiftPack}
|
|
219
|
+
>
|
|
220
|
+
{_translations.removeGiftPack}
|
|
221
|
+
</Button>
|
|
222
|
+
</div>
|
|
223
|
+
<div
|
|
224
|
+
className={clsx('mt-4', {
|
|
225
|
+
hidden: !hasNote.state
|
|
226
|
+
})}
|
|
227
|
+
>
|
|
228
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
229
|
+
<div className="flex justify-between mb-3">
|
|
230
|
+
<span className="text-[0.75rem] font-bold">
|
|
231
|
+
{_translations.addGiftNote}
|
|
232
|
+
</span>
|
|
233
|
+
<Button
|
|
234
|
+
appearance="ghost"
|
|
235
|
+
className="text-[#000000] cursor-pointer border-0 px-0 py-0 text-[0.75rem] select-none underline h-auto hover:bg-transparent hover:text-[#000000]"
|
|
236
|
+
onClick={(e) => {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
setHasNote((prevstate) => ({ ...prevstate, state: false }));
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
{_translations.close}
|
|
242
|
+
</Button>
|
|
243
|
+
</div>
|
|
244
|
+
<textarea
|
|
245
|
+
className="w-full border border-solid border-[#4a4f53] p-4 placeholder:text-[0.75rem] placeholder:text-[#c8c9c8] outline-none text-[0.75rem]"
|
|
246
|
+
rows={7}
|
|
247
|
+
name="message"
|
|
248
|
+
value={note !== defaultMessage ? note : ' '}
|
|
249
|
+
placeholder={_translations.placeholderInput}
|
|
250
|
+
{...register('message')}
|
|
251
|
+
/>
|
|
252
|
+
{errors.message && (
|
|
253
|
+
<span className="text-sm text-[#d72a04]">
|
|
254
|
+
{errors.message.message}
|
|
255
|
+
</span>
|
|
256
|
+
)}
|
|
257
|
+
<div className="flex justify-between items-center mt-2">
|
|
258
|
+
<span
|
|
259
|
+
className={clsx(
|
|
260
|
+
'text-[0.75rem] ',
|
|
261
|
+
textAreaCount > 160 ? 'text-[#e85150]' : 'text-[#82a27c]'
|
|
262
|
+
)}
|
|
263
|
+
>
|
|
264
|
+
{textAreaCount}/160 {_translations.charactersLength}
|
|
265
|
+
</span>
|
|
266
|
+
<div className="flex items-center gap-3">
|
|
267
|
+
<Button
|
|
268
|
+
appearance="ghost"
|
|
269
|
+
className="text-[#000000] cursor-pointer border-0 px-0 py-0 text-[0.75rem] select-none underline h-auto hover:bg-transparent hover:text-[#000000]"
|
|
270
|
+
onClick={removeNote}
|
|
271
|
+
>
|
|
272
|
+
{_translations.removeNote}
|
|
273
|
+
</Button>
|
|
274
|
+
<Button type="submit" className="w-28 h-7 font-medium uppercase">
|
|
275
|
+
{_translations.save}
|
|
276
|
+
</Button>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</form>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
);
|
|
283
|
+
};
|