Skip to content

Commit df779bb

Browse files
author
Andrei Neagu
committed
Merge remote-tracking branch 'upstream/master' into pr-osparc-dy-sidecar-ports-migrate
2 parents 5bee321 + c6c1d60 commit df779bb

File tree

14 files changed

+334
-155
lines changed

14 files changed

+334
-155
lines changed

services/static-webserver/client/source/class/osparc/auth/core/BaseAuthPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ qx.Class.define("osparc.auth.core.BaseAuthPage", {
5656
},
5757

5858
statics: {
59-
FORM_WIDTH: 310
59+
FORM_WIDTH: 330
6060
},
6161

6262
/*

services/static-webserver/client/source/class/osparc/auth/ui/Login2FAValidationCodeView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ qx.Class.define("osparc.auth.ui.Login2FAValidationCodeView", {
8080
});
8181

8282
this.beautifyFormFields();
83-
const formRenderer = new qx.ui.form.renderer.SinglePlaceholder(this._form);
83+
const formRenderer = new osparc.ui.form.renderer.LoginSinglePlaceholder(this._form);
8484
this.add(formRenderer);
8585

8686
// buttons

services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,21 +75,25 @@ qx.Class.define("osparc.auth.ui.LoginView", {
7575
});
7676
email.getContentElement().setAttribute("autocomplete", "username");
7777
osparc.utils.Utils.setIdToWidget(email, "loginUserEmailFld");
78-
this._form.add(email, " Your email address", qx.util.Validate.email(), "email");
79-
this.addListener("appear", () => {
78+
this._form.add(email, " Email", qx.util.Validate.email(), "email");
79+
const focusEmail = () => {
8080
email.focus();
8181
email.activate();
82-
});
82+
};
83+
this.addListener("appear", () => {
84+
focusEmail();
85+
setTimeout(() => focusEmail(), 100); // refocus
86+
}, this);
8387

8488
const pass = new osparc.ui.form.PasswordField().set({
8589
required: true
8690
});
8791
pass.getChildControl("passwordField").getContentElement().setAttribute("autocomplete", "current-password");
8892
osparc.utils.Utils.setIdToWidget(pass.getChildControl("passwordField"), "loginPasswordFld");
89-
this._form.add(pass, " Your password", null, "password");
93+
this._form.add(pass, " Password", null, "password");
9094

9195
this.beautifyFormFields();
92-
const formRenderer = new qx.ui.form.renderer.SinglePlaceholder(this._form);
96+
const formRenderer = new osparc.ui.form.renderer.LoginSinglePlaceholder(this._form);
9397
this.add(formRenderer);
9498

9599
// buttons

services/static-webserver/client/source/class/osparc/auth/ui/RegistrationView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ qx.Class.define("osparc.auth.ui.RegistrationView", {
8585
validator.setValidator(() => osparc.auth.core.Utils.checkSamePasswords(password1, password2));
8686

8787
this.beautifyFormFields();
88-
const formRenderer = new qx.ui.form.renderer.SinglePlaceholder(this._form);
88+
const formRenderer = new osparc.ui.form.renderer.LoginSinglePlaceholder(this._form);
8989
this.add(formRenderer);
9090

9191
// buttons

services/static-webserver/client/source/class/osparc/auth/ui/RequestAccount.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ qx.Class.define("osparc.auth.ui.RequestAccount", {
7777
break;
7878
}
7979

80-
const phone = new qx.ui.form.TextField();
80+
const phone = new osparc.ui.form.IntlTelInput().set({
81+
compactField: true,
82+
});
8183
this._form.add(phone, this.tr("Phone Number"), null, "phone");
8284

8385

@@ -416,6 +418,9 @@ qx.Class.define("osparc.auth.ui.RequestAccount", {
416418
this.fireDataEvent("done");
417419
})
418420
.catch(err => {
421+
if ("errors" in err) {
422+
osparc.utils.Utils.errorsToForm(this._form, err.errors);
423+
}
419424
osparc.FlashMessenger.logError(err);
420425
this.__restartCaptcha();
421426
});

services/static-webserver/client/source/class/osparc/auth/ui/ResetPassRequestView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ qx.Class.define("osparc.auth.ui.ResetPassRequestView", {
4949
});
5050

5151
this.beautifyFormFields();
52-
const formRenderer = new qx.ui.form.renderer.SinglePlaceholder(this._form);
52+
const formRenderer = new osparc.ui.form.renderer.LoginSinglePlaceholder(this._form);
5353
this.add(formRenderer);
5454

5555
// buttons

services/static-webserver/client/source/class/osparc/auth/ui/ResetPassView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ qx.Class.define("osparc.auth.ui.ResetPassView", {
6363
});
6464

6565
this.beautifyFormFields();
66-
const formRenderer = new qx.ui.form.renderer.SinglePlaceholder(this._form);
66+
const formRenderer = new osparc.ui.form.renderer.LoginSinglePlaceholder(this._form);
6767
this.add(formRenderer);
6868

6969
// buttons

services/static-webserver/client/source/class/osparc/auth/ui/VerifyPhoneNumberView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ qx.Class.define("osparc.auth.ui.VerifyPhoneNumberView", {
6666
this.add(control);
6767
break;
6868
case "intl-tel-input":
69-
control = new osparc.widget.IntlTelInput();
69+
control = new osparc.ui.form.IntlTelInput();
7070
this.getChildControl("phone-number-layout").add(control, {
7171
flex: 1
7272
});

services/static-webserver/client/source/class/osparc/data/Resources.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,7 @@ qx.Class.define("osparc.data.Resources", {
16431643
let message = null;
16441644
let status = null;
16451645
let supportId = null;
1646+
let errors = [];
16461647
if (e.getData().error) {
16471648
const errorData = e.getData().error;
16481649
if (errorData.message) {
@@ -1652,7 +1653,7 @@ qx.Class.define("osparc.data.Resources", {
16521653
if (message === null && logs && logs.length) {
16531654
message = logs[0].message;
16541655
}
1655-
const errors = errorData.errors || [];
1656+
errors = errorData.errors || [];
16561657
if (message === null && errors && errors.length) {
16571658
message = errors[0].message;
16581659
}
@@ -1691,6 +1692,9 @@ qx.Class.define("osparc.data.Resources", {
16911692
if (status) {
16921693
err.status = status;
16931694
}
1695+
if (errors.length) {
1696+
err.errors = errors;
1697+
}
16941698
if (supportId) {
16951699
err.supportId = supportId;
16961700
}
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2022 IT'IS Foundation, https://itis.swiss
9+
10+
License:
11+
MIT: https://opensource.org/licenses/MIT
12+
13+
Authors:
14+
* Odei Maiz (odeimaiz)
15+
16+
************************************************************************ */
17+
18+
/* global intlTelInput */
19+
20+
/**
21+
* @ignore(intlTelInput)
22+
*/
23+
24+
qx.Class.define("osparc.ui.form.IntlTelInput", {
25+
extend: qx.ui.core.Widget,
26+
implement: [qx.ui.form.IForm, qx.ui.form.IStringForm],
27+
include: [qx.ui.form.MForm, qx.ui.form.MModelProperty],
28+
29+
construct: function() {
30+
this.base(arguments);
31+
32+
this.setFocusable(true);
33+
34+
this._setLayout(new qx.ui.layout.HBox());
35+
36+
this.getContentElement().setStyles({
37+
"overflow": "visible" // needed for countries dropdown menu
38+
});
39+
40+
const randId = Math.floor(Math.random() * 100);
41+
this.__htmlId = `phone-${randId}`;
42+
const html = `<input type='tel' id='${this.__htmlId}' name='phone' autocomplete='off'>`;
43+
const phoneNumber = this.getChildControl("phone-input-field");
44+
phoneNumber.setHtml(html);
45+
phoneNumber.addListenerOnce("appear", () => this.__convertInputToPhoneInput(), this);
46+
47+
const themeManager = qx.theme.manager.Meta.getInstance();
48+
themeManager.addListener("changeTheme", () => this.__updateStyle());
49+
},
50+
51+
properties: {
52+
// Form-compatible property
53+
value: {
54+
check: "String",
55+
nullable: true,
56+
event: "changeValue",
57+
apply: "_applyValue"
58+
},
59+
60+
compactField: {
61+
check: "Boolean",
62+
init: false,
63+
nullable: false,
64+
apply: "__updateStyle",
65+
}
66+
},
67+
68+
members: {
69+
__htmlId: null,
70+
__inputElement: null,
71+
__phoneInput: null,
72+
73+
_createChildControlImpl: function(id) {
74+
let control;
75+
switch (id) {
76+
case "phone-input-field":
77+
control = new qx.ui.embed.Html();
78+
this._add(control, { flex: 1 });
79+
break;
80+
}
81+
return control || this.base(arguments, id);
82+
},
83+
84+
// IStringForm interface implementation
85+
getValue: function() {
86+
return this.__phoneInput ? this.__phoneInput.getNumber() : null;
87+
},
88+
89+
setValue: function(value) {
90+
if (this.__phoneInput && value) {
91+
// intlTelInput doesn't have a full setter for raw numbers
92+
this.__phoneInput.setNumber(value);
93+
}
94+
this._applyValue(value);
95+
},
96+
97+
resetValue: function() {
98+
this.setValue(null);
99+
},
100+
// IStringForm interface implementation
101+
102+
// Make the widget tabbable/focusable
103+
focus: function() {
104+
if (this.__inputElement) {
105+
this.__inputElement.focus();
106+
} else {
107+
// fallback: let qooxdoo focus the content element
108+
this.base(arguments);
109+
}
110+
},
111+
112+
tabFocus: function() {
113+
this.focus();
114+
},
115+
116+
getFocusElement: function() {
117+
const phoneNumber = this.getChildControl("phone-input-field");
118+
// phoneNumber is a qx.ui.embed.Html, it has a ContentElement (qx.html.Element)
119+
return phoneNumber.getContentElement();
120+
},
121+
// Make the widget tabbable/focusable
122+
123+
_applyValue: function(value) {
124+
this.fireDataEvent("changeValue", value);
125+
},
126+
127+
validate: function() {
128+
return this.isValidNumber();
129+
},
130+
131+
isValidNumber: function() {
132+
return this.__phoneInput ? this.__phoneInput.isValidNumber() : false;
133+
},
134+
135+
verifyPhoneNumber: function() {
136+
if (this.isValidNumber()) {
137+
this.setValid(true);
138+
} else {
139+
this.setValid(false);
140+
const validationError = this.__phoneInput.getValidationError();
141+
const errorMap = {
142+
0: this.tr("Invalid number"),
143+
1: this.tr("Invalid country code"),
144+
2: this.tr("Number too short"),
145+
3: this.tr("Number too long")
146+
};
147+
const errorMsg = errorMap[validationError] || this.tr("Invalid number");
148+
this.setInvalidMessage(errorMsg);
149+
}
150+
this.__updateStyle();
151+
},
152+
153+
__updateStyle: function() {
154+
const textColor = qx.theme.manager.Color.getInstance().resolve("text");
155+
const bgColor = qx.theme.manager.Color.getInstance().resolve("input_background");
156+
const productColor = qx.theme.manager.Color.getInstance().resolve("product-color");
157+
const defaultBottomBorder = qx.theme.manager.Color.getInstance().resolve("default-button-active");
158+
document.documentElement.style.setProperty('--country-list-dropdown-bg', bgColor);
159+
document.documentElement.style.setProperty('--country-list-dropdown-text', textColor);
160+
document.documentElement.style.setProperty('--tel-border-bottom-color', defaultBottomBorder);
161+
document.documentElement.style.setProperty('--tel-border-bottom-color-focused', productColor);
162+
163+
const isCompact = this.isCompactField();
164+
const phoneInputField = this.getChildControl("phone-input-field");
165+
const width = isCompact ? 152 : 223;
166+
const height = isCompact ? 26 : 30;
167+
168+
phoneInputField.set({
169+
maxWidth: width,
170+
maxHeight: height,
171+
margin: 0,
172+
});
173+
174+
const phoneInput = this.__phoneInput;
175+
if (phoneInput) {
176+
phoneInput.a.style["width"] = width + "px";
177+
phoneInput.a.style["height"] = height + "px";
178+
phoneInput.a.style["borderWidth"] = "0px";
179+
phoneInput.a.style["backgroundColor"] = isCompact ? "transparent" : bgColor;
180+
phoneInput.a.style["color"] = textColor;
181+
182+
if (this.getValue() && !this.isValidNumber()) {
183+
const errorColor = qx.theme.manager.Color.getInstance().resolve("failed-red");
184+
document.documentElement.style.setProperty('--tel-border-bottom-color', errorColor);
185+
}
186+
}
187+
},
188+
189+
__convertInputToPhoneInput: function() {
190+
const convertInputToPhoneInput = () => {
191+
const domElement = document.querySelector(`#${this.__htmlId}`);
192+
this.__inputElementToPhoneInput(domElement);
193+
const phoneNumber = this.getChildControl("phone-input-field");
194+
phoneNumber.getContentElement().setStyles({
195+
"overflow": "visible" // needed for countries dropdown menu
196+
});
197+
this.__updateStyle();
198+
};
199+
200+
const intlTelInputLib = osparc.wrapper.IntlTelInput.getInstance();
201+
if (intlTelInputLib.getLibReady()) {
202+
convertInputToPhoneInput();
203+
} else {
204+
intlTelInputLib.addListenerOnce("changeLibReady", e => {
205+
if (e.getData()) {
206+
convertInputToPhoneInput();
207+
}
208+
});
209+
}
210+
},
211+
212+
__inputElementToPhoneInput: function(domElement) {
213+
this.__inputElement = domElement; // keep reference to raw <input>
214+
this.__phoneInput = intlTelInput(domElement, {
215+
initialCountry: "auto",
216+
geoIpLookup: callback => {
217+
fetch("https://ipapi.co/json")
218+
.then(res => res.json())
219+
.then(data => callback(data.country_code))
220+
.catch(() => callback("ch"));
221+
},
222+
preferredCountries: [],
223+
dropdownContainer: document.body,
224+
});
225+
226+
// Trigger validation on blur
227+
domElement.addEventListener("blur", () => this.verifyPhoneNumber());
228+
229+
this.__updateStyle();
230+
}
231+
}
232+
});

0 commit comments

Comments
 (0)