@akinon/pz-otp 1.15.0 → 1.16.1

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,9 @@
1
1
  # @akinon/pz-otp
2
2
 
3
+ ## 1.16.1
4
+
5
+ ## 1.16.0
6
+
3
7
  ## 1.15.0
4
8
 
5
9
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/pz-otp",
3
- "version": "1.15.0",
3
+ "version": "1.16.1",
4
4
  "license": "MIT",
5
5
  "main": "src/index.tsx",
6
6
  "peerDependencies": {
package/readme.md CHANGED
@@ -1,26 +1,26 @@
1
- # pz-otp
2
-
3
- ### Install the npm package
4
-
5
- ```bash
6
- # For latest version
7
- yarn add git+ssh://git@bitbucket.org:akinonteam/pz-otp.git
8
-
9
- # For specific version
10
- yarn add git+ssh://git@bitbucket.org:akinonteam/pz-otp.git#eX4mPl3
11
- ```
12
-
13
- ### Example Usage
14
- ##### File Path: src/views/register/index.tsx
15
-
16
- ```javascript
17
- import { Otp } from 'pz-otp';
18
-
19
- export default function Register() {
20
- return (
21
- // ...
22
- <Otp />
23
- //
24
- );
25
- }
1
+ # pz-otp
2
+
3
+ ### Install the npm package
4
+
5
+ ```bash
6
+ # For latest version
7
+ yarn add git+ssh://git@bitbucket.org:akinonteam/pz-otp.git
8
+
9
+ # For specific version
10
+ yarn add git+ssh://git@bitbucket.org:akinonteam/pz-otp.git#eX4mPl3
11
+ ```
12
+
13
+ ### Example Usage
14
+ ##### File Path: src/views/register/index.tsx
15
+
16
+ ```javascript
17
+ import { Otp } from 'pz-otp';
18
+
19
+ export default function Register() {
20
+ return (
21
+ // ...
22
+ <Otp />
23
+ //
24
+ );
25
+ }
26
26
  ```
package/src/views/Otp.tsx CHANGED
@@ -1,218 +1,218 @@
1
- import { FormEvent, ReactNode, useEffect, useState } from 'react';
2
- import { useRouter } from '@akinon/next/hooks';
3
- import { Button } from '@akinon/next/components/button';
4
- import OtpInput from 'react-otp-input';
5
- import { SubmitHandler } from 'react-hook-form';
6
- import { twMerge } from 'tailwind-merge';
7
-
8
- enum Component {
9
- Title = 'title',
10
- Description = 'description',
11
- SubmitButton = 'submitButton',
12
- ResendText = 'resendText',
13
- ResendButton = 'resendButton',
14
- Input = 'input'
15
- }
16
-
17
- enum ComponentWrapper {
18
- Title = 'title',
19
- Form = 'form',
20
- Resend = 'resend',
21
- Otp = 'otp'
22
- }
23
-
24
- type ComponentClassKey = (typeof Component)[keyof typeof Component];
25
- type ComponentWrapperClassKey =
26
- `${(typeof ComponentWrapper)[keyof typeof ComponentWrapper]}Wrapper`;
27
- type ComponentClasses = ComponentClassKey | ComponentWrapperClassKey;
28
-
29
- type OtpProps = {
30
- codeLength?: number;
31
- timer?: number;
32
- setShowPopup: any;
33
- submitAction: SubmitHandler<{
34
- [key: string]: any;
35
- }>;
36
- data: {
37
- [key: string]: any;
38
- };
39
- texts?: {
40
- [c in Component]?: string | ReactNode;
41
- };
42
- classes?: {
43
- [c in ComponentClasses]?: string;
44
- };
45
- };
46
-
47
- export const Otp = ({
48
- submitAction,
49
- data,
50
- codeLength = 6,
51
- timer = 60,
52
- setShowPopup,
53
- texts,
54
- classes
55
- }: OtpProps) => {
56
- const router = useRouter();
57
-
58
- const [otp, setOtp] = useState('');
59
- const [canResend, setCanResend] = useState(false);
60
- const [time, setTime] = useState(timer);
61
- const [hasError, setHasError] = useState(false);
62
-
63
- const resetTimer = () => {
64
- setTime(timer);
65
- setCanResend(false);
66
- };
67
-
68
- const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
69
- event.preventDefault();
70
-
71
- if (submitAction) {
72
- data.code = otp;
73
- const res = await submitAction(data);
74
-
75
- if (res.status !== 200) {
76
- setHasError(true);
77
- }
78
- }
79
- };
80
-
81
- const closeHandler = () => {
82
- if (setShowPopup) {
83
- setShowPopup(false);
84
- }
85
- };
86
-
87
- const resendHandler = async () => {
88
- if (!canResend) return;
89
-
90
- if (submitAction) {
91
- resetTimer();
92
- data.resend = true;
93
- await submitAction(data);
94
- }
95
- };
96
-
97
- const onTimerEnd = () => {
98
- setCanResend(true);
99
- };
100
-
101
- useEffect(() => {
102
- if (time > 0) {
103
- const timerId = setInterval(() => {
104
- setTime(time - 1);
105
- }, 1000);
106
-
107
- return () => clearInterval(timerId);
108
- } else {
109
- onTimerEnd();
110
- }
111
- }, [time, onTimerEnd]);
112
-
113
- useEffect(() => {
114
- document.body.style.overflow = 'hidden';
115
- window.scrollTo({ top: 0, behavior: 'smooth' });
116
-
117
- return () => {
118
- document.body.style.overflow = 'auto';
119
- };
120
- });
121
-
122
- return (
123
- <div className="fixed left-0 top-0 z-50 flex h-screen w-screen items-end md:items-center md:justify-center md:bg-black/10">
124
- <div className="h-[calc(100vh-48px)] w-screen flex md:h-auto md:max-w-lg flex-col items-center rounded-sm bg-white p-8 shadow-xl">
125
- <div className="w-full flex items-center justify-end">
126
- <div className="cursor-pointer" onClick={closeHandler}>
127
- <svg
128
- width="14"
129
- height="14"
130
- viewBox="0 0 14 14"
131
- xmlns="http://www.w3.org/2000/svg"
132
- >
133
- <g fill="#000" fill-rule="nonzero">
134
- <path d="M.684 14A.684.684 0 0 1 .2 12.833L12.833.2a.684.684 0 1 1 .967.967L1.167 13.8a.682.682 0 0 1-.483.2z" />
135
- <path d="M13.316 14a.682.682 0 0 1-.483-.2L.2 1.167A.684.684 0 0 1 1.167.2L13.8 12.833A.684.684 0 0 1 13.316 14z" />
136
- </g>
137
- </svg>
138
- </div>
139
- </div>
140
- <div
141
- className={twMerge(
142
- 'flex flex-col items-center px-14 mt-5',
143
- classes?.titleWrapper
144
- )}
145
- >
146
- <div className={twMerge('text-2xl font-medium', classes?.title)}>
147
- {texts?.title ?? 'OTP Verification'}
148
- </div>
149
- <div
150
- className={twMerge(
151
- 'mt-2.5 text-center text-gray-700',
152
- classes?.description
153
- )}
154
- >
155
- {texts?.description ??
156
- `Please enter the 4-digit sms code sent to your registered number and email address`}
157
- </div>
158
- </div>
159
- <form
160
- onSubmit={onSubmit}
161
- className={twMerge(
162
- 'flex flex-col items-center w-full',
163
- classes?.formWrapper
164
- )}
165
- >
166
- <OtpInput
167
- value={otp}
168
- onChange={(otp) => {
169
- setOtp(otp);
170
- setHasError(false);
171
- }}
172
- numInputs={codeLength}
173
- containerStyle={twMerge(
174
- 'mt-12 gap-2 flex-wrap',
175
- classes?.otpWrapper
176
- )}
177
- inputStyle={twMerge(
178
- 'h-12 w-8 md:h-16 md:w-12 rounded-md border border-gray-600 text-center text-lg',
179
- hasError && 'border-error',
180
- classes?.input
181
- )}
182
- renderInput={({ style, ...props }) => <input {...props} />}
183
- skipDefaultStyles={true}
184
- />
185
- <Button
186
- type="submit"
187
- className={twMerge(
188
- 'mt-5 h-auto w-full py-4 text-lg font-medium uppercase',
189
- classes?.submitButton
190
- )}
191
- >
192
- {texts?.submitButton ?? 'Verify'}
193
- </Button>
194
- </form>
195
- <div
196
- className={twMerge(
197
- 'mt-6 flex flex-col items-center',
198
- classes?.resendWrapper
199
- )}
200
- >
201
- <span className={twMerge('text-gray-700', classes?.resendText)}>
202
- {texts?.resendText ?? 'I didn`t receive a code'}
203
- </span>
204
- <div
205
- className={twMerge(
206
- ' font-medium underline cursor-pointer',
207
- !canResend && 'cursor-not-allowed text-gray-700',
208
- classes?.resendButton
209
- )}
210
- onClick={resendHandler}
211
- >
212
- {texts?.resendButton ?? 'RESEND'}
213
- </div>
214
- </div>
215
- </div>
216
- </div>
217
- );
218
- };
1
+ import { FormEvent, ReactNode, useEffect, useState } from 'react';
2
+ import { useRouter } from '@akinon/next/hooks';
3
+ import { Button } from '@akinon/next/components/button';
4
+ import OtpInput from 'react-otp-input';
5
+ import { SubmitHandler } from 'react-hook-form';
6
+ import { twMerge } from 'tailwind-merge';
7
+
8
+ enum Component {
9
+ Title = 'title',
10
+ Description = 'description',
11
+ SubmitButton = 'submitButton',
12
+ ResendText = 'resendText',
13
+ ResendButton = 'resendButton',
14
+ Input = 'input'
15
+ }
16
+
17
+ enum ComponentWrapper {
18
+ Title = 'title',
19
+ Form = 'form',
20
+ Resend = 'resend',
21
+ Otp = 'otp'
22
+ }
23
+
24
+ type ComponentClassKey = (typeof Component)[keyof typeof Component];
25
+ type ComponentWrapperClassKey =
26
+ `${(typeof ComponentWrapper)[keyof typeof ComponentWrapper]}Wrapper`;
27
+ type ComponentClasses = ComponentClassKey | ComponentWrapperClassKey;
28
+
29
+ type OtpProps = {
30
+ codeLength?: number;
31
+ timer?: number;
32
+ setShowPopup: any;
33
+ submitAction: SubmitHandler<{
34
+ [key: string]: any;
35
+ }>;
36
+ data: {
37
+ [key: string]: any;
38
+ };
39
+ texts?: {
40
+ [c in Component]?: string | ReactNode;
41
+ };
42
+ classes?: {
43
+ [c in ComponentClasses]?: string;
44
+ };
45
+ };
46
+
47
+ export const Otp = ({
48
+ submitAction,
49
+ data,
50
+ codeLength = 6,
51
+ timer = 60,
52
+ setShowPopup,
53
+ texts,
54
+ classes
55
+ }: OtpProps) => {
56
+ const router = useRouter();
57
+
58
+ const [otp, setOtp] = useState('');
59
+ const [canResend, setCanResend] = useState(false);
60
+ const [time, setTime] = useState(timer);
61
+ const [hasError, setHasError] = useState(false);
62
+
63
+ const resetTimer = () => {
64
+ setTime(timer);
65
+ setCanResend(false);
66
+ };
67
+
68
+ const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
69
+ event.preventDefault();
70
+
71
+ if (submitAction) {
72
+ data.code = otp;
73
+ const res = await submitAction(data);
74
+
75
+ if (res.status !== 200) {
76
+ setHasError(true);
77
+ }
78
+ }
79
+ };
80
+
81
+ const closeHandler = () => {
82
+ if (setShowPopup) {
83
+ setShowPopup(false);
84
+ }
85
+ };
86
+
87
+ const resendHandler = async () => {
88
+ if (!canResend) return;
89
+
90
+ if (submitAction) {
91
+ resetTimer();
92
+ data.resend = true;
93
+ await submitAction(data);
94
+ }
95
+ };
96
+
97
+ const onTimerEnd = () => {
98
+ setCanResend(true);
99
+ };
100
+
101
+ useEffect(() => {
102
+ if (time > 0) {
103
+ const timerId = setInterval(() => {
104
+ setTime(time - 1);
105
+ }, 1000);
106
+
107
+ return () => clearInterval(timerId);
108
+ } else {
109
+ onTimerEnd();
110
+ }
111
+ }, [time, onTimerEnd]);
112
+
113
+ useEffect(() => {
114
+ document.body.style.overflow = 'hidden';
115
+ window.scrollTo({ top: 0, behavior: 'smooth' });
116
+
117
+ return () => {
118
+ document.body.style.overflow = 'auto';
119
+ };
120
+ });
121
+
122
+ return (
123
+ <div className="fixed left-0 top-0 z-50 flex h-screen w-screen items-end md:items-center md:justify-center md:bg-black/10">
124
+ <div className="h-[calc(100vh-48px)] w-screen flex md:h-auto md:max-w-lg flex-col items-center rounded-sm bg-white p-8 shadow-xl">
125
+ <div className="w-full flex items-center justify-end">
126
+ <div className="cursor-pointer" onClick={closeHandler}>
127
+ <svg
128
+ width="14"
129
+ height="14"
130
+ viewBox="0 0 14 14"
131
+ xmlns="http://www.w3.org/2000/svg"
132
+ >
133
+ <g fill="#000" fill-rule="nonzero">
134
+ <path d="M.684 14A.684.684 0 0 1 .2 12.833L12.833.2a.684.684 0 1 1 .967.967L1.167 13.8a.682.682 0 0 1-.483.2z" />
135
+ <path d="M13.316 14a.682.682 0 0 1-.483-.2L.2 1.167A.684.684 0 0 1 1.167.2L13.8 12.833A.684.684 0 0 1 13.316 14z" />
136
+ </g>
137
+ </svg>
138
+ </div>
139
+ </div>
140
+ <div
141
+ className={twMerge(
142
+ 'flex flex-col items-center px-14 mt-5',
143
+ classes?.titleWrapper
144
+ )}
145
+ >
146
+ <div className={twMerge('text-2xl font-medium', classes?.title)}>
147
+ {texts?.title ?? 'OTP Verification'}
148
+ </div>
149
+ <div
150
+ className={twMerge(
151
+ 'mt-2.5 text-center text-gray-700',
152
+ classes?.description
153
+ )}
154
+ >
155
+ {texts?.description ??
156
+ `Please enter the 4-digit sms code sent to your registered number and email address`}
157
+ </div>
158
+ </div>
159
+ <form
160
+ onSubmit={onSubmit}
161
+ className={twMerge(
162
+ 'flex flex-col items-center w-full',
163
+ classes?.formWrapper
164
+ )}
165
+ >
166
+ <OtpInput
167
+ value={otp}
168
+ onChange={(otp) => {
169
+ setOtp(otp);
170
+ setHasError(false);
171
+ }}
172
+ numInputs={codeLength}
173
+ containerStyle={twMerge(
174
+ 'mt-12 gap-2 flex-wrap',
175
+ classes?.otpWrapper
176
+ )}
177
+ inputStyle={twMerge(
178
+ 'h-12 w-8 md:h-16 md:w-12 rounded-md border border-gray-600 text-center text-lg',
179
+ hasError && 'border-error',
180
+ classes?.input
181
+ )}
182
+ renderInput={({ style, ...props }) => <input {...props} />}
183
+ skipDefaultStyles={true}
184
+ />
185
+ <Button
186
+ type="submit"
187
+ className={twMerge(
188
+ 'mt-5 h-auto w-full py-4 text-lg font-medium uppercase',
189
+ classes?.submitButton
190
+ )}
191
+ >
192
+ {texts?.submitButton ?? 'Verify'}
193
+ </Button>
194
+ </form>
195
+ <div
196
+ className={twMerge(
197
+ 'mt-6 flex flex-col items-center',
198
+ classes?.resendWrapper
199
+ )}
200
+ >
201
+ <span className={twMerge('text-gray-700', classes?.resendText)}>
202
+ {texts?.resendText ?? 'I didn`t receive a code'}
203
+ </span>
204
+ <div
205
+ className={twMerge(
206
+ ' font-medium underline cursor-pointer',
207
+ !canResend && 'cursor-not-allowed text-gray-700',
208
+ classes?.resendButton
209
+ )}
210
+ onClick={resendHandler}
211
+ >
212
+ {texts?.resendButton ?? 'RESEND'}
213
+ </div>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ );
218
+ };