rebuilding register

This commit is contained in:
eraden 2022-07-28 08:06:45 +02:00
parent 853e5050a3
commit d2a00dfc79
15 changed files with 432 additions and 222 deletions

View File

@ -15,14 +15,14 @@ import "./local-businesses/local-business.js";
import "./login-form.js";
import "./register-form.js";
import "./register-form/register-basic-form";
import "./register-form/register-business-account-form";
import "./register-form/register-item-form-row.js";
import "./register-form/register-items-form.js";
import "./register-form/register-business-form.js";
import "./register-form/register-user-type.js";
import "./register-form/register-user-form.js";
import "./register-form/register-business-contacts.js";
import "./register-form/register-submit-form.js";
import "./register-form/register-business-items-form.js";
import "./register-form/register-business-details-form.js";
import "./register-form/register-account-type.js";
import "./register-form/register-user-account-form.js";
import "./register-form/register-business-contact-form.js";
import "./register-form/register-business-submit-form.js";
import "./business-items/business-item.js";
import "./business-items/business-item-editor.js";
@ -64,3 +64,15 @@ if (!document.querySelector('#facebook-jssdk')) {
js.src = "https://connect.facebook.net/en_US/sdk.js";
document.head.appendChild(js);
}
Object.prototype.entry = function (key) {
const owner = this;
return {
owner, orElse(v) {
if (owner[key] === undefined) owner[key] = v;
return owner[key];
}, get() {
return owner[key];
}
}
};

View File

@ -1,6 +1,19 @@
import { FORM_STYLE, Component } from "./shared.js";
import { RegisterForm } from "./register-form/model.js";
/*
<article>
<register-user-type id="step-0"> </register-user-type>
<register-basic-form id="step-1"></register-basic-form>
<register-business-form id="step-2"></register-business-form>
<register-items-form id="step-3"></register-items-form>
<register-business-contacts id="step-4"></register-business-contacts>
<register-submit-form id="step-5"></register-submit-form>
<register-user-form id="step-40"></register-user-form>
</article>
*/
customElements.define('register-form', class extends Component {
#form = new RegisterForm;
@ -47,57 +60,54 @@ customElements.define('register-form', class extends Component {
</style>
<article id="host">
</article>
<article>
<register-user-type id="step-0"> </register-user-type>
<register-basic-form id="step-1"></register-basic-form>
<register-business-form id="step-2"></register-business-form>
<register-items-form id="step-3"></register-items-form>
<register-submit-form id="step-4"></register-submit-form>
<register-user-form id="step-40"></register-user-form>
</article>
`);
const finalForm = this.shadowRoot.querySelector('#step-4');
this.shadowRoot.addEventListener('account:type:user', ev => {
ev.stopPropagation();
this.#form.account_type = 'User';
finalForm.accountType = 'User';
this.step = 40;
});
this.shadowRoot.addEventListener('account:type:local-service', ev => {
ev.stopPropagation();
this.#form.account_type = 'Business';
finalForm.accountType = 'Business';
this.step = 1;
});
this.shadowRoot.addEventListener('form:next', ev => {
ev.stopPropagation();
const form = this.shadowRoot.querySelector(`#step-${ this.step }`);
if (this.#copyForm(form, finalForm)) {
this.step = this.step + 1;
}
this.step = this.step + 1;
});
this.shadowRoot.addEventListener('form:prev', ev => {
ev.stopPropagation();
this.step = this.step - 1;
});
this.addEventListener('account:basic', ev => {
console.log(ev);
console.log(ev.detail);
for (const [key, value] of Object.entries(ev.detail)) {
this.#form[key] = value;
}
console.log(this.#form, this.#form.payload);
});
finalForm.addEventListener('submit', ev => {
ev.preventDefault();
this.shadowRoot.addEventListener('account:type', ev => {
ev.stopPropagation();
this.#form.account_type = ev.detail;
if (this.#form.account_type === 'User') {
this.#showUserAccountForm();
} else {
this.#showBusinessAccountForm();
}
});
this.addEventListener('account:basic', ev => {
this.#copyDetail(ev)
this.#showBusinessDetailsForm();
});
this.addEventListener('account:business', ev => {
this.#copyDetail(ev)
});
this.addEventListener('account:items', ev => {
const items = Object.entries(ev.detail).reduce((items, [fieldName, value]) => {
const m = fieldName.match(/(items)\[(\d+)]\[(\w+)]/);
if (!Array.isArray(m)) return items;
const [_full, _items, n, name] = m;
items.entry(n).orElse({})[name] = value;
return items;
}, {});
this.#form.items = Array.from(Object.values(items));
});
}
#copyDetail(ev) {
ev.stopPropagation();
for (const [key, value] of Object.entries(ev.detail)) {
this.#form[key] = value;
}
}
connectedCallback() {
this.step = 0;
super.connectedCallback();
this.#showAccountTypeForm();
}
get step() {
@ -110,23 +120,77 @@ customElements.define('register-form', class extends Component {
this.setAttribute('step', n);
}
#copyForm(form, finalForm) {
form.reportValidity();
#host(body) {
this.shadowRoot.querySelector('#host').innerHTML = body;
}
for (const el of form.elements) {
if (el.name === '') continue;
if (!el.reportValidity()) {
return false;
}
#showAccountTypeForm() {
this.#host(`
<register-user-type> </register-user-type>
`);
}
#showUserAccountForm() {
this.#host(`
<register-user-account-form
login="${ this.#form.login }"
email="${ this.#form.email }"
password="${ this.#form.password }"
></register-user-account-form>
`);
}
#showBusinessAccountForm() {
this.#host(`
<register-business-account-form
login="${ this.#form.login }"
email="${ this.#form.email }"
password="${ this.#form.password }"
></register-business-account-form>
`);
}
#showBusinessDetailsForm() {
this.#host(`
<register-business-details-form
name="${ this.#form.name }"
description="${ this.#form.description }"
></register-business-details-form>
`);
}
#transfer(page, direction) {
const current = direction === 'next'
? this.#nextPage(page)
: this.#prevPage(page);
if (!current) return;
}
#nextPage(page) {
switch (page) {
case "account-type":
return this.#form.account_type === 'User'
? 'user-account'
: 'business-account';
case "user-account": return null;
case "business-account": return 'business-details';
case "business-details": return 'business-items';
case "business-items": return 'business-contact';
case "business-contact": return 'business-submit';
case "business-submit": return null;
}
}
#prevPage(page) {
switch (page) {
case "account-type":
return null;
case "user-account": return 'account-type';
case "business-account": return 'account-type';
case "business-details": return 'business-account';
case "business-items": return 'business-details';
case "business-contact": return 'business-items';
case "business-submit": return 'business-contact';
}
const inputs = form.inputs;
if (inputs)
finalForm.setItems(inputs);
else
for (const el of form.elements) {
if (el.name === '') continue;
finalForm.updateField(el.name, el.value);
}
return true;
}
});

View File

@ -1,13 +1,54 @@
import { PseudoForm } from "../shared.js";
export class RegisterFormComponent extends PseudoForm {
#mounted = false;
get submitEventName() {
return null;
}
mountFormHandler() {
if (this.#mounted) return;
this.#mounted = true;
const form = this.shadowRoot.querySelector('form');
form.addEventListener('submit', ev => {
ev.preventDefault();
ev.stopPropagation();
if (form.reportValidity()) {
this.shadowRoot.querySelector('form-navigation').next();
}
});
this.addEventListener('form:next', () => {
this.#dispatchForm()
});
}
#dispatchForm() {
const detail = [...this.elements].filter(el => el.name && el.name.length).reduce((memo, el) => ({
...memo,
[el.name]: el.value,
}), {});
console.warn('#dispatchForm', detail, this.elements)
this.dispatchEvent(new CustomEvent(this.submitEventName, { composed: true, bubbles: true, detail }));
}
get elements() {
return this.shadowRoot.querySelector('form').elements;
}
}
export class RegisterForm {
#email; //: String,
#login; //: String,
#password; //: String,
#facebook_id; //: Option<String>,
#account_type; //: db::AccountType,
#email = ''; //: String,
#login = ''; //: String,
#password = ''; //: String,
#facebook_id = null; //: Option<String>,
#account_type = 'User'; //: db::AccountType,
#items = null; //: Option<Vec<view::BusinessItemInput>>,
#contacts = null; //: Option<Vec<view::CreateContactInfoInput>>,
#name; //: Option<String>,
#description; //: Option<String>,
#name = null; //: Option<String>,
#description = null; //: Option<String>,
get email() {
return this.#email;
@ -68,6 +109,10 @@ export class RegisterForm {
return this.#items;
}
set items(a) {
this.#items = a;
}
appendItem(item) {
this.#items = this.#items || [];
this.#items.push(item);
@ -99,27 +144,23 @@ export class RegisterForm {
}
get payload() {
const {
email,
login,
password,
facebook_id,
account_type,
items,
contacts,
name,
description
} = this;
return {
email,
login,
password,
facebook_id,
account_type,
items,
contacts,
name,
description
};
return [
'email',
'login',
'password',
'facebook_id',
'account_type',
'items',
'contacts',
'name',
'description'
].reduce((m, k) => {
const v = this[k];
if (v === undefined || v === null) {
return m;
}
m[k] = v;
return m;
}, {})
}
}

View File

@ -1,6 +1,6 @@
import { Component, BUTTON_STYLE, TIP_STYLE } from "../shared";
customElements.define('register-user-type', class extends Component {
customElements.define('register-account-type', class extends Component {
constructor() {
super(`
<style>
@ -110,19 +110,22 @@ customElements.define('register-user-type', class extends Component {
</a>
</li>
</ul>
<form-navigation></form-navigation>
`;
const user = this.shadowRoot.querySelector('#user');
user.addEventListener('click', ev => {
ev.preventDefault();
ev.stopPropagation();
this.dispatchEvent(new CustomEvent('account:type:user', { bubbles: true, composed: true }));
this.dispatchEvent(new CustomEvent('account:type', { bubbles: true, composed: true, detail: 'User' }));
this.shadowRoot.querySelector('form-navigation').next();
});
const service = this.shadowRoot.querySelector('#local-service');
service.addEventListener('click', ev => {
ev.preventDefault();
ev.stopPropagation();
this.dispatchEvent(new CustomEvent('account:type:local-service', { bubbles: true, composed: true }));
this.dispatchEvent(new CustomEvent('account:type', { bubbles: true, composed: true, detail: 'Business' }));
this.shadowRoot.querySelector('form-navigation').next();
});
}
});

View File

@ -1,51 +0,0 @@
import { FORM_STYLE, PseudoForm } from "../shared";
customElements.define('register-basic-form', class extends PseudoForm {
constructor() {
super(`
<style>
:host {
display: block;
}
${ FORM_STYLE }
</style>
<form id="step-1">
<div>
<label>Login</label>
<input id="login" name="login" placeholder="Login" type="text" required autofocus />
</div>
<div>
<label>E-Mail</label>
<input name="email" placeholder="Email" type="email" required />
</div>
<div>
<label>Hasło</label>
<input name="pass" placeholder="Hasło" type="password" required />
</div>
<input type="submit" style="display: none">
<form-navigation></form-navigation>
</form>
`);
const form = this.shadowRoot.querySelector('form');
form.addEventListener('submit', ev => {
ev.preventDefault();
ev.stopPropagation();
if (form.reportValidity()) {
this.shadowRoot.querySelector('form-navigation').next();
}
});
this.addEventListener('form:next', () => {
this.#dispatchForm()
});
}
#dispatchForm() {
const detail = [...this.shadowRoot.querySelector('form').elements].filter(el => el.name && el.name.length).reduce((memo, el) => ({
...memo,
[el.name]: el.value,
}), {});
this.dispatchEvent(new CustomEvent('account:basic', { composed: true, bubbles: true, detail }));
}
});

View File

@ -0,0 +1,57 @@
import { FORM_STYLE } from "../shared";
import { RegisterFormComponent } from "./model";
customElements.define('register-business-account-form', class extends RegisterFormComponent {
static get observedAttributes() {
return ['login', 'password', 'email']
}
constructor() {
super(`
<style>
:host {
display: block;
}
${ FORM_STYLE }
</style>
<form id="step-1">
<div>
<label>Login</label>
<input id="login" name="login" placeholder="Login" type="text" required autofocus />
</div>
<div>
<label>E-Mail</label>
<input id="email" name="email" placeholder="Email" type="email" required />
</div>
<div>
<label>Hasło</label>
<input id="password" name="pass" placeholder="Hasło" type="password" required />
</div>
<input type="submit" style="display: none">
<form-navigation></form-navigation>
</form>
`);
this.mountFormHandler();
}
get submitEventName() {
return 'account:basic';
}
set email(v) {
this.#input('email').value = v;
}
set login(v) {
this.#input('login').value = v;
}
set password(v) {
this.#input('password').value = v;
}
#input(id) {
return this.shadowRoot.querySelector(`#${ id }`);
}
});

View File

@ -0,0 +1,22 @@
import { RegisterFormComponent } from "./model.js";
customElements.define('register-business-contact-form', class extends RegisterFormComponent {
constructor() {
super(`
<style>
:host { display: block; }
</style>
<article>
<form>
<form-navigation></form-navigation>
</form>
</article>
`);
this.mountFormHandler();
}
get submitEventName() {
return 'account:contacts';
}
});

View File

@ -1,13 +0,0 @@
import { Component } from "../shared.js";
customElements.define('register-business-contacts', class extends Component {
constructor() {
super(`
<style>
:host { display: block; }
</style>
<article>
</article>
`);
}
});

View File

@ -0,0 +1,48 @@
import { FORM_STYLE, TIP_STYLE } from "../shared";
import { RegisterFormComponent } from "./model";
customElements.define('register-business-details-form', class extends RegisterFormComponent {
static get observedAttributes() {
return ["name", "description"];
}
constructor() {
super(`
<style>
:host { display: block; }
* { font-family: 'Noto Sans', sans-serif; }
textarea { min-height: 200px; }
${ FORM_STYLE }${ TIP_STYLE }
</style>
<form id="step-2">
<div>
<label>Nazwa usługodawcy</label>
<input id="name" name="name" placeholder="Nazwa usługi" type="text" required autofocus />
</div>
<div>
<label>Opis usługodawcy</label>
<textarea id="description" name="description" required></textarea>
<div class="tip">Produkty dodawane w nastepnym kroku</div>
</div>
<form-navigation></form-navigation>
</form>
`);
this.mountFormHandler();
}
get submitEventName() {
return 'account:business';
}
set name(v) {
this.#input('name').value = v;
}
set description(v) {
this.#input('description').value = (v || '').trim();
}
#input(id) {
return this.shadowRoot.querySelector(`#${ id }`);
}
})

View File

@ -1,41 +0,0 @@
import { FORM_STYLE, TIP_STYLE, PseudoForm } from "../shared";
customElements.define('register-business-form', class extends PseudoForm {
constructor() {
super(`
<style>
:host { display: block; }
* { font-family: 'Noto Sans', sans-serif; }
textarea { min-height: 200px; }
${ FORM_STYLE }${ TIP_STYLE }
</style>
<form id="step-2">
<div>
<label>Nazwa usługodawcy</label>
<input name="name" placeholder="Nazwa usługi" type="text" required autofocus />
</div>
<div>
<label>Opis usługodawcy</label>
<textarea name="description" required></textarea>
<div class="tip">Produkty dodawane w nastepnym kroku</div>
</div>
<form-navigation></form-navigation>
</form>
`);
this.shadowRoot.querySelector('form').addEventListener('submit', ev => {
ev.preventDefault();
ev.stopPropagation();
this.#dispatchForm();
this.shadowRoot.querySelector('form-navigation').next();
});
}
#dispatchForm() {
const detail = [...this.shadowRoot.querySelector('form').elements].filter(el => el.name && el.name.length).reduce((memo, el) => ({
...memo,
[el.name]: el.value,
}), {});
this.dispatchEvent(new CustomEvent('account:business', { composed: true, bubbles: true, detail }));
}
})

View File

@ -1,6 +1,5 @@
import { FORM_STYLE, PseudoForm } from "../shared";
import "./register-item-form-row"
import { FORM_STYLE } from "../shared";
import { RegisterFormComponent } from "./model";
const updateItems = (form) => {
let idx = 0;
@ -10,7 +9,7 @@ const updateItems = (form) => {
return idx;
}
customElements.define('register-items-form', class extends PseudoForm {
customElements.define('register-business-items-form', class extends RegisterFormComponent {
constructor() {
super(`
<style>
@ -34,7 +33,6 @@ customElements.define('register-items-form', class extends PseudoForm {
<form-navigation></form-navigation>
</form>
`);
const nav = this.shadowRoot.querySelector('form-navigation');
this.addEventListener('item:removed', ev => {
ev.stopPropagation();
updateItems(this)
@ -48,20 +46,20 @@ customElements.define('register-items-form', class extends PseudoForm {
}
}
});
this.addEventListener('item:submit', ev => {
ev.preventDefault();
ev.stopPropagation();
nav.next();
});
this.shadowRoot.querySelector('#add-item').addEventListener('click', ev => {
ev.stopPropagation();
ev.preventDefault();
this.appendChild(document.createElement('register-item-form-row'));
updateItems(this);
});
this.mountFormHandler();
}
get inputs() {
return [...this.querySelectorAll("register-item-form-row")].map(form => form.inputs)
get submitEventName() {
return 'account:items';
}
get elements() {
return Array.from(this.querySelectorAll("register-item-form-row")).map(form => form.elements).flat();
}
});

View File

@ -1,6 +1,10 @@
import { FORM_STYLE, PseudoForm } from "../shared";
customElements.define('register-submit-form', class extends PseudoForm {
customElements.define('register-business-submit-form', class extends PseudoForm {
static get observedAttributes() {
return ['name', 'description', 'login', 'email', 'password', 'account_type']
}
constructor() {
super(`
<style>
@ -23,7 +27,7 @@ customElements.define('register-submit-form', class extends PseudoForm {
<div id="copied">
<input id="hidden-login" name="login" type="hidden" />
<input id="hidden-email" name="email" type="hidden" />
<input id="hidden-pass" name="password" type="hidden" />
<input id="hidden-password" name="password" type="hidden" />
<input id="hidden-name" name="name" type="hidden" />
<input id="hidden-description" name="description" type="hidden" />
</div>
@ -64,7 +68,7 @@ customElements.define('register-submit-form', class extends PseudoForm {
this.shadowRoot.querySelector(`[id="preview-${ name }"]`).value = value;
}
setItems(items) {
set items(items) {
const host = this.shadowRoot.querySelector('#items');
host.innerHTML = ``;
for (const row of items) {
@ -85,7 +89,42 @@ customElements.define('register-submit-form', class extends PseudoForm {
}
}
set accountType(v) {
set account_type(v) {
this.shadowRoot.querySelector('#account_type').value = v;
}
get name() {
return this.#hidden('name').value
}
set name(v) {
this.#hidden('name').value = v;
}
get description() {
return this.#hidden('description').value
}
set description(v) {
this.#hidden('description').value = v;
}
get login() {
return this.#hidden('login').value
}
set login(v) {
this.#hidden('login').value = v;
}
get email() {
return this.#hidden('email').value
}
set email(v) {
this.#hidden('email').value = v;
}
get password() {
return this.#hidden('password').value
}
set password(v) {
this.#hidden('password').value = v;
}
#hidden(selector) {
return this.shadowRoot.querySelector(`#hidden-${selector}`)
}
});

View File

@ -1,6 +1,7 @@
import { FORM_STYLE, PseudoForm } from "../shared";
import { FORM_STYLE } from "../shared";
import { RegisterFormComponent } from "./model";
customElements.define('register-item-form-row', class extends PseudoForm {
customElements.define('register-item-form-row', class extends RegisterFormComponent {
static get observedAttributes() {
return ['idx', 'name', 'picture-url', 'action', 'remove', 'save']
}
@ -78,6 +79,10 @@ customElements.define('register-item-form-row', class extends PseudoForm {
});
}
get submitEventName() {
return 'account:items';
}
connectedCallback() {
super.connectedCallback();
this.#updateNames(this.idx);
@ -98,6 +103,11 @@ customElements.define('register-item-form-row', class extends PseudoForm {
return this.#inputs.map(extract);
}
get elements() {
console.warn('item form elements', this.#inputs)
return this.#inputs
}
get #inputs() {
return [
this.shadowRoot.querySelector('.item-name'),

View File

@ -1,8 +1,9 @@
import { Component, FORM_STYLE } from "../shared";
import { FORM_STYLE } from "../shared";
import { RegisterFormComponent } from "./model";
customElements.define('register-user-form', class extends Component {
customElements.define('register-user-account-form', class extends RegisterFormComponent {
static get observedAttributes() {
return ['mode']
return ['mode', 'login', 'password', 'email']
}
constructor() {
@ -100,6 +101,10 @@ customElements.define('register-user-form', class extends Component {
});
}
get submitEventName() {
return 'account:user:details';
}
connectedCallback() {
this.mode = 'email';
}
@ -123,4 +128,20 @@ customElements.define('register-user-form', class extends Component {
set mode(v) {
this.setAttribute('mode', v);
}
set email(v) {
this.#input('email').value = v;
}
set login(v) {
this.#input('login').value = v;
}
set password(v) {
this.#input('password').value = v;
}
#input(id) {
return this.shadowRoot.querySelector(`#${ id }`);
}
});

View File

@ -347,11 +347,11 @@ impl RegisterPage {
pub fn as_str(&self) -> &str {
match self {
RegisterPage::UserType => "user-type",
RegisterPage::Basic => "basic",
RegisterPage::Business => "business",
RegisterPage::User => "user-account",
RegisterPage::Basic => "business-account",
RegisterPage::Business => "business-details",
RegisterPage::Items => "items",
RegisterPage::Submit => "submit",
RegisterPage::User => "user",
RegisterPage::Contact => "contact",
}
}