aio-login 8.0.3 → 9.0.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.
Files changed (4) hide show
  1. package/index.css +10 -9
  2. package/index.d.ts +83 -69
  3. package/index.js +315 -239
  4. package/package.json +1 -1
package/index.css CHANGED
@@ -22,7 +22,6 @@
22
22
  padding: 0 6px;
23
23
  outline: none;
24
24
  flex-shrink: 0;
25
- margin-top: 30px;
26
25
  }
27
26
 
28
27
  .ai-login-form {
@@ -34,6 +33,7 @@
34
33
  display: flex;
35
34
  flex-direction: column;
36
35
  flex-shrink: 0;
36
+ min-width: 300px;
37
37
  }
38
38
 
39
39
  .ai-login-title {
@@ -65,14 +65,6 @@
65
65
  height: 48px;
66
66
  }
67
67
 
68
- .ai-login-errors {
69
- display: flex;
70
- flex-direction: column;
71
- gap: 6px;
72
- padding: 12px;
73
- box-sizing: border-box;
74
- }
75
-
76
68
  .ai-login-error {
77
69
  color: red;
78
70
  display: flex;
@@ -110,3 +102,12 @@
110
102
  .ai-login button:disabled {
111
103
  opacity: 0.3;
112
104
  }
105
+ .ai-form-item-label{
106
+ font-size:12px;
107
+ display:flex;
108
+ gap:4px;
109
+ align-items: center;
110
+ }
111
+ .ai-form-required{
112
+ color:red;
113
+ }
package/index.d.ts CHANGED
@@ -1,31 +1,43 @@
1
1
  import { FC, ReactNode } from "react";
2
- import { AITYPE } from "aio-input";
2
+ import { I_formInput } from "aio-input";
3
3
  import './index.css';
4
4
  type I_loginMode = 'userpass' | 'register' | 'otpcode' | 'otpnumber';
5
- type I_login_field = string;
6
- export type I_login_key = 'registerButton' | 'userpassButton' | 'otpnumberButton' | 'otpcodeButton' | 'registerTitle' | 'userpassTitle' | 'otpcodeTitle' | 'otpnumberTitle' | 'switchuserpass' | 'switchregister' | 'switchotp' | 'repasswordMatch' | 'usernameRequired' | 'passwordRequired' | 'repasswordRequired' | 'otpnumberRequired' | 'otpcodeLength' | 'registerError' | 'userpassError' | 'otpcodeError' | 'otpnumberError';
7
- type I_login_model = {
8
- username: string;
9
- password: string;
5
+ export type I_login_key = 'registerButton' | 'userpassButton' | 'otpnumberButton' | 'otpcodeButton' | 'registerTitle' | 'userpassTitle' | 'otpcodeTitle' | 'otpnumberTitle' | 'switchuserpass' | 'switchregister' | 'switchotpnumber' | 'repasswordMatch' | 'usernameRequired' | 'passwordRequired' | 'repasswordRequired' | 'otpnumberRequired' | 'otpcodeLength' | 'registerError' | 'userpassError' | 'otpcodeError' | 'otpnumberError' | 'username' | 'password' | 'repassword' | 'otpnumber' | 'otpcode';
6
+ type I_login_model<T> = {
7
+ userpass: {
8
+ username: string;
9
+ password: string;
10
+ };
10
11
  otpnumber: string;
11
12
  otpcode: string;
12
- register: any;
13
+ register: {
14
+ username: string;
15
+ password: string;
16
+ repassword: string;
17
+ properties: {
18
+ [key in keyof T]?: T[key] | undefined;
19
+ };
20
+ };
13
21
  };
14
- type I_AILogin = {
15
- checkToken: (token: string) => Promise<{
16
- method: 'post' | 'get';
22
+ type I_registerInputs = {
23
+ [field: string]: I_formInput;
24
+ };
25
+ type I_AILogin<T> = {
26
+ app: FC<{
27
+ user: T;
28
+ token: string;
29
+ base_url: string;
30
+ logout: () => void;
31
+ }>;
32
+ base_url: string;
33
+ checkToken: {
17
34
  url: string;
35
+ method?: 'post' | 'get';
18
36
  body?: any;
19
- onSuccess: (response: any) => string | boolean;
20
- onCatch: (response: any) => string | false;
21
- }>;
37
+ getResult?: (response: any) => boolean;
38
+ };
22
39
  before?: (mode: I_loginMode) => ReactNode;
23
40
  after?: (mode: I_loginMode) => ReactNode;
24
- renderApp: (p: {
25
- user: any;
26
- token: string;
27
- logout: () => void;
28
- }) => ReactNode;
29
41
  translate?: (key: I_login_key) => string | undefined;
30
42
  fa?: boolean;
31
43
  id: string;
@@ -33,69 +45,71 @@ type I_AILogin = {
33
45
  html: ReactNode;
34
46
  time: number;
35
47
  };
36
- label: (field: I_login_field) => string;
37
- validation?: (model: I_login_model, mode: I_loginMode) => string | undefined;
38
- otpLength?: number;
39
- otp?: {
40
- numberApi: (model: I_login_model, mode: I_loginMode) => Promise<{
41
- method: 'post' | 'get';
42
- url: string;
43
- body?: any;
44
- onSuccess: (response: any) => Promise<{
45
- message?: string;
46
- }>;
47
- onCatch: (response: any) => string;
48
+ validate?: (p: {
49
+ field: string;
50
+ data: I_login_model<T>;
51
+ value: any;
52
+ input: I_formInput;
53
+ }) => string | undefined;
54
+ otpnumber?: {
55
+ path: string;
56
+ method: 'post' | 'get';
57
+ body?: (data: I_login_model<T>) => any;
58
+ onSuccess: (response: any) => Promise<{
59
+ message?: string;
48
60
  }>;
49
- codeApi: (model: I_login_model, mode: I_loginMode) => Promise<{
50
- method: 'post' | 'get';
51
- url: string;
52
- body?: any;
53
- onSuccess: (response: any) => Promise<{
54
- user: any;
55
- token: string;
56
- message?: string;
57
- }>;
58
- onCatch: (response: any) => string;
61
+ };
62
+ otpcode?: {
63
+ length: number;
64
+ method: 'post' | 'get';
65
+ body?: (data: I_login_model<T>) => any;
66
+ path: string;
67
+ onSuccess: (response: any) => Promise<{
68
+ user: T;
69
+ token: string;
70
+ message?: string;
59
71
  }>;
60
72
  };
61
73
  userpass?: {
62
- api: (model: I_login_model, mode: I_loginMode) => Promise<{
63
- method: 'post' | 'get';
64
- url: string;
65
- body?: any;
66
- onSuccess: (response: any) => Promise<{
67
- user: any;
68
- token: string;
69
- message?: string;
70
- }>;
71
- onCatch: (response: any) => string;
74
+ path: string;
75
+ method: 'post' | 'get';
76
+ body?: (data: I_login_model<T>) => any;
77
+ onSuccess: (response: any) => Promise<{
78
+ user: T;
79
+ token: string;
80
+ message?: string;
72
81
  }>;
73
82
  };
74
83
  register?: {
75
- defaultValue?: {
76
- [field: string]: any;
77
- };
78
- inputs?: (model: I_login_model) => (AITYPE & {
79
- field: string;
80
- })[];
81
- api: (model: I_login_model, mode: I_loginMode) => Promise<{
82
- method: 'post' | 'get';
83
- url: string;
84
- body?: any;
85
- onSuccess: (response: any) => Promise<{
86
- message?: string;
87
- }>;
88
- onCatch: (response: any) => string;
84
+ inputs?: (model: I_login_model<T>) => I_registerInputs;
85
+ path: string;
86
+ method: 'post' | 'get';
87
+ body?: (data: I_login_model<T>) => any;
88
+ onSuccess: (response: any) => Promise<{
89
+ message?: string;
89
90
  }>;
91
+ defaultData?: any;
90
92
  };
93
+ getStatus?: (response: any) => number;
94
+ getMessage?: (response: any) => string;
91
95
  mode?: I_loginMode;
92
- attrs?: any;
96
+ attrs?: {
97
+ [key: string]: any;
98
+ };
93
99
  setAttrs?: (key: I_login_key) => any;
94
100
  mock?: {
95
- user: any;
101
+ user: T;
96
102
  token: string;
97
103
  };
98
104
  };
99
- export declare function AIOLogin_updateCatchedUser(loginId: string, newUser: any): any;
100
- declare const AILogin: FC<I_AILogin>;
101
- export default AILogin;
105
+ export declare function AIOLogin_updateCatchedUser<T>(loginId: string, newUser: T): any;
106
+ export type I_AuthContext<T> = {
107
+ user?: T;
108
+ token?: string;
109
+ logout: () => void;
110
+ base_url: string;
111
+ };
112
+ declare const useLogin: <T extends Record<string, any>>(props: I_AILogin<T>) => {
113
+ render: () => JSX.Element;
114
+ };
115
+ export default useLogin;
package/index.js CHANGED
@@ -7,14 +7,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { createElement as _createElement } from "react";
11
- import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
12
- import { useEffect, useRef, useState } from "react";
13
- import AIOInput from "aio-input";
14
- import { AddToAttrs, Storage } from "aio-utils";
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import { useEffect, useState } from "react";
12
+ import { useForm } from "aio-input";
13
+ import { AddToAttrs, FixUrl, Storage } from "aio-utils";
15
14
  import { Loading, Alert } from "aio-popup";
16
15
  import axios from "axios";
17
16
  import './index.css';
17
+ import { Navigate, Route, Routes } from "react-router-dom";
18
18
  export function AIOLogin_updateCatchedUser(loginId, newUser) {
19
19
  const storage = new Storage('ai-login' + loginId);
20
20
  const storedData = storage.load('data');
@@ -24,135 +24,120 @@ export function AIOLogin_updateCatchedUser(loginId, newUser) {
24
24
  const newStoredData = Object.assign(Object.assign({}, storedData), { user: newUser });
25
25
  return storage.save('data', newStoredData);
26
26
  }
27
- const AILogin = (props) => {
28
- const { translate = () => { }, otpLength = 4 } = props;
29
- const { validation = () => { return undefined; } } = props;
30
- const [data, setData] = useState();
31
- const [storage] = useState(new Storage('ai-login' + props.id));
32
- const [model, setModel] = useState(getModel);
27
+ const useLoading = (rootProps) => {
28
+ const { register, translate = () => { }, fa, validate = () => { return undefined; }, userpass, splash, otpnumber, otpcode } = rootProps;
29
+ const [storage] = useState(new Storage('ai-login' + rootProps.id));
33
30
  const [loading] = useState(new Loading());
34
- const [submitDisabled, setSubmitDisabled] = useState(false);
35
- const modelRef = useRef(model);
36
- modelRef.current = model;
37
- console.log(modelRef.current);
38
- const [mode, setMode] = useState(getMode());
39
- function getModeKey() {
40
- if (props.mode) {
41
- return props.mode;
42
- }
43
- if (props.userpass) {
44
- return 'userpass';
45
- }
46
- if (props.otp) {
47
- return 'otpnumber';
48
- }
49
- return 'userpass';
50
- }
31
+ const [splashing, setSplashing] = useState(!!rootProps.splash);
32
+ const [data, setData] = useState();
33
+ const [checkingToken, setCheckingToken] = useState();
34
+ const [mode, setMode] = useState(getMode);
51
35
  function getMode(mode) {
52
- let res = { inputs: () => null, key: mode || getModeKey(), title: null, submitText: '', shouldReturnsUserAndToken: false };
53
- if (res.key === 'userpass') {
54
- res.inputs = () => {
55
- return (_jsxs(_Fragment, { children: [getInput({ mode: res.key, field: 'username', type: 'text' }), getInput({ mode: res.key, field: 'password', type: 'text' })] }));
56
- };
57
- res.shouldReturnsUserAndToken = true;
36
+ let key = 'userpass';
37
+ if (mode) {
38
+ key = mode;
58
39
  }
59
- else if (res.key === 'register') {
60
- if (props.register) {
61
- const inputs = (props.register.inputs || (() => []))(modelRef.current) || [];
62
- res.inputs = () => {
63
- return (_jsxs(_Fragment, { children: [getInput({ mode: res.key, field: 'username', type: 'text' }), getInput({ mode: res.key, field: 'password', type: 'password' }), getInput({ mode: res.key, field: 'repassword', type: 'password' }), inputs.map((input) => getInput({ mode: res.key, field: input.field, type: input.type, inputProps: input }))] }));
64
- };
65
- }
40
+ else if (rootProps.mode) {
41
+ key = rootProps.mode;
66
42
  }
67
- else if (res.key === 'otpnumber') {
68
- res.inputs = () => getInput({ mode: res.key, field: 'otpnumber', type: 'text' });
43
+ else if (userpass) {
44
+ key = 'userpass';
69
45
  }
70
- else if (res.key === 'otpcode') {
71
- res.inputs = () => getInput({ mode: res.key, field: 'otpcode', type: 'text' });
72
- res.shouldReturnsUserAndToken = true;
46
+ else if (otpnumber) {
47
+ key = 'otpnumber';
73
48
  }
74
- res.submitText = trans(res.key + 'Button');
75
- res.title = _jsx("div", { className: "ai-login-title", children: trans(res.key + 'Title') });
76
- return res;
77
- }
78
- function getInput(p) {
79
- const inputProps = p.inputProps || {};
80
- let model = modelRef.current;
81
- if (p.mode === 'register') {
82
- model = model.register;
49
+ else {
50
+ key = 'userpass';
83
51
  }
84
- const { maxLength = { otpnumber: 11, otpcode: otpLength }[p.mode], filter = p.mode === 'otpnumber' || p.mode === 'otpcode' ? ['number'] : undefined, preview = p.field === 'password', rtl = !!props.fa, label = props.label(p.field) } = inputProps;
85
- const inputAttrs = Object.assign(Object.assign({}, inputProps), { 'aria-label': `aio-login-${p.field}` });
86
- const onChange = (v) => {
87
- let model = modelRef.current;
88
- if (p.mode === 'register') {
89
- setModel(Object.assign(Object.assign({}, model), { register: Object.assign(Object.assign({}, model.register), { [p.field]: v }) }));
90
- }
91
- else {
92
- setModel(Object.assign(Object.assign({}, model), { [p.field]: v }));
93
- }
52
+ return {
53
+ key,
54
+ submitText: trans(key + 'Button'),
55
+ shouldReturnsUserAndToken: key === 'userpass' || key === 'otpcode',
56
+ title: _jsx("div", { className: "ai-login-title", children: trans(key + 'Title') })
94
57
  };
95
- return (_createElement(AIOInput, Object.assign({}, inputProps, { maxLength, filter, preview, rtl, label, onChange, inputAttrs, key: `${p.mode}-${p.field}`, value: model[p.field], type: p.type })));
96
58
  }
97
- function getModel() {
98
- var _a;
99
- let model = { username: '', password: '', otpnumber: '', otpcode: '', register: { username: '', password: '', repassword: '' } };
100
- if (!props.register) {
101
- return model;
102
- }
103
- if ((_a = props.register) === null || _a === void 0 ? void 0 : _a.defaultValue) {
104
- let register = {};
105
- for (let prop in props.register.defaultValue) {
106
- register[prop] = props.register.defaultValue[prop];
59
+ const formHook = useForm({
60
+ initData: () => getModel(), fa,
61
+ isFieldActive: (field) => mode.key === field.split('.')[0],
62
+ inputs: () => {
63
+ let inputs = {
64
+ 'userpass.username': { type: 'text', label: trans('username') },
65
+ 'userpass.password': { type: 'password', preview: true, label: trans('password') },
66
+ 'register.username': { type: 'text', label: trans('username') },
67
+ 'register.password': { type: 'password', preview: true, label: trans('password') },
68
+ 'register.repassword': { type: 'password', preview: true, label: trans('repassword') },
69
+ 'otpnumber': { type: 'text', maxLength: 11, filter: ['number'], label: trans('otpnumber') },
70
+ 'otpcode': { type: 'text', maxLength: (otpcode === null || otpcode === void 0 ? void 0 : otpcode.length) || 4, filter: ['number'], label: trans('otpcode') }
71
+ };
72
+ if (register && register.inputs) {
73
+ const registerInputs = register.inputs({});
74
+ for (let field in registerInputs) {
75
+ const input = registerInputs[field];
76
+ inputs[`register.properties.${field}`] = Object.assign(Object.assign({}, input), { inputProps: Object.assign(Object.assign({}, input.inputAttrs), { 'aria-label': `aio-login-${field}` }) });
77
+ }
107
78
  }
108
- model.register = register;
109
- }
110
- return model;
111
- }
112
- const [waitingCheckToken, setWeightingCheckToken] = useState(true);
113
- const [splashing, setSplashing] = useState(!!props.splash);
114
- function trans(key) {
115
- const dic = {
116
- registerButton: { en: 'Register', fa: 'ثبت نام' },
117
- userpassButton: { en: 'Login', fa: 'ورود' },
118
- otpnumberButton: { en: 'Send Number', fa: 'ارسال شماره همراه' },
119
- otpcodeButton: { en: 'Login', fa: 'ورود' },
120
- registerTitle: { en: 'Register', fa: 'ثبت نام' },
121
- userpassTitle: { en: 'Login', fa: 'ورود' },
122
- otpcodeTitle: { en: 'OTP Code', fa: 'کد یکبار مصرف' },
123
- otpnumberTitle: { en: 'Phone Number', fa: 'شماره همراه' },
124
- switchuserpass: { en: 'login by user name', fa: 'ورود با نام کاربری' },
125
- switchregister: { en: 'Go To Register', fa: 'ثبت نام' },
126
- switchotp: { en: 'login by otp', fa: 'ورود با رمز یکبار مصرف' },
127
- repasswordMatch: { en: 'Password is not match with Re password', fa: 'رمز عبور با تکرار آن مطابقت ندارد' },
128
- usernameRequired: { en: 'User Name is required', fa: 'نام کاربری ضروری است' },
129
- passwordRequired: { en: 'password is required', fa: 'رمز عبور ضروری است' },
130
- repasswordRequired: { en: 'Re Password is required', fa: 'تکرار رمز عبور ضروری است' },
131
- otpnumberRequired: { en: 'Phone Number is required', fa: 'شماره همراه ضروری است' },
132
- otpcodeLength: { en: `otp code should be ${otpLength} digit`, fa: `کد یکبار مصرف باید ${otpLength} رقم باشد` },
133
- registerError: { en: 'Registeration failed', fa: 'ثبت نام با خطا روبرو شد' },
134
- userpassError: { en: 'login by username failed', fa: 'ورود با نام کاربری با خطا روبرو شد' },
135
- otpcodeError: { en: 'login by otp failed', fa: 'ورود با کد یکبار مصرف با خطا روبرو شد' },
136
- otpnumberError: { en: 'send otp number for receive otp code failed', fa: 'ارسال شماره همراه برای دریافت کد یکبار مصرف با خطا روبرو شد' },
137
- };
138
- return props.fa ? dic[key].fa : translate(key) || dic[key].en;
139
- }
79
+ return inputs;
80
+ },
81
+ validate: ({ field, data, value, input }) => {
82
+ if (field === 'otpcode') {
83
+ if ((value || '').length !== ((otpcode === null || otpcode === void 0 ? void 0 : otpcode.length) || 4)) {
84
+ return trans('otpcodeLength');
85
+ }
86
+ }
87
+ if (!value) {
88
+ if (field === 'otpnumber') {
89
+ return trans('otpnumberRequired');
90
+ }
91
+ if (field === 'userpass.username') {
92
+ return trans('usernameRequired');
93
+ }
94
+ if (field === 'userpass.password') {
95
+ return trans('passwordRequired');
96
+ }
97
+ if (field === 'register.username') {
98
+ return trans('usernameRequired');
99
+ }
100
+ if (field === 'register.password') {
101
+ return trans('passwordRequired');
102
+ }
103
+ if (field === 'register.repassword') {
104
+ return trans('repasswordRequired');
105
+ }
106
+ }
107
+ if (field === 'register.repassword' && data.register.password !== value) {
108
+ return trans('repasswordMatch');
109
+ }
110
+ return validate({ field, data, value, input });
111
+ },
112
+ onSubmit: (data) => __awaiter(void 0, void 0, void 0, function* () {
113
+ const { path, method, body = () => { }, onSuccess } = rootProps[mode.key];
114
+ const { base_url } = rootProps;
115
+ const url = FixUrl(base_url, path);
116
+ loading.show('login0');
117
+ loading.hide('login0');
118
+ axios[method](url, body(data)).then(success).catch(response => {
119
+ const subtext = getMessage(response, mode.key);
120
+ Alert({ type: 'error', text: trans(`${mode.key}Error`), subtext });
121
+ });
122
+ })
123
+ });
124
+ function changeMode(mode) { formHook.setData(getModel()); setMode(getMode(mode)); }
140
125
  function success_1(response, mode) {
141
126
  return __awaiter(this, void 0, void 0, function* () {
142
- if (!props.userpass) {
127
+ if (!rootProps[mode]) {
143
128
  return;
144
129
  }
145
- const { onSuccess } = yield props.userpass.api(modelRef.current, 'userpass');
130
+ const { onSuccess } = rootProps[mode];
146
131
  loading.show('login0');
147
132
  const res = yield onSuccess(response);
148
133
  loading.hide('login0');
149
134
  if (typeof res !== 'object' || !res.user || typeof res.token !== 'string') {
150
135
  const message = `onSuccess of props.${mode}.${mode === 'userpass' ? 'api' : 'codeApi'} should returns {user:any,token:string}`;
151
- setAlert({ type: 'error', text: trans(`${mode}Error`), subtext: message });
136
+ Alert({ type: 'error', text: trans(`${mode}Error`), subtext: message });
152
137
  }
153
138
  else {
154
139
  if (res.message) {
155
- setAlert({ type: 'success', text: res.message });
140
+ Alert({ type: 'success', text: res.message });
156
141
  }
157
142
  const { user, token } = res;
158
143
  storage.save('data', { user, token });
@@ -162,15 +147,15 @@ const AILogin = (props) => {
162
147
  }
163
148
  function success_2(response, mode) {
164
149
  return __awaiter(this, void 0, void 0, function* () {
165
- if (!props.userpass) {
150
+ if (!rootProps[mode]) {
166
151
  return;
167
152
  }
168
- const { onSuccess } = yield props.userpass.api(modelRef.current, 'userpass');
153
+ const { onSuccess } = rootProps[mode];
169
154
  loading.show('login0');
170
155
  const res = yield onSuccess(response);
171
156
  loading.hide('login0');
172
157
  if (res.message) {
173
- setAlert({ type: 'success', text: res.message });
158
+ Alert({ type: 'success', text: res.message });
174
159
  }
175
160
  if (mode === 'otpnumber') {
176
161
  setMode(getMode('otpcode'));
@@ -190,154 +175,245 @@ const AILogin = (props) => {
190
175
  }
191
176
  });
192
177
  }
193
- function submit() {
194
- return __awaiter(this, void 0, void 0, function* () {
195
- loading.show('login0');
196
- const { url, method, body, onCatch } = yield props[mode.key].api(modelRef.current, mode.key);
197
- loading.hide('login0');
198
- axios[method](url, body).then(success).catch(response => {
199
- if (onCatch) {
200
- setAlert({ type: 'error', text: 'Error', subtext: onCatch(response) });
201
- }
202
- else if (response.message) {
203
- setAlert({ type: 'error', text: 'Error', subtext: response.message });
178
+ const getStatus = (error, name) => {
179
+ let text = `${name} unknown status.`, subtext = 'please set getStatus props in useLogin for extracting status';
180
+ if (error.response) {
181
+ const data = error.response.data;
182
+ let status = error.response.status;
183
+ if (typeof status !== 'number') {
184
+ const { getStatus = () => { } } = rootProps;
185
+ status = getStatus(error);
186
+ if (typeof status === 'number') {
187
+ return status;
204
188
  }
205
- });
206
- });
207
- }
208
- function changeMode(mode) { setModel(getModel()); setMode(getMode(mode)); }
209
- function mode_props(key) { return { className: 'ai-login-mode', onClick: () => changeMode(key) }; }
210
- function mode_layout() {
211
- return (_jsxs("div", { className: "ai-login-modes", children: [props.userpass && mode.key !== 'userpass' && _jsx("button", Object.assign({}, mode_props('userpass'), { children: trans('switchuserpass') })), props.register && mode.key !== 'register' && _jsx("button", Object.assign({}, mode_props('register'), { children: trans('switchregister') })), props.otp && mode.key !== 'otpnumber' && _jsx("button", Object.assign({}, mode_props('otpnumber'), { children: trans('switchotp') }))] }));
212
- }
213
- function validate() {
214
- const model = modelRef.current;
215
- if (mode.key === 'otpcode') {
216
- if ((model.otpcode || '').length !== otpLength) {
217
- return trans('otpcodeLength');
218
189
  }
219
190
  }
220
- if (mode.key === 'otpnumber') {
221
- if (!model.otpnumber) {
222
- return trans('otpnumberRequired');
223
- }
224
- }
225
- if (mode.key === 'userpass') {
226
- if (!model.username) {
227
- return trans('usernameRequired');
228
- }
229
- }
230
- if (mode.key === 'register') {
231
- if (!model.register.username) {
232
- return trans('usernameRequired');
191
+ Alert({ type: 'error', text, subtext });
192
+ };
193
+ const getMessage = (error, name) => {
194
+ let text = `${name} unknown message.`, subtext = 'please set getMessage props in useLogin for extracting message';
195
+ if (error.response) {
196
+ const data = error.response.data;
197
+ if (typeof data === 'string') {
198
+ return data;
233
199
  }
234
- if (!model.register.password) {
235
- return trans('passwordRequired');
200
+ else if (data === null || data === void 0 ? void 0 : data.message) {
201
+ return data.message;
236
202
  }
237
- if (!model.register.repassword) {
238
- return trans('repasswordRequired');
203
+ else if (data === null || data === void 0 ? void 0 : data.error) {
204
+ return data.error;
239
205
  }
240
- if (model.register.password !== model.register.repassword) {
241
- return trans('repasswordMatch');
206
+ else {
207
+ const { getMessage = () => '' } = rootProps;
208
+ const message = getMessage(error);
209
+ if (message) {
210
+ return message;
211
+ }
242
212
  }
243
213
  }
244
- return validation(model, mode.key);
245
- }
246
- function submit_layout() {
247
- const message = validate();
248
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "ai-login-errors", children: !!message && _jsx("div", { className: "ai-login-error", children: message }) }), _jsx("button", { className: 'ai-login-submit', disabled: !!message || !!submitDisabled, onClick: () => {
249
- setSubmitDisabled(true);
250
- setTimeout(() => setSubmitDisabled(false), 3000);
251
- submit();
252
- }, children: mode.submitText })] }));
253
- }
254
- function form_layout() {
255
- const { title, inputs } = mode;
256
- return (_jsxs("div", { className: "ai-login-form", children: [title, inputs(), submit_layout(), mode_layout()] }));
257
- }
258
- const bf_layout = (type) => {
259
- const fn = props[type];
260
- let content = null;
261
- if (fn) {
262
- content = fn(mode.key);
214
+ else if (typeof error.request === 'string') {
215
+ return error.request;
216
+ }
217
+ else if (typeof error.message === 'string') {
218
+ return error.message;
219
+ }
220
+ Alert({ text, subtext, type: 'error' });
221
+ return 'unkown message';
222
+ };
223
+ const checkTokenCatch = (error) => {
224
+ const status = getStatus(error, 'checking token');
225
+ if (status === 401) {
226
+ logout();
227
+ }
228
+ const message = getMessage(error, 'checking token');
229
+ let text = 'error in checking token';
230
+ if (rootProps.fa) {
231
+ text = 'خطا در بررسی توکن';
232
+ }
233
+ Alert({ type: 'error', text, subtext: message });
234
+ };
235
+ const checkTokenThen = (response, user, token) => {
236
+ const { checkToken } = rootProps;
237
+ const { getResult = () => true } = checkToken;
238
+ const res = getResult(response);
239
+ if (res === true) {
240
+ setData({ user, token });
241
+ setCheckingToken(true);
242
+ }
243
+ else if (res === false) {
244
+ logout();
245
+ }
246
+ else {
247
+ Alert({ type: 'error', text: 'check token error', subtext: 'checkToken.getResult should returns boolean' });
263
248
  }
264
- return (_jsx("div", { className: `ai-login-${type}`, children: content }));
265
249
  };
266
- function logout() { storage.remove('data'); window.location.reload(); }
267
250
  function CheckToken() {
268
251
  return __awaiter(this, void 0, void 0, function* () {
269
- if (props.splash) {
270
- setTimeout(() => { setSplashing(false); }, props.splash.time);
252
+ if (rootProps.splash) {
253
+ setTimeout(() => { setSplashing(false); }, rootProps.splash.time);
271
254
  }
272
- if (props.mock) {
273
- setData({ user: props.mock.user, token: props.mock.token });
255
+ if (rootProps.mock) {
256
+ setData({ user: rootProps.mock.user, token: rootProps.mock.token });
274
257
  return;
275
258
  }
276
- const storedData = storage.load('data', {}), { user, token } = storedData;
259
+ const storedData = storage.load('data', {});
260
+ const { user, token } = storedData;
277
261
  loading.show('login0');
278
- const { url, method, onSuccess, onCatch } = yield props.checkToken(token || '');
262
+ const { checkToken } = rootProps;
263
+ const { url, method = 'get', body } = checkToken;
279
264
  loading.hide('login0');
280
265
  if (user && token) {
281
- axios[method](url, { headers: { authorization: `Bearer ${token}` } })
282
- .then(response => {
283
- let res;
284
- try {
285
- res = onSuccess(response);
286
- }
287
- catch (err) {
288
- setAlert({ type: 'error', text: 'checkToken failed', subtext: err.message });
289
- return;
290
- }
291
- if (res === true) {
292
- setData({ user, token });
293
- }
294
- else if (res === false) {
295
- logout();
296
- }
297
- else {
298
- setAlert({ type: 'error', text: 'checkToken failed', subtext: 'checkToken props should return string as error or true as token is valid and false as token is invalid' });
299
- }
300
- })
301
- .catch(response => {
302
- let res, message = '';
303
- try {
304
- res = onCatch(response);
305
- }
306
- catch (err) {
307
- message = err.message;
308
- }
309
- if (typeof res === 'string') {
310
- message = res;
311
- }
312
- else if (res === false) {
313
- logout();
314
- }
315
- else {
316
- message = 'AILogin checkToken onCatch props should returns string as error or false as invalid token';
317
- }
318
- if (message) {
319
- setAlert({ type: 'error', text: 'checkToken failed', subtext: message });
320
- }
321
- else if (response.message) {
322
- setAlert({ type: 'error', text: 'Error', subtext: response.message });
323
- }
324
- });
266
+ const headers = { authorization: `Bearer ${token}` };
267
+ if (method === 'get') {
268
+ axios.get(url, { headers }).then(response => checkTokenThen(response, user, token)).catch(error => checkTokenCatch(error));
269
+ }
270
+ else {
271
+ axios.post(url, body, { headers }).then(response => checkTokenThen(response, user, token)).catch(error => checkTokenCatch(error));
272
+ }
273
+ }
274
+ else {
275
+ logout();
325
276
  }
326
- setWeightingCheckToken(false);
327
277
  });
328
278
  }
279
+ function logout() {
280
+ storage.remove('data');
281
+ setCheckingToken(false);
282
+ setData(undefined);
283
+ }
284
+ function trans(key) {
285
+ const dic = {
286
+ username: { en: 'User Name', fa: 'نام کاربری' },
287
+ password: { en: 'Password', fa: 'رمز عبور' },
288
+ repassword: { en: 'User Name', fa: 'تکرار رمز عبور' },
289
+ otpnumber: { en: 'Phone Number', fa: 'شماره همراه' },
290
+ otpcode: { en: 'One Time Code', fa: 'کد یکبار مصرف' },
291
+ registerButton: { en: 'Register', fa: 'ثبت نام' },
292
+ userpassButton: { en: 'Login', fa: 'ورود' },
293
+ otpnumberButton: { en: 'Send Number', fa: 'ارسال شماره همراه' },
294
+ otpcodeButton: { en: 'Login', fa: 'ورود' },
295
+ registerTitle: { en: 'Register', fa: 'ثبت نام' },
296
+ userpassTitle: { en: 'Login', fa: 'ورود' },
297
+ otpcodeTitle: { en: 'One Time Code', fa: 'کد یکبار مصرف' },
298
+ otpnumberTitle: { en: 'Phone Number', fa: 'شماره همراه' },
299
+ switchuserpass: { en: 'login by user name', fa: 'ورود با نام کاربری' },
300
+ switchregister: { en: 'Go To Register', fa: 'ثبت نام' },
301
+ switchotpnumber: { en: 'login by otp', fa: 'ورود با رمز یکبار مصرف' },
302
+ repasswordMatch: { en: 'Password is not match with Re password', fa: 'رمز عبور با تکرار آن مطابقت ندارد' },
303
+ usernameRequired: { en: 'User Name is required', fa: 'نام کاربری ضروری است' },
304
+ passwordRequired: { en: 'password is required', fa: 'رمز عبور ضروری است' },
305
+ repasswordRequired: { en: 'Re Password is required', fa: 'تکرار رمز عبور ضروری است' },
306
+ otpnumberRequired: { en: 'Phone Number is required', fa: 'شماره همراه ضروری است' },
307
+ otpcodeLength: { en: `otp code should be ${(otpcode === null || otpcode === void 0 ? void 0 : otpcode.length) || 4} digit`, fa: `کد یکبار مصرف باید ${(otpcode === null || otpcode === void 0 ? void 0 : otpcode.length) || 4} رقم باشد` },
308
+ registerError: { en: 'Registeration failed', fa: 'ثبت نام با خطا روبرو شد' },
309
+ userpassError: { en: 'login by username failed', fa: 'ورود با نام کاربری با خطا روبرو شد' },
310
+ otpcodeError: { en: 'login by otp failed', fa: 'ورود با کد یکبار مصرف با خطا روبرو شد' },
311
+ otpnumberError: { en: 'send otp number for receive otp code failed', fa: 'ارسال شماره همراه برای دریافت کد یکبار مصرف با خطا روبرو شد' },
312
+ };
313
+ return fa ? dic[key].fa : translate(key) || dic[key].en;
314
+ }
329
315
  useEffect(() => { CheckToken(); }, []);
330
- function setAlert(p) { Alert(p); }
331
- function getContent() {
332
- if (waitingCheckToken || splashing) {
333
- return props.splash ? props.splash.html : null;
316
+ function getModel() {
317
+ let model = {
318
+ userpass: { username: '', password: '' },
319
+ otpnumber: '', otpcode: '',
320
+ register: { username: '', password: '', repassword: '', properties: {} }
321
+ };
322
+ if (!register) {
323
+ return model;
324
+ }
325
+ if (register && register.inputs && register.defaultData) {
326
+ model.register.properties = Object.assign({}, register.defaultData);
327
+ }
328
+ return model;
329
+ }
330
+ const getFormNode = () => {
331
+ let registerInputs = {};
332
+ if (register && register.inputs) {
333
+ registerInputs = register.inputs(formHook.data);
334
+ }
335
+ const registerFields = Object.keys(registerInputs);
336
+ return {
337
+ scroll: true,
338
+ v: [
339
+ { input: 'userpass.username', show: mode.key === 'userpass' },
340
+ { input: 'userpass.password', show: mode.key === 'userpass' },
341
+ { input: 'register.username', show: mode.key === 'register' },
342
+ { input: 'register.password', show: mode.key === 'register' },
343
+ { input: 'register.repassword', show: mode.key === 'register' },
344
+ { input: 'otpnumber', show: mode.key === 'otpnumber' },
345
+ { input: 'otpcode', show: mode.key === 'otpcode' },
346
+ { v: registerFields.map((field) => ({ input: `register.properties.${field}`, show: mode.key === 'register' })) }
347
+ ]
348
+ };
349
+ };
350
+ function renderMode(modeKey) {
351
+ if (mode.key === modeKey) {
352
+ return null;
353
+ }
354
+ return _jsx("button", { className: 'ai-login-mode', onClick: () => changeMode(modeKey), children: trans(`switch${modeKey}`) });
355
+ }
356
+ function renderModes() {
357
+ return (_jsxs("div", { className: "ai-login-modes", children: [userpass && renderMode('userpass'), register && renderMode('register'), otpnumber && renderMode('otpnumber')] }));
358
+ }
359
+ function renderLoginBox() {
360
+ const { title } = mode;
361
+ return formHook.render({
362
+ attrs: { className: "ai-login-form" },
363
+ v: [
364
+ { html: title },
365
+ getFormNode(),
366
+ { html: _jsx("div", { style: { height: 24 } }), size: 24, },
367
+ { submitButton: { text: mode.submitText, attrs: { className: 'ai-login-submit' } } },
368
+ { html: renderModes() }
369
+ ]
370
+ });
371
+ }
372
+ const bf_layout = (type) => {
373
+ const fn = rootProps[type];
374
+ let content = null;
375
+ if (fn) {
376
+ content = fn(mode.key);
377
+ }
378
+ return (_jsx("div", { className: `ai-login-${type}`, children: content }));
379
+ };
380
+ function renderLoginPage() {
381
+ if (checkingToken || splashing) {
382
+ return splash ? splash.html : null;
334
383
  }
335
384
  if (!data) {
336
- const attrs = AddToAttrs(props.attrs, { className: 'ai-login', style: { direction: !!props.fa ? 'rtl' : undefined } });
337
- return (_jsxs("div", Object.assign({}, attrs, { children: [bf_layout('before'), " ", form_layout(), " ", bf_layout('after')] })));
385
+ const attrs = AddToAttrs(rootProps.attrs, { className: 'ai-login', style: { direction: !!rootProps.fa ? 'rtl' : undefined } });
386
+ return (_jsxs("div", Object.assign({}, attrs, { children: [bf_layout('before'), " ", renderLoginBox(), " ", bf_layout('after')] })));
338
387
  }
339
- return props.renderApp({ token: data.token, user: data.user, logout });
388
+ return _jsx(Navigate, { to: "/" });
340
389
  }
341
- return (_jsx(_Fragment, { children: getContent() }));
390
+ function renderApp() {
391
+ const { user, token } = data || {};
392
+ const { app, base_url } = rootProps;
393
+ if (user && token) {
394
+ const COMP = app;
395
+ return _jsx(COMP, { user: user, token: token, logout: logout, base_url: base_url });
396
+ }
397
+ if (checkingToken === false) {
398
+ return _jsx(Navigate, { to: "/login" });
399
+ }
400
+ return null;
401
+ }
402
+ return {
403
+ logout, storage,
404
+ splashing, loading, checkingToken,
405
+ data, setData,
406
+ getMessage, trans,
407
+ getModel, formHook,
408
+ mode, getMode, setMode, changeMode,
409
+ renderApp, renderLoginBox, renderLoginPage
410
+ };
411
+ };
412
+ const useLogin = (props) => {
413
+ const render = () => {
414
+ const hook = useLoading(props);
415
+ return (_jsxs(Routes, { children: [_jsx(Route, { path: '/login', element: hook.renderLoginPage() }), _jsx(Route, { path: "/*", element: hook.renderApp() })] }));
416
+ };
417
+ return { render };
342
418
  };
343
- export default AILogin;
419
+ export default useLogin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aio-login",
3
- "version": "8.0.3",
3
+ "version": "9.0.1",
4
4
  "description": "handle all types of popup and modals in react",
5
5
  "main": "index.js",
6
6
  "scripts": {