@nu-art/user-account-frontend 0.400.5
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/_ats/account-editor/ATS_AccountEditor.d.ts +15 -0
- package/_ats/account-editor/ATS_AccountEditor.js +45 -0
- package/_ats/account-editor/ATS_AccountEditor.scss +127 -0
- package/_ats_session-data/ATS_SessionData.d.ts +18 -0
- package/_ats_session-data/ATS_SessionData.js +40 -0
- package/_entity/account/ModuleFE_Account.d.ts +47 -0
- package/_entity/account/ModuleFE_Account.js +153 -0
- package/_entity/account/consts.d.ts +8 -0
- package/_entity/account/consts.js +7 -0
- package/_entity/account/index.d.ts +3 -0
- package/_entity/account/index.js +3 -0
- package/_entity/account/module-pack.d.ts +2 -0
- package/_entity/account/module-pack.js +3 -0
- package/_entity/session/ModuleFE_Session.d.ts +33 -0
- package/_entity/session/ModuleFE_Session.js +95 -0
- package/_entity.d.ts +3 -0
- package/_entity.js +3 -0
- package/account-editor/Component_AccountEditor.d.ts +38 -0
- package/account-editor/Component_AccountEditor.js +134 -0
- package/account-editor/Component_AccountEditor.scss +139 -0
- package/consts.d.ts +1 -0
- package/consts.js +1 -0
- package/index.d.ts +9 -0
- package/index.js +26 -0
- package/module-pack.d.ts +2 -0
- package/module-pack.js +21 -0
- package/package.json +81 -0
- package/ui/Component_AccountThumbnail/Component_AccountThumbnail.d.ts +14 -0
- package/ui/Component_AccountThumbnail/Component_AccountThumbnail.js +59 -0
- package/ui/Component_AccountThumbnail/Component_AccountThumbnail.scss +66 -0
- package/ui/Component_ChangePassword/Component_ChangePassword.d.ts +17 -0
- package/ui/Component_ChangePassword/Component_ChangePassword.js +52 -0
- package/ui/Component_GoogleSAMLLogin/Component_GoogleSAMLLogin.d.ts +6 -0
- package/ui/Component_GoogleSAMLLogin/Component_GoogleSAMLLogin.js +16 -0
- package/ui/Component_GoogleSAMLLogin/Component_GoogleSAMLLogin.scss +20 -0
- package/ui/Component_Login/Component_Login.d.ts +23 -0
- package/ui/Component_Login/Component_Login.js +124 -0
- package/ui/Component_Login/Component_Login.scss +66 -0
- package/ui/Component_Login/index.d.ts +1 -0
- package/ui/Component_Login/index.js +18 -0
- package/ui/Component_LoginBlocked/Component_LoginBlocked.d.ts +19 -0
- package/ui/Component_LoginBlocked/Component_LoginBlocked.js +35 -0
- package/ui/Component_LoginBlocked/Component_LoginBlocked.scss +3 -0
- package/ui/Component_Register.d.ts +26 -0
- package/ui/Component_Register.js +152 -0
- package/ui/PopUp_AccountMenu/PopUp_AccountMenu.d.ts +62 -0
- package/ui/PopUp_AccountMenu/PopUp_AccountMenu.js +130 -0
- package/ui/PopUp_AccountMenu/PopUp_AccountMenu.scss +167 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
@use '@nu-art/ts-styles' as styles;
|
|
2
|
+
|
|
3
|
+
.ts-account__saml-button {
|
|
4
|
+
|
|
5
|
+
&[data-variant="primary"] {
|
|
6
|
+
height: 40px;
|
|
7
|
+
width: 100%;
|
|
8
|
+
margin: 0;
|
|
9
|
+
gap: 16px;
|
|
10
|
+
border: 2px solid #006fab;
|
|
11
|
+
@include styles.mouse-interactive-background(transparent);
|
|
12
|
+
@include styles.mouse-interactive-border-color(styles.dark-blue(2), styles.dark-blue(1));
|
|
13
|
+
|
|
14
|
+
.ts-account__saml-button__icon {
|
|
15
|
+
width: 20px;
|
|
16
|
+
height: 20px;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Account_Login, AccountEmail, AccountPassword } from '@nu-art/user-account-shared';
|
|
2
|
+
import './Component_Login.scss';
|
|
3
|
+
import { ComponentSync } from '@nu-art/thunderstorm-frontend/index';
|
|
4
|
+
type State<T> = {
|
|
5
|
+
data: Partial<T>;
|
|
6
|
+
errorMessages?: string[];
|
|
7
|
+
blockedUntil?: number;
|
|
8
|
+
submitting: boolean;
|
|
9
|
+
};
|
|
10
|
+
type Props = {};
|
|
11
|
+
export declare class Component_Login extends ComponentSync<Props, State<Account_Login['request']>> {
|
|
12
|
+
protected deriveStateFromProps(nextProps: Props, state: State<Account_Login['request']>): State<AccountEmail & {
|
|
13
|
+
deviceId: string;
|
|
14
|
+
} & AccountPassword>;
|
|
15
|
+
private loginDataValid;
|
|
16
|
+
private login;
|
|
17
|
+
private onValueChanged;
|
|
18
|
+
render(): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
private renderErrorMessages;
|
|
20
|
+
private renderAccountBlockedTimer;
|
|
21
|
+
private errorRenderer;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* User secured registration and login management system..
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { _keys, exists, formatTimestamp } from '@nu-art/ts-common';
|
|
20
|
+
import { ErrorType_LoginBlocked } from '@nu-art/user-account-shared';
|
|
21
|
+
import './Component_Login.scss';
|
|
22
|
+
import { Button, ComponentSync, LL_H_C, LL_V_C, ModuleFE_Toaster, TS_PropRenderer } from '@nu-art/thunderstorm-frontend/index';
|
|
23
|
+
import { ModuleFE_Account, StorageKey_DeviceId } from '../../_entity.js';
|
|
24
|
+
import { TS_InputV2 } from '@nu-art/thunderstorm-frontend/components/TS_V2_Input/index';
|
|
25
|
+
import { Component_LoginBlocked } from '../Component_LoginBlocked/Component_LoginBlocked.js';
|
|
26
|
+
const form = {
|
|
27
|
+
email: {
|
|
28
|
+
type: 'text',
|
|
29
|
+
hint: 'email',
|
|
30
|
+
label: 'Email',
|
|
31
|
+
},
|
|
32
|
+
password: {
|
|
33
|
+
type: 'password',
|
|
34
|
+
hint: '****',
|
|
35
|
+
label: 'Password',
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export class Component_Login extends ComponentSync {
|
|
39
|
+
//######################### Life Cycle #########################
|
|
40
|
+
deriveStateFromProps(nextProps, state) {
|
|
41
|
+
state.data ??= {};
|
|
42
|
+
return state;
|
|
43
|
+
}
|
|
44
|
+
//######################### Logic #########################
|
|
45
|
+
loginDataValid = () => {
|
|
46
|
+
const data = this.state.data;
|
|
47
|
+
const errors = _keys(form).map(key => {
|
|
48
|
+
const field = form[key];
|
|
49
|
+
return data[key] ? undefined : ` * missing ${field.label}`;
|
|
50
|
+
}).filter(error => !!error);
|
|
51
|
+
if (errors.length > 0) {
|
|
52
|
+
ModuleFE_Toaster.toastError(`Wrong input:\n${errors.join('\n')}`);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
};
|
|
57
|
+
login = async () => {
|
|
58
|
+
//Fail fast if already submitted login
|
|
59
|
+
if (this.state.submitting)
|
|
60
|
+
return;
|
|
61
|
+
//Fail fast if login is blocked
|
|
62
|
+
if (exists(this.state.blockedUntil))
|
|
63
|
+
return;
|
|
64
|
+
//Fail fast if login data is not valid
|
|
65
|
+
if (!this.loginDataValid())
|
|
66
|
+
return;
|
|
67
|
+
this.setState({ submitting: true, errorMessages: undefined }, async () => {
|
|
68
|
+
try {
|
|
69
|
+
await ModuleFE_Account._v1.login({ ...this.state.data, deviceId: StorageKey_DeviceId.get() })
|
|
70
|
+
.executeSync();
|
|
71
|
+
this.setState({ submitting: false });
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err.errorResponse.error?.type === ErrorType_LoginBlocked) {
|
|
75
|
+
const blockedUntil = err.errorResponse.error.data.blockedUntil;
|
|
76
|
+
return this.setState({
|
|
77
|
+
blockedUntil: blockedUntil,
|
|
78
|
+
errorMessages: [`Login blocked until ${formatTimestamp('DD/MM/YYYY HH:mm', blockedUntil)}`],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
this.setState({ errorMessages: ['Email or password incorrect'], submitting: false });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
onValueChanged = (value, id, isAccept = false) => {
|
|
86
|
+
const data = { ...this.state.data };
|
|
87
|
+
data[id] = value;
|
|
88
|
+
this.setState({ data, errorMessages: undefined }, () => {
|
|
89
|
+
if (isAccept)
|
|
90
|
+
this.login();
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
//######################### Render #########################
|
|
94
|
+
render() {
|
|
95
|
+
const data = this.state.data;
|
|
96
|
+
return _jsxs(LL_V_C, { className: "ts-account__authenticate", children: [_keys(form).map((key, i) => {
|
|
97
|
+
const field = form[key];
|
|
98
|
+
return _jsx(TS_PropRenderer.Vertical, { label: field.label, children: _jsx(TS_InputV2, { id: key, value: data[key], type: field.type, allowAccept: true, onChange: (value) => {
|
|
99
|
+
this.onValueChanged(value, key);
|
|
100
|
+
}, onAccept: (value) => {
|
|
101
|
+
this.onValueChanged(value, key, true);
|
|
102
|
+
} }) }, i);
|
|
103
|
+
}), _jsx(LL_H_C, { className: 'ts-account__error-container', children: this.errorRenderer() }), _jsx(Button, { variant: 'tertiary', className: `ts-account__action-button`, actionInProgress: this.state.submitting, disabled: exists(this.state.blockedUntil), onClick: this.login, children: "Login" })] });
|
|
104
|
+
}
|
|
105
|
+
renderErrorMessages = () => {
|
|
106
|
+
if (!this.state.errorMessages?.length)
|
|
107
|
+
return '';
|
|
108
|
+
return _jsx("ul", { className: 'ts-account__error-messages', children: this.state.errorMessages.map((message, i) => {
|
|
109
|
+
return _jsx("li", { children: message }, i);
|
|
110
|
+
}) });
|
|
111
|
+
};
|
|
112
|
+
renderAccountBlockedTimer = () => {
|
|
113
|
+
if (!exists(this.state.blockedUntil))
|
|
114
|
+
return;
|
|
115
|
+
return _jsx(Component_LoginBlocked, { hide: () => this.setState({ blockedUntil: undefined }), blockedUntil: this.state.blockedUntil });
|
|
116
|
+
};
|
|
117
|
+
errorRenderer = () => {
|
|
118
|
+
if (!this.state.errorMessages && !this.state.blockedUntil)
|
|
119
|
+
return;
|
|
120
|
+
if (this.state.blockedUntil)
|
|
121
|
+
return this.renderAccountBlockedTimer();
|
|
122
|
+
return this.renderErrorMessages();
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* User secured registration and login management system..
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
@use '@nu-art/ts-styles' as styles;
|
|
20
|
+
|
|
21
|
+
.ts-account__authenticate {
|
|
22
|
+
margin-top: 6px;
|
|
23
|
+
width: 300px;
|
|
24
|
+
|
|
25
|
+
> input {
|
|
26
|
+
margin-block: 4px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ts-account__error-container {
|
|
30
|
+
justify-content: center;
|
|
31
|
+
align-items: center;
|
|
32
|
+
min-height: 22px;
|
|
33
|
+
|
|
34
|
+
.ts-account__error-messages {
|
|
35
|
+
margin-block: 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.ts-account__authenticate__password-rules {
|
|
40
|
+
border: 1px solid black;
|
|
41
|
+
border-radius: 10px;
|
|
42
|
+
width: 100%;
|
|
43
|
+
padding: 10px;
|
|
44
|
+
grid-template-columns: 1fr 1fr;
|
|
45
|
+
gap: 10px;
|
|
46
|
+
margin-top: 10px;
|
|
47
|
+
|
|
48
|
+
.ts-account__authenticate__password-rule {
|
|
49
|
+
gap: 10px;
|
|
50
|
+
align-items: center;
|
|
51
|
+
color: styles.red(3);
|
|
52
|
+
|
|
53
|
+
.icon--wrapper {
|
|
54
|
+
@include styles.mouse-interactive-icon(styles.red(3));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&.valid {
|
|
58
|
+
color: styles.green(2);
|
|
59
|
+
|
|
60
|
+
.icon--wrapper {
|
|
61
|
+
@include styles.mouse-interactive-icon(styles.green(2));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Component_Login.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* User secured registration and login management system..
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
export * from './Component_Login.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ComponentSync } from '@nu-art/thunderstorm-frontend/index';
|
|
2
|
+
type Props = {
|
|
3
|
+
blockedUntil: number;
|
|
4
|
+
hide: VoidFunction;
|
|
5
|
+
};
|
|
6
|
+
type State = {
|
|
7
|
+
blockedUntil: number;
|
|
8
|
+
timeLeft: number;
|
|
9
|
+
};
|
|
10
|
+
export declare class Component_LoginBlocked extends ComponentSync<Props, State> {
|
|
11
|
+
private timerInterval;
|
|
12
|
+
componentDidMount(): void;
|
|
13
|
+
componentWillUnmount(): void;
|
|
14
|
+
protected deriveStateFromProps(nextProps: Props, state: State): State;
|
|
15
|
+
private updateTimeLeft;
|
|
16
|
+
private renderBlockedTimer;
|
|
17
|
+
render(): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ComponentSync, LL_H_C } from '@nu-art/thunderstorm-frontend/index';
|
|
3
|
+
import { currentTimeMillis, Second } from '@nu-art/ts-common';
|
|
4
|
+
export class Component_LoginBlocked extends ComponentSync {
|
|
5
|
+
timerInterval;
|
|
6
|
+
componentDidMount() {
|
|
7
|
+
this.timerInterval = setInterval(this.updateTimeLeft, Second);
|
|
8
|
+
}
|
|
9
|
+
componentWillUnmount() {
|
|
10
|
+
clearInterval(this.timerInterval);
|
|
11
|
+
}
|
|
12
|
+
deriveStateFromProps(nextProps, state) {
|
|
13
|
+
state.blockedUntil = nextProps.blockedUntil;
|
|
14
|
+
state.timeLeft = state.blockedUntil - currentTimeMillis();
|
|
15
|
+
return state;
|
|
16
|
+
}
|
|
17
|
+
updateTimeLeft = () => {
|
|
18
|
+
const newTimeLeft = this.state.blockedUntil - currentTimeMillis();
|
|
19
|
+
if (newTimeLeft <= 0) {
|
|
20
|
+
clearInterval(this.timerInterval);
|
|
21
|
+
return this.props.hide();
|
|
22
|
+
}
|
|
23
|
+
this.setState({ timeLeft: newTimeLeft });
|
|
24
|
+
};
|
|
25
|
+
renderBlockedTimer = () => {
|
|
26
|
+
const totalSeconds = Math.floor(this.state.timeLeft / 1000);
|
|
27
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
28
|
+
const seconds = totalSeconds % 60;
|
|
29
|
+
const formattedSeconds = seconds < 10 ? `0${seconds}` : seconds;
|
|
30
|
+
return `${minutes}:${formattedSeconds}`;
|
|
31
|
+
};
|
|
32
|
+
render() {
|
|
33
|
+
return _jsxs(LL_H_C, { className: 'login-blocked', children: ["Login blocked, try again in: ", this.renderBlockedTimer()] });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ComponentSync } from '@nu-art/thunderstorm-frontend/index';
|
|
2
|
+
import { PasswordAssertionConfig, PasswordFailureReport, Request_RegisterAccount } from '@nu-art/user-account-shared';
|
|
3
|
+
type State<T> = {
|
|
4
|
+
data: Partial<T>;
|
|
5
|
+
errorMessages?: string[];
|
|
6
|
+
renderPasswordRules: boolean;
|
|
7
|
+
passwordFailureReport?: PasswordFailureReport;
|
|
8
|
+
passwordAssertionConfig?: PasswordAssertionConfig;
|
|
9
|
+
submitting: boolean;
|
|
10
|
+
};
|
|
11
|
+
type Props<T> = {
|
|
12
|
+
validate?: (data: Partial<T>) => string | undefined;
|
|
13
|
+
renderPasswordRules?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare class Component_Register extends ComponentSync<Props<Request_RegisterAccount>, State<Request_RegisterAccount>> {
|
|
16
|
+
protected deriveStateFromProps(nextProps: Props<Request_RegisterAccount>, state: State<Request_RegisterAccount>): State<Request_RegisterAccount>;
|
|
17
|
+
private getPasswordRuleText;
|
|
18
|
+
private onValueChanged;
|
|
19
|
+
private defaultFormStateUpdateCallback;
|
|
20
|
+
private canSubmit;
|
|
21
|
+
private registerClicked;
|
|
22
|
+
render(): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
private renderErrorMessages;
|
|
24
|
+
private renderPasswordRules;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* User secured registration and login management system..
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { _className, Button, ComponentSync, Grid, LL_H_C, LL_V_C, TS_PropRenderer } from '@nu-art/thunderstorm-frontend/index';
|
|
20
|
+
import { _keys, addItemToArray, filterInstances } from '@nu-art/ts-common';
|
|
21
|
+
import { assertPasswordRules, PasswordAssertionType_LowerCaseLetters, PasswordAssertionType_MaxLength, PasswordAssertionType_MinLength, PasswordAssertionType_Numbers, PasswordAssertionType_SpecialChars } from '@nu-art/user-account-shared';
|
|
22
|
+
import { ModuleFE_Account, StorageKey_DeviceId } from '../_entity.js';
|
|
23
|
+
import { TS_Icons } from '@nu-art/ts-styles';
|
|
24
|
+
import { TS_InputV2 } from '@nu-art/thunderstorm-frontend/components/TS_V2_Input/index';
|
|
25
|
+
const form = {
|
|
26
|
+
email: {
|
|
27
|
+
className: '',
|
|
28
|
+
type: 'text',
|
|
29
|
+
hint: 'email',
|
|
30
|
+
label: 'Email',
|
|
31
|
+
},
|
|
32
|
+
password: {
|
|
33
|
+
type: 'password',
|
|
34
|
+
hint: '****',
|
|
35
|
+
label: 'Password',
|
|
36
|
+
},
|
|
37
|
+
passwordCheck: {
|
|
38
|
+
type: 'password',
|
|
39
|
+
hint: '****',
|
|
40
|
+
label: 'Password Check',
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
export class Component_Register extends ComponentSync {
|
|
44
|
+
// ######################### Lifecycle #########################
|
|
45
|
+
deriveStateFromProps(nextProps, state) {
|
|
46
|
+
state.data ??= {};
|
|
47
|
+
state.renderPasswordRules = nextProps.renderPasswordRules ?? false;
|
|
48
|
+
if (state.renderPasswordRules) {
|
|
49
|
+
state.passwordAssertionConfig = ModuleFE_Account.getPasswordAssertionConfig();
|
|
50
|
+
state.passwordFailureReport = assertPasswordRules(state.data.password ?? '', state.passwordAssertionConfig);
|
|
51
|
+
}
|
|
52
|
+
return state;
|
|
53
|
+
}
|
|
54
|
+
// ######################### Logic #########################
|
|
55
|
+
getPasswordRuleText = (key) => {
|
|
56
|
+
const amount = this.state.passwordAssertionConfig?.[key];
|
|
57
|
+
switch (key) {
|
|
58
|
+
case PasswordAssertionType_LowerCaseLetters:
|
|
59
|
+
return `At least ${amount} lower case letters`;
|
|
60
|
+
case 'capital-letters':
|
|
61
|
+
return `At least ${amount} capital letters`;
|
|
62
|
+
case PasswordAssertionType_MaxLength:
|
|
63
|
+
return `No more than ${amount} characters`;
|
|
64
|
+
case PasswordAssertionType_MinLength:
|
|
65
|
+
return `At least ${amount} characters`;
|
|
66
|
+
case PasswordAssertionType_Numbers:
|
|
67
|
+
return `At least ${amount} numbers`;
|
|
68
|
+
case PasswordAssertionType_SpecialChars:
|
|
69
|
+
return `At least ${amount} special characters`;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
onValueChanged = (value, id, onAccept = false) => {
|
|
73
|
+
const data = { ...this.state.data };
|
|
74
|
+
data[id] = value;
|
|
75
|
+
if (id !== 'password')
|
|
76
|
+
return this.setState({ data, errorMessages: undefined }, () => this.defaultFormStateUpdateCallback(onAccept));
|
|
77
|
+
const passwordFailureReport = assertPasswordRules(value, this.state.passwordAssertionConfig);
|
|
78
|
+
this.setState({
|
|
79
|
+
data,
|
|
80
|
+
errorMessages: undefined,
|
|
81
|
+
passwordFailureReport
|
|
82
|
+
}, () => this.defaultFormStateUpdateCallback(onAccept));
|
|
83
|
+
};
|
|
84
|
+
defaultFormStateUpdateCallback = (onAccept) => {
|
|
85
|
+
if (!onAccept)
|
|
86
|
+
return;
|
|
87
|
+
if (!this.canSubmit())
|
|
88
|
+
return;
|
|
89
|
+
this.setState({ submitting: true }, async () => {
|
|
90
|
+
await this.registerClicked();
|
|
91
|
+
this.setState({ submitting: false });
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
canSubmit = () => {
|
|
95
|
+
if (this.state.passwordFailureReport)
|
|
96
|
+
return;
|
|
97
|
+
const data = this.state.data;
|
|
98
|
+
const errors = filterInstances(_keys(form).map(key => {
|
|
99
|
+
const field = form[key];
|
|
100
|
+
return data[key] ? undefined : `missing ${field.label}`;
|
|
101
|
+
}));
|
|
102
|
+
const validateError = this.props.validate && this.props.validate(data);
|
|
103
|
+
if (validateError)
|
|
104
|
+
addItemToArray(errors, validateError);
|
|
105
|
+
if (errors.length > 0) {
|
|
106
|
+
this.setState({ errorMessages: errors });
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
};
|
|
111
|
+
registerClicked = async () => {
|
|
112
|
+
if (!this.canSubmit())
|
|
113
|
+
return;
|
|
114
|
+
try {
|
|
115
|
+
await ModuleFE_Account._v1.registerAccount({
|
|
116
|
+
...this.state.data,
|
|
117
|
+
deviceId: StorageKey_DeviceId.get()
|
|
118
|
+
}).executeSync();
|
|
119
|
+
}
|
|
120
|
+
catch (_err) {
|
|
121
|
+
const err = _err;
|
|
122
|
+
this.setState({ errorMessages: [err.message] });
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
// ######################### Render #########################
|
|
126
|
+
render() {
|
|
127
|
+
const data = this.state.data;
|
|
128
|
+
return _jsxs(LL_V_C, { className: "ts-account__authenticate", children: [_keys(form).map((key, i) => {
|
|
129
|
+
const field = form[key];
|
|
130
|
+
return _jsxs(TS_PropRenderer.Vertical, { label: field.label, children: [_jsx(TS_InputV2, { id: key, value: data[key], type: field.type, allowAccept: true, onChange: (data) => this.onValueChanged(data, key), onAccept: (data) => this.onValueChanged(data, key, true) }), key === 'password' && this.renderPasswordRules()] }, i);
|
|
131
|
+
}), this.renderErrorMessages(), _jsx(Button, { onClick: this.registerClicked, actionInProgress: this.state.submitting, className: `clickable ts-account__action-button`, disabled: !!this.state.passwordFailureReport, children: "Register" })] });
|
|
132
|
+
}
|
|
133
|
+
renderErrorMessages = () => {
|
|
134
|
+
if (!this.state.errorMessages?.length)
|
|
135
|
+
return '';
|
|
136
|
+
return _jsx("ul", { className: 'ts-account__error-messages', children: this.state.errorMessages.map((message, i) => {
|
|
137
|
+
return _jsx("li", { children: message }, i);
|
|
138
|
+
}) });
|
|
139
|
+
};
|
|
140
|
+
renderPasswordRules = () => {
|
|
141
|
+
if (!this.state.renderPasswordRules || !this.state.data.password)
|
|
142
|
+
return;
|
|
143
|
+
const assertionKeys = _keys(this.state.passwordAssertionConfig ?? {});
|
|
144
|
+
if (!assertionKeys.length)
|
|
145
|
+
return;
|
|
146
|
+
return _jsx(Grid, { className: 'ts-account__authenticate__password-rules', children: assertionKeys.map(assertionKey => {
|
|
147
|
+
const isFulfilled = !this.state.passwordFailureReport?.[assertionKey];
|
|
148
|
+
const className = _className('ts-account__authenticate__password-rule', isFulfilled && 'valid');
|
|
149
|
+
return _jsxs(LL_H_C, { className: className, children: [isFulfilled ? _jsx(TS_Icons.v.component, {}) : _jsx(TS_Icons.x.component, {}), this.getPasswordRuleText(assertionKey)] });
|
|
150
|
+
}) });
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ComponentSync } from '@nu-art/thunderstorm-frontend/index';
|
|
3
|
+
import { ResolvableContent, TypedMap, UniqueId } from '@nu-art/ts-common';
|
|
4
|
+
import { DB_Account } from '@nu-art/user-account-shared';
|
|
5
|
+
import './PopUp_AccountMenu.scss';
|
|
6
|
+
type ModalProps = {
|
|
7
|
+
modalPos?: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
};
|
|
11
|
+
offset?: {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
type Props = {
|
|
17
|
+
accountId: UniqueId;
|
|
18
|
+
accountDisplayModifier?: (account: DB_Account) => string | undefined;
|
|
19
|
+
acronymComposer?: (accountId: UniqueId) => string | undefined;
|
|
20
|
+
menuActions?: ResolvableContent<PopUp_AccountMenu_Action>[];
|
|
21
|
+
};
|
|
22
|
+
type State = {
|
|
23
|
+
account?: DB_Account;
|
|
24
|
+
menuActions?: PopUp_AccountMenu_Action[];
|
|
25
|
+
pageKey?: string;
|
|
26
|
+
prevPageKey?: string;
|
|
27
|
+
pageMap: TypedMap<PopUp_AccountMenu_Action_ContentFunction>;
|
|
28
|
+
};
|
|
29
|
+
export type PopUp_AccountMenu_Action_ContentFunction = (account: DB_Account, leaveMenuTrigger: VoidFunction) => React.ReactNode;
|
|
30
|
+
export type PopUp_AccountMenu_Action = {
|
|
31
|
+
label: string;
|
|
32
|
+
className?: string;
|
|
33
|
+
id?: string;
|
|
34
|
+
} & ({
|
|
35
|
+
type: 'page';
|
|
36
|
+
pageKey: string;
|
|
37
|
+
content: PopUp_AccountMenu_Action_ContentFunction;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'action';
|
|
40
|
+
closePopUp?: boolean;
|
|
41
|
+
action: () => (Promise<void> | void);
|
|
42
|
+
});
|
|
43
|
+
export declare class PopUp_AccountMenu extends ComponentSync<Props, State> {
|
|
44
|
+
static show(e: React.MouseEvent, props: Props, modalProps?: ModalProps): void;
|
|
45
|
+
static Action_AppToolsButton: (url: string) => PopUp_AccountMenu_Action;
|
|
46
|
+
static Action_EditPassword: PopUp_AccountMenu_Action;
|
|
47
|
+
static defaultProps: Partial<Props>;
|
|
48
|
+
protected deriveStateFromProps(nextProps: Props, state: State): State;
|
|
49
|
+
private closePopUp;
|
|
50
|
+
private logOut;
|
|
51
|
+
private onActionClick;
|
|
52
|
+
private closeCustomPage;
|
|
53
|
+
render(): import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
private renderNoAccount;
|
|
55
|
+
private renderAccount;
|
|
56
|
+
private renderSeparatorBar;
|
|
57
|
+
private renderHeader;
|
|
58
|
+
private renderBody;
|
|
59
|
+
private renderFooter;
|
|
60
|
+
private renderCustomPage;
|
|
61
|
+
}
|
|
62
|
+
export {};
|