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 "./login-form.js";
import "./register-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-item-form-row.js";
import "./register-form/register-items-form.js"; import "./register-form/register-business-items-form.js";
import "./register-form/register-business-form.js"; import "./register-form/register-business-details-form.js";
import "./register-form/register-user-type.js"; import "./register-form/register-account-type.js";
import "./register-form/register-user-form.js"; import "./register-form/register-user-account-form.js";
import "./register-form/register-business-contacts.js"; import "./register-form/register-business-contact-form.js";
import "./register-form/register-submit-form.js"; import "./register-form/register-business-submit-form.js";
import "./business-items/business-item.js"; import "./business-items/business-item.js";
import "./business-items/business-item-editor.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"; js.src = "https://connect.facebook.net/en_US/sdk.js";
document.head.appendChild(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 { FORM_STYLE, Component } from "./shared.js";
import { RegisterForm } from "./register-form/model.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 { customElements.define('register-form', class extends Component {
#form = new RegisterForm; #form = new RegisterForm;
@ -47,57 +60,54 @@ customElements.define('register-form', class extends Component {
</style> </style>
<article id="host"> <article id="host">
</article> </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 => { this.shadowRoot.addEventListener('form:next', ev => {
ev.stopPropagation(); 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 => { this.shadowRoot.addEventListener('form:prev', ev => {
ev.stopPropagation(); ev.stopPropagation();
this.step = this.step - 1; this.step = this.step - 1;
}); });
this.addEventListener('account:basic', ev => {
console.log(ev); this.shadowRoot.addEventListener('account:type', 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();
ev.stopPropagation(); 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() { connectedCallback() {
this.step = 0; super.connectedCallback();
this.#showAccountTypeForm();
} }
get step() { get step() {
@ -110,23 +120,77 @@ customElements.define('register-form', class extends Component {
this.setAttribute('step', n); this.setAttribute('step', n);
} }
#copyForm(form, finalForm) { #host(body) {
form.reportValidity(); this.shadowRoot.querySelector('#host').innerHTML = body;
}
for (const el of form.elements) { #showAccountTypeForm() {
if (el.name === '') continue; this.#host(`
if (!el.reportValidity()) { <register-user-type> </register-user-type>
return false; `);
}
#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;
} }
} }
const inputs = form.inputs;
if (inputs) #prevPage(page) {
finalForm.setItems(inputs); switch (page) {
else case "account-type":
for (const el of form.elements) { return null;
if (el.name === '') continue; case "user-account": return 'account-type';
finalForm.updateField(el.name, el.value); 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';
} }
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 { export class RegisterForm {
#email; //: String, #email = ''; //: String,
#login; //: String, #login = ''; //: String,
#password; //: String, #password = ''; //: String,
#facebook_id; //: Option<String>, #facebook_id = null; //: Option<String>,
#account_type; //: db::AccountType, #account_type = 'User'; //: db::AccountType,
#items = null; //: Option<Vec<view::BusinessItemInput>>, #items = null; //: Option<Vec<view::BusinessItemInput>>,
#contacts = null; //: Option<Vec<view::CreateContactInfoInput>>, #contacts = null; //: Option<Vec<view::CreateContactInfoInput>>,
#name; //: Option<String>, #name = null; //: Option<String>,
#description; //: Option<String>, #description = null; //: Option<String>,
get email() { get email() {
return this.#email; return this.#email;
@ -68,6 +109,10 @@ export class RegisterForm {
return this.#items; return this.#items;
} }
set items(a) {
this.#items = a;
}
appendItem(item) { appendItem(item) {
this.#items = this.#items || []; this.#items = this.#items || [];
this.#items.push(item); this.#items.push(item);
@ -99,27 +144,23 @@ export class RegisterForm {
} }
get payload() { get payload() {
const { return [
email, 'email',
login, 'login',
password, 'password',
facebook_id, 'facebook_id',
account_type, 'account_type',
items, 'items',
contacts, 'contacts',
name, 'name',
description 'description'
} = this; ].reduce((m, k) => {
return { const v = this[k];
email, if (v === undefined || v === null) {
login, return m;
password, }
facebook_id, m[k] = v;
account_type, return m;
items, }, {})
contacts,
name,
description
};
} }
} }

View File

@ -1,6 +1,6 @@
import { Component, BUTTON_STYLE, TIP_STYLE } from "../shared"; 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() { constructor() {
super(` super(`
<style> <style>
@ -110,19 +110,22 @@ customElements.define('register-user-type', class extends Component {
</a> </a>
</li> </li>
</ul> </ul>
<form-navigation></form-navigation>
`; `;
const user = this.shadowRoot.querySelector('#user'); const user = this.shadowRoot.querySelector('#user');
user.addEventListener('click', ev => { user.addEventListener('click', ev => {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); 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'); const service = this.shadowRoot.querySelector('#local-service');
service.addEventListener('click', ev => { service.addEventListener('click', ev => {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); 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 { FORM_STYLE } from "../shared";
import { RegisterFormComponent } from "./model";
import "./register-item-form-row"
const updateItems = (form) => { const updateItems = (form) => {
let idx = 0; let idx = 0;
@ -10,7 +9,7 @@ const updateItems = (form) => {
return idx; return idx;
} }
customElements.define('register-items-form', class extends PseudoForm { customElements.define('register-business-items-form', class extends RegisterFormComponent {
constructor() { constructor() {
super(` super(`
<style> <style>
@ -34,7 +33,6 @@ customElements.define('register-items-form', class extends PseudoForm {
<form-navigation></form-navigation> <form-navigation></form-navigation>
</form> </form>
`); `);
const nav = this.shadowRoot.querySelector('form-navigation');
this.addEventListener('item:removed', ev => { this.addEventListener('item:removed', ev => {
ev.stopPropagation(); ev.stopPropagation();
updateItems(this) 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 => { this.shadowRoot.querySelector('#add-item').addEventListener('click', ev => {
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
this.appendChild(document.createElement('register-item-form-row')); this.appendChild(document.createElement('register-item-form-row'));
updateItems(this); updateItems(this);
}); });
this.mountFormHandler();
} }
get inputs() { get submitEventName() {
return [...this.querySelectorAll("register-item-form-row")].map(form => form.inputs) 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"; 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() { constructor() {
super(` super(`
<style> <style>
@ -23,7 +27,7 @@ customElements.define('register-submit-form', class extends PseudoForm {
<div id="copied"> <div id="copied">
<input id="hidden-login" name="login" type="hidden" /> <input id="hidden-login" name="login" type="hidden" />
<input id="hidden-email" name="email" 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-name" name="name" type="hidden" />
<input id="hidden-description" name="description" type="hidden" /> <input id="hidden-description" name="description" type="hidden" />
</div> </div>
@ -64,7 +68,7 @@ customElements.define('register-submit-form', class extends PseudoForm {
this.shadowRoot.querySelector(`[id="preview-${ name }"]`).value = value; this.shadowRoot.querySelector(`[id="preview-${ name }"]`).value = value;
} }
setItems(items) { set items(items) {
const host = this.shadowRoot.querySelector('#items'); const host = this.shadowRoot.querySelector('#items');
host.innerHTML = ``; host.innerHTML = ``;
for (const row of items) { 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; 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() { static get observedAttributes() {
return ['idx', 'name', 'picture-url', 'action', 'remove', 'save'] 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() { connectedCallback() {
super.connectedCallback(); super.connectedCallback();
this.#updateNames(this.idx); this.#updateNames(this.idx);
@ -98,6 +103,11 @@ customElements.define('register-item-form-row', class extends PseudoForm {
return this.#inputs.map(extract); return this.#inputs.map(extract);
} }
get elements() {
console.warn('item form elements', this.#inputs)
return this.#inputs
}
get #inputs() { get #inputs() {
return [ return [
this.shadowRoot.querySelector('.item-name'), 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() { static get observedAttributes() {
return ['mode'] return ['mode', 'login', 'password', 'email']
} }
constructor() { constructor() {
@ -100,6 +101,10 @@ customElements.define('register-user-form', class extends Component {
}); });
} }
get submitEventName() {
return 'account:user:details';
}
connectedCallback() { connectedCallback() {
this.mode = 'email'; this.mode = 'email';
} }
@ -123,4 +128,20 @@ customElements.define('register-user-form', class extends Component {
set mode(v) { set mode(v) {
this.setAttribute('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 { pub fn as_str(&self) -> &str {
match self { match self {
RegisterPage::UserType => "user-type", RegisterPage::UserType => "user-type",
RegisterPage::Basic => "basic", RegisterPage::User => "user-account",
RegisterPage::Business => "business", RegisterPage::Basic => "business-account",
RegisterPage::Business => "business-details",
RegisterPage::Items => "items", RegisterPage::Items => "items",
RegisterPage::Submit => "submit", RegisterPage::Submit => "submit",
RegisterPage::User => "user",
RegisterPage::Contact => "contact", RegisterPage::Contact => "contact",
} }
} }