@akinon/pz-checkout-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,29 @@
1
+ # pz-checkout-gift-package
2
+
3
+ ### Install the npm package
4
+
5
+ ```bash
6
+ # For latest version
7
+ yarn add ​git+ssh://git@bitbucket.org:akinonteam/pz-checkout-gift-package.git
8
+
9
+ # For specific version
10
+ yarn add ​git+ssh://git@bitbucket.org:akinonteam/pz-checkout-gift-package.git#445a9da
11
+ ```
12
+
13
+ ### Next Config Configuration
14
+
15
+ ##### apps/web/next.config.js**
16
+
17
+ ```javascript
18
+ const withTM = require("next-transpile-modules")(["pz-checkout-gift-package"]);
19
+ ```
20
+
21
+ ### Example Usage
22
+ ##### File Path: src/views/checkout/summary.tsx
23
+
24
+
25
+ ```javascript
26
+ import { CheckoutGiftPackage } from 'pz-checkout-gift-package';
27
+
28
+ <CheckoutGiftPackage />
29
+ ```
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@akinon/pz-checkout-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,39 @@
1
+ import { CheckoutContext, PreOrder } from '@akinon/next/types';
2
+ import { buildClientRequestUrl } from '@akinon/next/utils';
3
+ import { api } from '@akinon/next/data/client/api';
4
+
5
+ interface CheckoutResponse {
6
+ pre_order?: PreOrder;
7
+ errors: {
8
+ non_field_errors: string;
9
+ };
10
+ context_list?: CheckoutContext[];
11
+ template_name?: string;
12
+ redirect_url?: string;
13
+ }
14
+
15
+ export const checkoutApi = api.injectEndpoints({
16
+ endpoints: (build) => ({
17
+ addGiftPack: build.mutation<CheckoutResponse, { note: string }>({
18
+ query: ({ note }) => ({
19
+ url: buildClientRequestUrl('/orders/checkout?page=GiftBoxPage', {
20
+ useFormData: true
21
+ }),
22
+ method: 'POST',
23
+ body: {
24
+ note
25
+ }
26
+ })
27
+ }),
28
+ removeGiftPack: build.mutation<CheckoutResponse, void>({
29
+ query: () => ({
30
+ url: buildClientRequestUrl('/orders/checkout?page=GiftBoxIndexPage'),
31
+ method: 'POST'
32
+ })
33
+ })
34
+ }),
35
+ overrideExisting: false
36
+ });
37
+
38
+ export const { useAddGiftPackMutation, useRemoveGiftPackMutation } =
39
+ checkoutApi;
package/src/index.tsx ADDED
@@ -0,0 +1,219 @@
1
+ import { useAppSelector } from '@akinon/next/redux/hooks';
2
+ import { yupResolver } from '@hookform/resolvers/yup';
3
+ import clsx from 'clsx';
4
+ import { Accordion, Button, Icon } from '@akinon/next/components';
5
+ import { useEffect, useState } from 'react';
6
+ import { SubmitHandler, useForm } from 'react-hook-form';
7
+ import { RootState } from 'redux/store';
8
+ import * as yup from 'yup';
9
+ import { useAddGiftPackMutation, useRemoveGiftPackMutation } from './endpoints';
10
+
11
+ interface GiftPackForm {
12
+ message: string;
13
+ }
14
+
15
+ interface CheckoutGiftPackProps {
16
+ className?: string;
17
+ translations: {
18
+ addGiftPackText: string;
19
+ giftPackAdded: string;
20
+ removeGiftPackText: string;
21
+ informationText: string;
22
+ updateNote: string;
23
+ removeGiftNoteText: string;
24
+ charactersLength: string;
25
+ placeholderInput: string;
26
+ accordionTitle: string;
27
+ warningMessage: string;
28
+ save: string;
29
+ };
30
+ }
31
+
32
+ export const CheckoutGiftPack = ({
33
+ className,
34
+ translations
35
+ }: CheckoutGiftPackProps) => {
36
+ const [isNoteFormOpen, setIsNoteFormOpen] = useState(false);
37
+
38
+ const { preOrder, hasGiftBox } = useAppSelector(
39
+ (state: RootState) => state.checkout
40
+ );
41
+
42
+ const defaultTranslations = {
43
+ addGiftPackText: 'Add Gift Pack',
44
+ giftPackAdded: 'Gift Pack Added',
45
+ removeGiftPackText: 'Remove Gift Pack',
46
+ informationText: 'This order will be gift packaged*',
47
+ updateNote: 'Update Note',
48
+ removeGiftNoteText: 'Remove Gift Note',
49
+ charactersLength: 'characters left',
50
+ placeholderInput:
51
+ 'You can leave empty this area. However it will be a gift package without note.',
52
+ accordionTitle: 'Gift Note',
53
+ warningMessage: 'Ensure this field has no more than 160 characters.',
54
+ save: 'SAVE'
55
+ };
56
+
57
+ const _translations = {
58
+ ...defaultTranslations,
59
+ ...translations
60
+ };
61
+
62
+ const giftPackFormSchema = () =>
63
+ yup.object().shape({
64
+ message: yup.string().max(160, _translations.warningMessage)
65
+ });
66
+
67
+ if (!hasGiftBox) {
68
+ console.warn(
69
+ 'Gift box for checkout is disabled. Please enable it from Omnitron.'
70
+ );
71
+ return null;
72
+ }
73
+
74
+ const [addGiftPack] = useAddGiftPackMutation();
75
+ const [removeGiftPack] = useRemoveGiftPackMutation();
76
+ const DEFAULT_NOTE = 'NO-GIFT-MESSAGE';
77
+
78
+ const {
79
+ register,
80
+ watch,
81
+ handleSubmit,
82
+ resetField,
83
+ setValue,
84
+ formState: { errors }
85
+ } = useForm<GiftPackForm>({
86
+ resolver: yupResolver(giftPackFormSchema())
87
+ });
88
+
89
+ const subscribeNote = watch('message');
90
+ const noteLength = subscribeNote?.length ?? 0;
91
+ const giftBox = preOrder?.gift_box;
92
+
93
+ const onSubmit: SubmitHandler<GiftPackForm> = async (data) => {
94
+ await addGiftPack({
95
+ note: noteLength > 0 ? data.message : DEFAULT_NOTE
96
+ }).unwrap();
97
+ setIsNoteFormOpen(false);
98
+ };
99
+
100
+ const removeGiftNote = async () => {
101
+ await addGiftPack({ note: DEFAULT_NOTE }).unwrap();
102
+ resetField('message');
103
+ };
104
+
105
+ useEffect(() => {
106
+ if (giftBox?.note !== DEFAULT_NOTE) {
107
+ setValue('message', giftBox?.note);
108
+ }
109
+ }, [giftBox]);
110
+
111
+ return (
112
+ <div className={className}>
113
+ <div className="flex justify-between items-center py-2 px-5">
114
+ <div className="flex gap-2 items-center">
115
+ <Icon name="giftbox" size={16} className="fill-[#000000]" />
116
+ <span className="text-[1rem] font-light">
117
+ {giftBox ? (
118
+ _translations.giftPackAdded
119
+ ) : (
120
+ <Button
121
+ appearance="ghost"
122
+ className="cursor-pointer border-0 px-0 py-0 text-[1rem] font-light h-auto hover:bg-transparent hover:text-[#000000]"
123
+ onClick={() => addGiftPack({ note: DEFAULT_NOTE })}
124
+ >
125
+ {_translations.addGiftPackText}
126
+ </Button>
127
+ )}
128
+ </span>
129
+ </div>
130
+ <Button
131
+ appearance="ghost"
132
+ className={clsx(
133
+ 'text-[0.75rem] underline cursor-pointer border-0 px-0 py-0 h-auto hover:bg-transparent hover:text-[#e95151]',
134
+ {
135
+ hidden: !giftBox
136
+ }
137
+ )}
138
+ onClick={async () => {
139
+ await removeGiftPack().unwrap();
140
+ setIsNoteFormOpen(false);
141
+ }}
142
+ >
143
+ {_translations.removeGiftPackText}
144
+ </Button>
145
+ </div>
146
+ <div
147
+ className={clsx(
148
+ 'flex flex-col gap-2 bg-[#f7f7f7] text-[#58585a] text-[0.875rem] py-2 px-5',
149
+ {
150
+ hidden: !giftBox
151
+ }
152
+ )}
153
+ >
154
+ <div className="flex justify-between items-center">
155
+ <span>{_translations.informationText}</span>
156
+ <Icon name="giftbox" size={14} className="fill-[#58585a]" />
157
+ </div>
158
+ <div className="flex justify-between items-center gap-8">
159
+ <span>{giftBox?.note !== DEFAULT_NOTE ? giftBox?.note : ''}</span>
160
+ <Button
161
+ appearance="ghost"
162
+ className="underline cursor-pointer flex-shrink-0 border-0 px-0 py-0 h-auto hover:bg-transparent hover:text-[#000000]"
163
+ onClick={() => setIsNoteFormOpen(true)}
164
+ >
165
+ {_translations.updateNote}
166
+ </Button>
167
+ </div>
168
+ </div>
169
+ <form
170
+ className={clsx('py-2 px-5', {
171
+ hidden: !isNoteFormOpen
172
+ })}
173
+ onSubmit={handleSubmit(onSubmit)}
174
+ >
175
+ <Accordion
176
+ title={_translations.accordionTitle}
177
+ titleClassName="text-[0.75rem]"
178
+ isCollapse={true}
179
+ >
180
+ <textarea
181
+ className={clsx(
182
+ 'w-full border border-solid p-4 placeholder:text-[0.75rem] placeholder:text-[#9c9d9d] outline-none text-[0.75rem]',
183
+ noteLength > 160 ? 'border-[#e85150]' : 'border-[#7b9d75]'
184
+ )}
185
+ rows={4}
186
+ placeholder={_translations.placeholderInput}
187
+ {...register('message')}
188
+ />
189
+ {errors.message && (
190
+ <span className="text-sm text-[#d72a04] text-[0.875rem]">
191
+ {errors.message.message}
192
+ </span>
193
+ )}
194
+ <div className="text-[0.75rem]">
195
+ {noteLength} / 160 {_translations.charactersLength}
196
+ </div>
197
+ <div className="flex justify-end items-end gap-2">
198
+ {giftBox?.note !== DEFAULT_NOTE && (
199
+ <Button
200
+ appearance="ghost"
201
+ className="text-[0.75rem] underline cursor-pointer border-0 px-0 py-0 h-auto hover:bg-transparent hover:text-[#e95151]"
202
+ onClick={removeGiftNote}
203
+ >
204
+ {_translations.removeGiftNoteText}
205
+ </Button>
206
+ )}
207
+ <Button
208
+ type="submit"
209
+ className="w-[7rem] h-[1.75rem] font-[0.75rem] uppercase"
210
+ appearance="outlined"
211
+ >
212
+ {_translations.save}
213
+ </Button>
214
+ </div>
215
+ </Accordion>
216
+ </form>
217
+ </div>
218
+ );
219
+ };