@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 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
+ }
@@ -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
+ };