@akinon/pz-otp 2.0.0-beta.2 → 2.0.0-beta.21

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,18 +1,138 @@
1
1
  # @akinon/pz-otp
2
2
 
3
- ## 2.0.0-beta.2
3
+ ## 2.0.0-beta.21
4
4
 
5
- ## 2.0.0-beta.1
5
+ ## 2.0.0-beta.20
6
+
7
+ ## 1.126.0
8
+
9
+ ## 1.125.2
10
+
11
+ ## 1.125.1
12
+
13
+ ## 1.125.0
14
+
15
+ ## 1.124.0
16
+
17
+ ## 1.123.0
18
+
19
+ ## 1.122.0
20
+
21
+ ## 1.121.0
22
+
23
+ ## 1.120.0
6
24
 
7
25
  ### Minor Changes
8
26
 
9
- - ZERO-3091: Upgrade Next.js to v15 and React to v19
27
+ - 6ad72e8d: ZERO-4032: Add loading state management for payment submissions across multiple components and add safe guarding
28
+
29
+ ## 1.119.0
30
+
31
+ ## 1.118.0
32
+
33
+ ## 1.117.0
34
+
35
+ ## 1.116.0
36
+
37
+ ## 1.115.0
38
+
39
+ ## 1.114.0
40
+
41
+ ## 1.113.0
42
+
43
+ ## 1.112.0
44
+
45
+ ## 1.111.0
46
+
47
+ ## 1.110.0
48
+
49
+ ## 1.109.0
50
+
51
+ ## 1.108.0
52
+
53
+ ## 1.107.0
54
+
55
+ ## 1.106.0
56
+
57
+ ### Minor Changes
58
+
59
+ - ae010f0: ZERO-3410: Update readme
60
+
61
+ ## 1.105.0
62
+
63
+ ## 1.104.0
64
+
65
+ ## 1.103.0
66
+
67
+ ## 1.102.0
68
+
69
+ ## 1.101.0
70
+
71
+ ## 1.100.0
72
+
73
+ ## 1.99.0
74
+
75
+ ### Minor Changes
76
+
77
+ - d58538b: ZERO-3638: Enhance RC pipeline: add fetch, merge, and pre-release setup with conditional commit
78
+
79
+ ## 1.98.0
80
+
81
+ ## 1.97.0
82
+
83
+ ## 1.96.0
84
+
85
+ ## 1.95.0
86
+
87
+ ## 1.94.0
88
+
89
+ ## 1.93.0
90
+
91
+ ## 1.92.0
92
+
93
+ ## 1.91.0
94
+
95
+ ## 1.90.0
96
+
97
+ ### Minor Changes
98
+
99
+ - 9c3a22a: ZERO-3382: Enhance OTP component with customizable UI rendering support
100
+
101
+ ## 1.89.0
102
+
103
+ ## 1.88.0
104
+
105
+ ## 1.87.0
106
+
107
+ ## 1.86.0
108
+
109
+ ## 1.85.0
110
+
111
+ ### Minor Changes
112
+
113
+ - a0a1bac: ZERO-3228 :edit OTP login flow to use Redux for popup visibility and clean up unused props
114
+
115
+ ## 1.84.0
116
+
117
+ ### Minor Changes
118
+
119
+ - 624a4eb: ZERO-3276: Update installation instructions across multiple README files to standardize format and improve clarity
120
+
121
+ ## 1.83.0
122
+
123
+ ## 1.82.0
124
+
125
+ ## 1.81.0
126
+
127
+ ## 1.80.0
128
+
129
+ ## 1.79.0
10
130
 
11
- ## 2.0.0-beta.0
131
+ ## 1.78.0
12
132
 
13
- ### Major Changes
133
+ ## 1.77.0
14
134
 
15
- - be6c09d: ZERO-3114: Create beta version.
135
+ ## 1.76.0
16
136
 
17
137
  ## 1.75.0
18
138
 
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@akinon/pz-otp",
3
- "version": "2.0.0-beta.2",
3
+ "version": "2.0.0-beta.21",
4
4
  "license": "MIT",
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
7
7
  "peerDependencies": {
8
- "react": "^19.0.0",
9
- "react-dom": "^19.0.0"
8
+ "react": "^18.0.0 || ^19.0.0",
9
+ "react-dom": "^18.0.0 || ^19.0.0"
10
10
  },
11
11
  "devDependencies": {
12
- "@types/node": "^22.10.2",
13
- "@types/react": "^19.0.2",
14
- "@types/react-dom": "^19.0.2",
15
- "prettier": "^3.4.2",
12
+ "@types/node": "^18.7.8",
13
+ "@types/react": "^18.0.17",
14
+ "@types/react-dom": "^18.0.6",
15
+ "prettier": "3.0.3",
16
16
  "prettier-plugin-tailwindcss": "0.5.6",
17
- "react": "^19.0.0",
18
- "react-dom": "^19.0.0",
17
+ "react": "19.2.5",
18
+ "react-dom": "19.2.5",
19
19
  "react-hook-form": "7.31.3",
20
- "typescript": "^5.7.2"
20
+ "typescript": "^4.7.4"
21
21
  },
22
22
  "dependencies": {
23
23
  "react-otp-input": "^3.1.0",
package/readme.md CHANGED
@@ -1,11 +1,127 @@
1
1
  # @akinon/pz-otp
2
2
 
3
- ### Install the npm package
3
+ ## Installation method
4
+
5
+ You can use the following command to install the extension with the latest plugins:
4
6
 
5
7
  ```bash
6
- # For latest version
7
- yarn add @akinon/pz-otp
8
8
 
9
- # Preferred installation method
10
9
  npx @akinon/projectzero@latest --plugins
10
+
11
+ ```
12
+
13
+ ### Props
14
+
15
+ | Property | Type | Required | Description |
16
+ | --- | --- | --- | --- |
17
+ | `customUIRender` | `React.ReactNode` | `Optional` | function to fully customize the otp. Receives closeHandler, resendHandler, otp, setOtp, hasError, error, canResend, time, codeLength, setHasError and onSubmit props. |
18
+ | `submitAction` | `SubmitHandler<{ [key: string]: any }>` | `Required` | A submit handler function triggered when OTP form is submitted. |
19
+ | `data` | `{ [key: string]: any }` | `Required` | Data object containing required fields like phone number. OTP code will be appended here before submission |
20
+
21
+ ### Default Usage
22
+
23
+ ```js
24
+ import PluginModule, { Component } from '@akinon/next/components/plugin-module';
25
+
26
+ <PluginModule
27
+ component={Component.Otp}
28
+ props={{
29
+ data: getValues(),
30
+ submitAction: registerHandler
31
+ }}
32
+ />;
33
+ ```
34
+
35
+ ### Customizing OTP
36
+
37
+ ```js
38
+ import PluginModule, { Component } from '@akinon/next/components/plugin-module';
39
+
40
+ <PluginModule
41
+ component={Component.Otp}
42
+ props={{
43
+ data: getValues(),
44
+ submitAction: registerHandler,
45
+ customUIRender: ({
46
+ closeHandler,
47
+ resendHandler,
48
+ onSubmit,
49
+ otp,
50
+ setOtp,
51
+ hasError,
52
+ error,
53
+ canResend,
54
+ time,
55
+ codeLength,
56
+ setHasError
57
+ }) => {
58
+ return (
59
+ <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">
60
+ <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">
61
+ <div className="w-full flex items-center justify-end">
62
+ <div className="cursor-pointer" onClick={closeHandler}>
63
+ <svg
64
+ width="14"
65
+ height="14"
66
+ viewBox="0 0 14 14"
67
+ xmlns="http://www.w3.org/2000/svg"
68
+ >
69
+ <g fill="#000" fillRule="nonzero">
70
+ <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" />
71
+ <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" />
72
+ </g>
73
+ </svg>
74
+ </div>
75
+ </div>
76
+ <div className="flex flex-col items-center px-14 mt-5">
77
+ <div className="text-2xl font-medium">OTP Verification</div>
78
+ <div className={twMerge('mt-2.5 text-center text-gray-700')}>
79
+ {`Please enter the ${codeLength}-digit sms code sent to your registered number and email address`}
80
+ </div>
81
+ </div>
82
+ <form
83
+ onSubmit={onSubmit}
84
+ className="flex flex-col items-center w-full"
85
+ >
86
+ <OtpInput
87
+ value={otp}
88
+ onChange={(otp) => {
89
+ setOtp(otp);
90
+ setHasError(false);
91
+ }}
92
+ numInputs={codeLength}
93
+ containerStyle="mt-12 gap-2 flex-wrap"
94
+ inputStyle={twMerge(
95
+ 'h-12 w-8 md:h-16 md:w-12 rounded-md border border-gray-600 text-center text-lg',
96
+ hasError && 'border-error'
97
+ )}
98
+ renderInput={({ ...props }) => <input {...props} />}
99
+ skipDefaultStyles={true}
100
+ />
101
+ {error && <p className="text-xs text-error mt-2">{error}</p>}
102
+ <Button
103
+ type="submit"
104
+ className="mt-5 h-auto w-full py-4 text-lg font-medium uppercase"
105
+ >
106
+ Verify
107
+ </Button>
108
+ </form>
109
+ <div className="mt-6 flex flex-col items-center">
110
+ <span className="text-gray-700">I didn`t receive a code</span>
111
+ <div
112
+ className={twMerge(
113
+ ' font-medium underline cursor-pointer',
114
+ !canResend && 'cursor-not-allowed text-gray-700'
115
+ )}
116
+ onClick={resendHandler}
117
+ >
118
+ RESEND
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ );
124
+ }
125
+ }}
126
+ />;
11
127
  ```
package/src/views/Otp.tsx CHANGED
@@ -30,7 +30,6 @@ type ComponentClasses = ComponentClassKey | ComponentWrapperClassKey;
30
30
  type OtpProps = {
31
31
  codeLength?: number;
32
32
  timer?: number;
33
- phone?: string;
34
33
  submitAction: SubmitHandler<{
35
34
  [key: string]: any;
36
35
  }>;
@@ -44,6 +43,19 @@ type OtpProps = {
44
43
  [c in ComponentClasses]?: string;
45
44
  };
46
45
  error?: string;
46
+ customUIRender?: (props: {
47
+ closeHandler?: () => void;
48
+ resendHandler?: () => void;
49
+ onSubmit?: (event: FormEvent<HTMLFormElement>) => Promise<void>;
50
+ otp?: string;
51
+ setOtp?: (otp: string) => void;
52
+ hasError?: boolean;
53
+ error?: string;
54
+ canResend?: boolean;
55
+ time?: number;
56
+ codeLength?: number;
57
+ setHasError?: (hasError: boolean) => void;
58
+ }) => ReactNode;
47
59
  };
48
60
 
49
61
  export const Otp = ({
@@ -54,14 +66,16 @@ export const Otp = ({
54
66
  texts,
55
67
  classes,
56
68
  error,
57
- phone
69
+ customUIRender
58
70
  }: OtpProps) => {
71
+ const { phone } = data;
59
72
  const [otp, setOtp] = useState('');
60
73
  const [canResend, setCanResend] = useState(false);
61
74
  const [time, setTime] = useState(timer);
62
75
  const [hasError, setHasError] = useState(false);
76
+ const [isSubmitting, setIsSubmitting] = useState(false);
63
77
  const dispatch = useAppDispatch();
64
- const otpPopupVisible = useAppSelector((state) => state.otp?.isPopupVisible);
78
+ const { isPopupVisible } = useAppSelector((state) => state.otp);
65
79
 
66
80
  const resetTimer = () => {
67
81
  setTime(timer);
@@ -71,18 +85,26 @@ export const Otp = ({
71
85
  const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
72
86
  event.preventDefault();
73
87
 
74
- if (submitAction) {
75
- data.code = otp;
76
- data.phone = phone;
77
- const res = await submitAction(data);
88
+ if (isSubmitting) return;
78
89
 
79
- if (res?.status !== 200 || error) {
80
- setHasError(true);
81
- }
90
+ setIsSubmitting(true);
91
+
92
+ try {
93
+ if (submitAction) {
94
+ data.code = otp;
95
+ data.phone = phone;
96
+ const res = await submitAction(data);
82
97
 
83
- if (res?.status === 200) {
84
- dispatch(hidePopup());
98
+ if (res?.status !== 200 || error) {
99
+ setHasError(true);
100
+ }
101
+
102
+ if (res?.status === 200) {
103
+ dispatch(hidePopup());
104
+ }
85
105
  }
106
+ } finally {
107
+ setIsSubmitting(false);
86
108
  }
87
109
  };
88
110
 
@@ -118,15 +140,33 @@ export const Otp = ({
118
140
  }, [time, onTimerEnd]);
119
141
 
120
142
  useEffect(() => {
121
- document.body.style.overflow = 'hidden';
122
- window.scrollTo({ top: 0, behavior: 'smooth' });
143
+ if (isPopupVisible) {
144
+ document.body.style.overflow = 'hidden';
145
+ window.scrollTo({ top: 0, behavior: 'smooth' });
123
146
 
124
- return () => {
125
- document.body.style.overflow = 'auto';
126
- };
147
+ return () => {
148
+ document.body.style.overflow = 'auto';
149
+ };
150
+ }
127
151
  }, []);
128
152
 
129
- if (!otpPopupVisible) return null;
153
+ if (!isPopupVisible) return null;
154
+
155
+ if (customUIRender) {
156
+ return customUIRender({
157
+ closeHandler,
158
+ resendHandler,
159
+ onSubmit,
160
+ otp,
161
+ setOtp,
162
+ hasError,
163
+ error,
164
+ canResend,
165
+ time,
166
+ codeLength,
167
+ setHasError
168
+ });
169
+ }
130
170
 
131
171
  return (
132
172
  <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">
@@ -194,6 +234,7 @@ export const Otp = ({
194
234
  {error && <p className="text-xs text-error mt-2">{error}</p>}
195
235
  <Button
196
236
  type="submit"
237
+ disabled={isSubmitting}
197
238
  className={twMerge(
198
239
  'mt-5 h-auto w-full py-4 text-lg font-medium uppercase',
199
240
  classes?.submitButton