Improve register company
This commit is contained in:
parent
d2a00dfc79
commit
a9669432ec
@ -1,3 +1,7 @@
|
||||
import "./poly.js";
|
||||
|
||||
import "./shared/error-message.js";
|
||||
|
||||
import "./shared/rich-text-editor.js";
|
||||
import "./shared/form-navigation.js";
|
||||
import "./shared/image-popup.js";
|
||||
@ -8,6 +12,7 @@ import "./shared/price/price-input.js";
|
||||
import "./shared/price/price-view.js";
|
||||
|
||||
import "./ow-account/ow-account.js";
|
||||
import "./ow-account/account-view.js";
|
||||
|
||||
import "./local-businesses/local-businesses.js";
|
||||
import "./local-businesses/local-business-item.js";
|
||||
@ -16,12 +21,12 @@ import "./local-businesses/local-business.js";
|
||||
import "./login-form.js";
|
||||
import "./register-form.js";
|
||||
import "./register-form/register-business-account-form";
|
||||
import "./register-form/register-item-form-row.js";
|
||||
import "./register-form/register-business-item-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-contacts-form.js";
|
||||
import "./register-form/register-business-submit-form.js";
|
||||
|
||||
import "./business-items/business-item.js";
|
||||
@ -64,15 +69,3 @@ 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];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, FORM_STYLE } from "../shared";
|
||||
|
||||
import "../register-form/register-item-form-row";
|
||||
import "../register-form/register-business-item-form";
|
||||
|
||||
customElements.define('business-item-editor', class extends Component {
|
||||
#idx;
|
||||
|
@ -2,7 +2,7 @@ import { Component, FORM_STYLE } from "../shared";
|
||||
|
||||
customElements.define('contact-info-editor', class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ['type', "contact-id", "content"];
|
||||
return ['type', "contact-id", "content", 'save'];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -26,6 +26,9 @@ customElements.define('contact-info-editor', class extends Component {
|
||||
article {
|
||||
margin: 8px;
|
||||
}
|
||||
:host([save="false"]) #submit {
|
||||
display: none;
|
||||
}
|
||||
@media only screen and (min-device-width: 1000px) {
|
||||
article {
|
||||
margin: 0;
|
||||
@ -41,14 +44,14 @@ customElements.define('contact-info-editor', class extends Component {
|
||||
<label>E-Mail</label>
|
||||
<div id="input">
|
||||
<div id="icon">
|
||||
<contact-type-icon contact-type="email"></contact-type-icon>
|
||||
<contact-type-icon type="email"></contact-type-icon>
|
||||
</div>
|
||||
|
||||
<input type="text" id="content" name="content" />
|
||||
<input type="hidden" id="contact_type" name="contact_type" />
|
||||
<input type="hidden" id="type" name="type" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div id="submit">
|
||||
<input type="submit" value="Dodaj" />
|
||||
</div>
|
||||
</form>
|
||||
@ -65,6 +68,7 @@ customElements.define('contact-info-editor', class extends Component {
|
||||
input.addEventListener('change', ev => {
|
||||
ev.stopPropagation();
|
||||
this.#updateContactType(input.value, null);
|
||||
this.content = input.value;
|
||||
});
|
||||
input.addEventListener('keyup', ev => {
|
||||
ev.stopPropagation();
|
||||
@ -72,11 +76,12 @@ customElements.define('contact-info-editor', class extends Component {
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null;
|
||||
this.#updateContactType(input.value, null);
|
||||
this.content = input.value;
|
||||
}, 1000 / 3)
|
||||
});
|
||||
}
|
||||
|
||||
this.shadowRoot.querySelector('#contact_type').addEventListener('change', ev => {
|
||||
this.shadowRoot.querySelector('#type').addEventListener('change', ev => {
|
||||
ev.stopPropagation();
|
||||
this.#updateContactType(null, ev.target.value);
|
||||
});
|
||||
@ -136,8 +141,8 @@ customElements.define('contact-info-editor', class extends Component {
|
||||
type = type || this.#resolveContactType(value);
|
||||
this.setAttribute('type', type);
|
||||
const icon = this.shadowRoot.querySelector('contact-type-icon');
|
||||
icon.setAttribute('contact-type', type);
|
||||
this.shadowRoot.querySelector('#contact_type').value = type;
|
||||
icon.type = type;
|
||||
this.shadowRoot.querySelector('#type').value = type;
|
||||
}
|
||||
|
||||
#resolveContactType(s) {
|
||||
|
@ -2,7 +2,7 @@ import { Component } from "../shared";
|
||||
|
||||
customElements.define('contact-info', class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ['contact-type', 'content', 'contact-id', 'mode'];
|
||||
return ['type', 'content', 'contact-id', 'mode'];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -24,13 +24,13 @@ customElements.define('contact-info', class extends Component {
|
||||
<slot></slot>
|
||||
<section>
|
||||
<a target="_blank">
|
||||
<contact-type-icon contact-type="email"></contact-type-icon>
|
||||
<contact-type-icon type="email"></contact-type-icon>
|
||||
<div id="content"></div>
|
||||
</a>
|
||||
</section>
|
||||
`);
|
||||
this.shadowRoot.querySelector('a').addEventListener('click', ev => {
|
||||
if (this.contact_type === 'mobile') {
|
||||
if (this.type === 'mobile') {
|
||||
ev.preventDefault();
|
||||
const decoded = atob(this.content);
|
||||
if (this.#isMobile()) {
|
||||
@ -44,16 +44,16 @@ customElements.define('contact-info', class extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
set contact_type(v) {
|
||||
set type(v) {
|
||||
if (!v) return;
|
||||
this.setAttribute('contact-type', v);
|
||||
this.shadowRoot.querySelector('contact-type-icon').setAttribute('contact-type', v);
|
||||
this.setAttribute('type', v);
|
||||
this.shadowRoot.querySelector('contact-type-icon').type = v;
|
||||
|
||||
this.#setHref();
|
||||
}
|
||||
|
||||
get contact_type() {
|
||||
return this.getAttribute('contact-type');
|
||||
get type() {
|
||||
return this.getAttribute('type');
|
||||
}
|
||||
|
||||
set content(v) {
|
||||
@ -90,7 +90,7 @@ customElements.define('contact-info', class extends Component {
|
||||
|
||||
#createLinkPath() {
|
||||
const s = this.shadowRoot.querySelector('#content').textContent || '';
|
||||
switch (this.contact_type) {
|
||||
switch (this.type) {
|
||||
case 'email':
|
||||
return `mailto:${ s }`;
|
||||
default:
|
||||
|
@ -3,7 +3,7 @@ import { Component } from "../shared";
|
||||
customElements.define('contact-type-icon',
|
||||
class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ['contact-type'];
|
||||
return ['type'];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -11,16 +11,16 @@ customElements.define('contact-type-icon',
|
||||
<style>
|
||||
:host { display: block; }
|
||||
svg { display: none; }
|
||||
:host([contact-type="email"]) #email-icon {
|
||||
:host([type="email"]) #email-icon {
|
||||
display: block;
|
||||
}
|
||||
:host([contact-type="facebook"]) #fb-icon {
|
||||
:host([type="facebook"]) #fb-icon {
|
||||
display: block;
|
||||
}
|
||||
:host([contact-type="other"]) #other-icon {
|
||||
:host([type="other"]) #other-icon {
|
||||
display: block;
|
||||
}
|
||||
:host([contact-type="mobile"]) #mobile-icon {
|
||||
:host([type="mobile"]) #mobile-icon {
|
||||
display: block;
|
||||
}
|
||||
path {
|
||||
@ -48,15 +48,11 @@ customElements.define('contact-type-icon',
|
||||
`);
|
||||
}
|
||||
|
||||
set contact_type(v) {
|
||||
this.contactType = v;
|
||||
set type(v) {
|
||||
this.setAttribute('type', v || 'email');
|
||||
}
|
||||
|
||||
set contactType(v) {
|
||||
this.setAttribute('contact-type', v || 'email');
|
||||
}
|
||||
|
||||
get contact_type() {
|
||||
return this.getAttribute('contact-type')
|
||||
get type() {
|
||||
return this.getAttribute('type')
|
||||
}
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import { Component, BUTTON_STYLE } from "../shared";
|
||||
|
||||
customElements.define('edit-contact-info', class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ['contact-id', "mode"];
|
||||
return ['contact-id', "mode", "delete", 'type'];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -41,6 +41,9 @@ customElements.define('edit-contact-info', class extends Component {
|
||||
:host([mode = 'edit']) ::slotted(contact-info) {
|
||||
display: none;
|
||||
}
|
||||
:host([delete = "false"]) #deleteButton {
|
||||
display: none;
|
||||
}
|
||||
${ BUTTON_STYLE }
|
||||
</style>
|
||||
<article>
|
||||
@ -49,7 +52,7 @@ customElements.define('edit-contact-info', class extends Component {
|
||||
<contact-info-editor></contact-info-editor>
|
||||
<div id="buttons">
|
||||
<input type="button" value="Edytuj" id="edit" />
|
||||
<form action="/contacts/delete" method="post">
|
||||
<form id="deleteButton" action="/contacts/delete" method="post">
|
||||
<input type="hidden" name="id" id="remove-id" />
|
||||
<input type="submit" value="Usuń" id="remove" />
|
||||
</form>
|
||||
@ -67,7 +70,7 @@ customElements.define('edit-contact-info', class extends Component {
|
||||
if (!info) return;
|
||||
|
||||
form.contact_id = info.contact_id;
|
||||
form.type = info.contact_type;
|
||||
form.type = info.type;
|
||||
form.content = info.content;
|
||||
|
||||
this.mode = 'edit';
|
||||
|
@ -129,6 +129,7 @@ customElements.define('account-view', class extends Component {
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.register_success = (location.search || '').includes('success');
|
||||
localStorage.removeItem('register');
|
||||
}
|
||||
|
||||
get name() {
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { Component, FORM_STYLE } from "../shared";
|
||||
import '../shared/facebook-button';
|
||||
import './account-view';
|
||||
import { Component, FORM_STYLE, goTo, historyDetails } from "../shared";
|
||||
|
||||
customElements.define('ow-account', class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ['mode', "id", "name", 'email', "facebook-id"]
|
||||
return ['mode']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -12,23 +10,15 @@ customElements.define('ow-account', class extends Component {
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
#form > * {
|
||||
#switch-register, #switch-login {
|
||||
display: none;
|
||||
}
|
||||
:host([mode="login"]) #form > login-form, :host([mode="login"]) #switch-register {
|
||||
:host([mode="login"]) #switch-register {
|
||||
display: block !important;
|
||||
}
|
||||
:host([mode="register"]) #form > register-form, :host([mode="register"]) #switch-login {
|
||||
:host([mode="register"]) #switch-login {
|
||||
display: block !important;
|
||||
}
|
||||
account-view {
|
||||
display: none;
|
||||
}
|
||||
:host([mode="display"]) account-view { display: block; }
|
||||
:host([mode="display"]) #form { display: none; }
|
||||
:host([mode="form"]) #form,
|
||||
:host([mode="login"]) #form { display: block; }
|
||||
|
||||
a {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
@ -41,9 +31,8 @@ customElements.define('ow-account', class extends Component {
|
||||
|
||||
${ FORM_STYLE }
|
||||
</style>
|
||||
<article id="form">
|
||||
<login-form></login-form>
|
||||
<register-form></register-form>
|
||||
<article>
|
||||
<slot></slot>
|
||||
<section id="switch-register">
|
||||
<a class="btn">Nie masz konta? Utwórz nowe</a>
|
||||
</section>
|
||||
@ -51,31 +40,40 @@ customElements.define('ow-account', class extends Component {
|
||||
<a class="btn">Posiadasz konto? Zaloguj się</a>
|
||||
</section>
|
||||
</article>
|
||||
<account-view></account-view>
|
||||
`);
|
||||
|
||||
this.shadowRoot.querySelector('#switch-login > a').addEventListener('click', ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.mode = 'login';
|
||||
goTo('/login');
|
||||
});
|
||||
this.shadowRoot.querySelector('#switch-register > a').addEventListener('click', ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.mode = 'register';
|
||||
goTo('/register/account-type');
|
||||
});
|
||||
this.addEventListener('facebook:account', ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.mode = 'facebook';
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this.mode === '') this.mode = 'login';
|
||||
this.name = this.name;
|
||||
this.email = this.email;
|
||||
this.facebook_id = this.facebook_id;
|
||||
super.connectedCallback();
|
||||
const parts = historyDetails().parts;
|
||||
switch (parts.first) {
|
||||
case 'register': {
|
||||
this.mode = 'register';
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this.mode = 'login';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
listenHistory = ({ parts }) => {
|
||||
console.warn(parts);
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
@ -90,31 +88,4 @@ customElements.define('ow-account', class extends Component {
|
||||
value = ['login', 'register', 'display'].includes(value) ? value : 'login';
|
||||
this.setAttribute('mode', value);
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.getAttribute('name') || '';
|
||||
}
|
||||
|
||||
set name(v) {
|
||||
this.setAttribute('name', v);
|
||||
this.shadowRoot.querySelector('account-view').name = v;
|
||||
}
|
||||
|
||||
get email() {
|
||||
return this.getAttribute('email') || '';
|
||||
}
|
||||
|
||||
set email(v) {
|
||||
this.setAttribute('email', v);
|
||||
this.shadowRoot.querySelector('account-view').email = v;
|
||||
}
|
||||
|
||||
get facebook_id() {
|
||||
return this.getAttribute('facebook-id');
|
||||
}
|
||||
|
||||
set facebook_id(v) {
|
||||
this.setAttribute('facebook-id', v);
|
||||
this.shadowRoot.querySelector('account-view').facebook_id = v;
|
||||
}
|
||||
});
|
||||
|
22
client/src/poly.js
Normal file
22
client/src/poly.js
Normal file
@ -0,0 +1,22 @@
|
||||
Object.defineProperties(Object.prototype, {
|
||||
'entry': {
|
||||
value(key) {
|
||||
const owner = this;
|
||||
return {
|
||||
owner, orElse(v) {
|
||||
if (owner[key] === undefined) owner[key] = v;
|
||||
return owner[key];
|
||||
}, get() {
|
||||
return owner[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperties(Array.prototype, {
|
||||
'last': { get() { return this[this.length - 1] } },
|
||||
'tail': { get() { return this[this.length - 1] } },
|
||||
'first': { get() { return this[0] } },
|
||||
'head': { get() { return this[0] } },
|
||||
})
|
@ -1,24 +1,11 @@
|
||||
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>
|
||||
*/
|
||||
import { FORM_STYLE, Component, goTo } from "./shared.js";
|
||||
import { RegisterForm } from "./register-form/model.js";
|
||||
|
||||
customElements.define('register-form', class extends Component {
|
||||
#form = new RegisterForm;
|
||||
|
||||
static get observedAttributes() {
|
||||
return ['step']
|
||||
return ['current']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -26,13 +13,6 @@ customElements.define('register-form', class extends Component {
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
[id^="step-"] { display: none; }
|
||||
:host([step="0"]) #step-0 { display: block; }
|
||||
:host([step="1"]) #step-1 { display: block; }
|
||||
:host([step="2"]) #step-2 { display: block; }
|
||||
:host([step="3"]) #step-3 { display: block; }
|
||||
:host([step="4"]) #step-4 { display: block; }
|
||||
:host([step="40"]) #step-40 { display: block; }
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
@ -58,139 +38,239 @@ customElements.define('register-form', class extends Component {
|
||||
|
||||
${ FORM_STYLE }
|
||||
</style>
|
||||
<article id="host">
|
||||
<article>
|
||||
<slot></slot>
|
||||
</article>
|
||||
`);
|
||||
this.shadowRoot.addEventListener('form:next', ev => {
|
||||
ev.stopPropagation();
|
||||
this.step = this.step + 1;
|
||||
this.#transfer('next');
|
||||
});
|
||||
this.shadowRoot.addEventListener('form:prev', ev => {
|
||||
ev.stopPropagation();
|
||||
this.step = this.step - 1;
|
||||
this.#transfer('prev');
|
||||
});
|
||||
|
||||
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.#copyDetail(ev);
|
||||
});
|
||||
this.addEventListener('account:basic', ev => {
|
||||
this.#copyDetail(ev)
|
||||
this.#showBusinessDetailsForm();
|
||||
this.#copyDetail(ev);
|
||||
});
|
||||
this.addEventListener('account:business', ev => {
|
||||
this.#copyDetail(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));
|
||||
this.#copyDetail(ev);
|
||||
});
|
||||
this.addEventListener('account:contacts', ev => {
|
||||
this.#copyDetail(ev);
|
||||
});
|
||||
this.#loadCache();
|
||||
}
|
||||
|
||||
#loadCache() {
|
||||
let register = {};
|
||||
try {
|
||||
register = JSON.parse(localStorage.getItem('register'));
|
||||
} catch (e) {
|
||||
}
|
||||
this.#form.from(register);
|
||||
}
|
||||
|
||||
#copyDetail(ev) {
|
||||
ev.stopPropagation();
|
||||
console.info('ev.detail', ev.detail);
|
||||
for (const [key, value] of Object.entries(ev.detail)) {
|
||||
this.#form[key] = value;
|
||||
}
|
||||
localStorage.setItem('register', JSON.stringify(this.#form.payload));
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.#showAccountTypeForm();
|
||||
}
|
||||
|
||||
get step() {
|
||||
const step = parseInt(this.getAttribute('step'));
|
||||
return isNaN(step) ? 1 : step;
|
||||
}
|
||||
|
||||
set step(n) {
|
||||
if (n < 0) return;
|
||||
this.setAttribute('step', n);
|
||||
listenHistory = ({ parts }) => {
|
||||
this.current = parts.last;
|
||||
}
|
||||
|
||||
#host(body) {
|
||||
this.shadowRoot.querySelector('#host').innerHTML = body;
|
||||
this.innerHTML = body;
|
||||
}
|
||||
|
||||
#showAccountTypeForm() {
|
||||
this.#host(`
|
||||
<register-user-type> </register-user-type>
|
||||
<register-account-type></register-account-type>
|
||||
`);
|
||||
}
|
||||
|
||||
#showUserAccountForm() {
|
||||
this.#host(`
|
||||
<register-user-account-form
|
||||
login="${ this.#form.login }"
|
||||
email="${ this.#form.email }"
|
||||
password="${ this.#form.password }"
|
||||
></register-user-account-form>
|
||||
<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>
|
||||
<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>
|
||||
<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;
|
||||
#showBusinessItemsForm() {
|
||||
this.#host(`
|
||||
<register-business-items-form>
|
||||
${ this.#form.items.map(
|
||||
({ name, price, picture_url }) => `
|
||||
<register-business-item-form
|
||||
name="${ name }"
|
||||
price="${ price }"
|
||||
picture-url="${ picture_url }"
|
||||
></register-business-item-form>
|
||||
`
|
||||
).join('') }
|
||||
</register-business-items-form>
|
||||
`);
|
||||
}
|
||||
|
||||
#nextPage(page) {
|
||||
switch (page) {
|
||||
#showBusinessContactsForm() {
|
||||
this.#host(`
|
||||
<register-business-contacts-form>
|
||||
${ this.#form.contacts.map(
|
||||
({ type, content }) => `
|
||||
<edit-contact-info
|
||||
mode="view"
|
||||
delete="false"
|
||||
>
|
||||
<contact-info
|
||||
type="${ type }"
|
||||
content="${ content }"
|
||||
></contact-info>
|
||||
</edit-contact-info>
|
||||
`).join('') }
|
||||
</register-business-contacts-form>
|
||||
`);
|
||||
}
|
||||
|
||||
#showBusinessSubmitForm() {
|
||||
this.#host(`
|
||||
<register-business-submit-form
|
||||
name="${ this.#form.name }"
|
||||
description="${ this.#form.description }"
|
||||
login="${ this.#form.login }"
|
||||
email="${ this.#form.email }"
|
||||
password="${ this.#form.password }"
|
||||
account-type="${ this.#form.account_type }"
|
||||
>
|
||||
${ this.#form.items.map(
|
||||
({ name, price, picture_url }) => `
|
||||
<local-business-item
|
||||
slot="items"
|
||||
name="${ name }"
|
||||
price="${ price }"
|
||||
picture-url="${ picture_url }"
|
||||
></local-business-item>
|
||||
`
|
||||
).join('') }
|
||||
${ this.#form.contacts.map(
|
||||
({ type, content }) => `
|
||||
<contact-info
|
||||
slot="contacts"
|
||||
type="${ type }"
|
||||
content="${ content }"
|
||||
></contact-info>
|
||||
`).join('') }
|
||||
</register-business-submit-form>
|
||||
`);
|
||||
}
|
||||
|
||||
#transfer(direction) {
|
||||
const current = direction === 'next'
|
||||
? this.#nextPage()
|
||||
: this.#prevPage();
|
||||
this.current = current;
|
||||
goTo(`/register/${ current }`);
|
||||
}
|
||||
|
||||
get current() {
|
||||
return this.getAttribute('current');
|
||||
}
|
||||
|
||||
set current(current) {
|
||||
if (!current) return;
|
||||
this.setAttribute('current', current);
|
||||
|
||||
switch (current) {
|
||||
case "account-type":
|
||||
return this.#showAccountTypeForm();
|
||||
case "user-account":
|
||||
return this.#showUserAccountForm();
|
||||
case "business-account":
|
||||
return this.#showBusinessAccountForm();
|
||||
case "business-details":
|
||||
return this.#showBusinessDetailsForm();
|
||||
case "business-items":
|
||||
return this.#showBusinessItemsForm();
|
||||
case "business-contacts":
|
||||
return this.#showBusinessContactsForm();
|
||||
case "business-submit":
|
||||
return this.#showBusinessSubmitForm();
|
||||
default:
|
||||
throw new Error(`Unknown page "${ current }"`);
|
||||
}
|
||||
}
|
||||
|
||||
#nextPage() {
|
||||
switch (this.current) {
|
||||
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;
|
||||
case "user-account":
|
||||
return null;
|
||||
case "business-account":
|
||||
return 'business-details';
|
||||
case "business-details":
|
||||
return 'business-items';
|
||||
case "business-items":
|
||||
return 'business-contacts';
|
||||
case "business-contacts":
|
||||
return 'business-submit';
|
||||
case "business-submit":
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#prevPage(page) {
|
||||
switch (page) {
|
||||
#prevPage() {
|
||||
switch (this.current) {
|
||||
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';
|
||||
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-contacts":
|
||||
return 'business-items';
|
||||
case "business-submit":
|
||||
return 'business-contacts';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -7,10 +7,12 @@ export class RegisterFormComponent extends PseudoForm {
|
||||
return null;
|
||||
}
|
||||
|
||||
mountFormHandler() {
|
||||
mountFormHandler(dispatchForm) {
|
||||
if (this.#mounted) return;
|
||||
this.#mounted = true;
|
||||
|
||||
if (!dispatchForm) dispatchForm = () => this.#dispatchForm();
|
||||
|
||||
const form = this.shadowRoot.querySelector('form');
|
||||
form.addEventListener('submit', ev => {
|
||||
ev.preventDefault();
|
||||
@ -21,7 +23,7 @@ export class RegisterFormComponent extends PseudoForm {
|
||||
}
|
||||
});
|
||||
this.addEventListener('form:next', () => {
|
||||
this.#dispatchForm()
|
||||
dispatchForm()
|
||||
});
|
||||
}
|
||||
|
||||
@ -30,7 +32,6 @@ export class RegisterFormComponent extends PseudoForm {
|
||||
...memo,
|
||||
[el.name]: el.value,
|
||||
}), {});
|
||||
console.warn('#dispatchForm', detail, this.elements)
|
||||
this.dispatchEvent(new CustomEvent(this.submitEventName, { composed: true, bubbles: true, detail }));
|
||||
}
|
||||
|
||||
@ -106,25 +107,19 @@ export class RegisterForm {
|
||||
}
|
||||
|
||||
get items() {
|
||||
return this.#items;
|
||||
return this.#items || [];
|
||||
}
|
||||
|
||||
set items(a) {
|
||||
this.#items = a;
|
||||
}
|
||||
|
||||
appendItem(item) {
|
||||
this.#items = this.#items || [];
|
||||
this.#items.push(item);
|
||||
}
|
||||
|
||||
get contacts() {
|
||||
return this.#contacts;
|
||||
return this.#contacts || [];
|
||||
}
|
||||
|
||||
appendContact(contact) {
|
||||
this.#contacts = this.#contacts || [];
|
||||
this.#contacts.push(contact);
|
||||
set contacts(c) {
|
||||
this.#contacts = c;
|
||||
}
|
||||
|
||||
get name() {
|
||||
@ -163,4 +158,22 @@ export class RegisterForm {
|
||||
return m;
|
||||
}, {})
|
||||
}
|
||||
|
||||
from(object) {
|
||||
[
|
||||
'email',
|
||||
'login',
|
||||
'password',
|
||||
'facebook_id',
|
||||
'account_type',
|
||||
'items',
|
||||
'contacts',
|
||||
'name',
|
||||
'description'
|
||||
].forEach(key => {
|
||||
const value = object[key];
|
||||
if (!value) return;
|
||||
this[key] = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -117,14 +117,22 @@ customElements.define('register-account-type', class extends Component {
|
||||
user.addEventListener('click', ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.dispatchEvent(new CustomEvent('account:type', { bubbles: true, composed: true, detail: 'User' }));
|
||||
this.dispatchEvent(new CustomEvent('account:type', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { account_type: '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', { bubbles: true, composed: true, detail: 'Business' }));
|
||||
this.dispatchEvent(new CustomEvent('account:type', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { account_type: 'Business' }
|
||||
}));
|
||||
this.shadowRoot.querySelector('form-navigation').next();
|
||||
});
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
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';
|
||||
}
|
||||
});
|
65
client/src/register-form/register-business-contacts-form.js
Normal file
65
client/src/register-form/register-business-contacts-form.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { BUTTON_STYLE } from "../shared.js";
|
||||
import { RegisterFormComponent } from "./model.js";
|
||||
|
||||
customElements.define('register-business-contacts-form', class extends RegisterFormComponent {
|
||||
constructor() {
|
||||
super(`
|
||||
<style>
|
||||
:host { display: block; }
|
||||
section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
${ BUTTON_STYLE }
|
||||
</style>
|
||||
<article>
|
||||
<form>
|
||||
<section>
|
||||
<contact-info-editor
|
||||
save="false"
|
||||
></contact-info-editor>
|
||||
<input type="button" id="addButton" value="Dodaj" />
|
||||
</section>
|
||||
<slot></slot>
|
||||
<form-navigation></form-navigation>
|
||||
</form>
|
||||
</article>
|
||||
`);
|
||||
|
||||
this.shadowRoot.querySelector('#addButton').addEventListener('click', ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
const form = this.shadowRoot.querySelector('contact-info-editor');
|
||||
const { type, content } = form;
|
||||
this.innerHTML += `
|
||||
<edit-contact-info mode="view" delete="false">
|
||||
<contact-info
|
||||
type="${ type }"
|
||||
content="${ content }"
|
||||
></contact-info>
|
||||
</edit-contact-info>
|
||||
`;
|
||||
});
|
||||
|
||||
this.mountFormHandler(() => this.#emitChange());
|
||||
}
|
||||
|
||||
get submitEventName() {
|
||||
return 'account:contacts';
|
||||
}
|
||||
|
||||
get #rows() {
|
||||
return Array.from(this.querySelectorAll('contact-info'));
|
||||
}
|
||||
|
||||
#emitChange() {
|
||||
const rows = this.#rows;
|
||||
const contacts = rows.map(({ type, content }) => ({
|
||||
type,
|
||||
content
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent(this.submitEventName, {
|
||||
bubbles: true, composed: true, detail: { contacts: contacts.length ? contacts : null },
|
||||
}));
|
||||
}
|
||||
});
|
@ -1,9 +1,9 @@
|
||||
import { FORM_STYLE } from "../shared";
|
||||
import { RegisterFormComponent } from "./model";
|
||||
|
||||
customElements.define('register-item-form-row', class extends RegisterFormComponent {
|
||||
customElements.define('register-business-item-form', class extends RegisterFormComponent {
|
||||
static get observedAttributes() {
|
||||
return ['idx', 'name', 'picture-url', 'action', 'remove', 'save']
|
||||
return ['idx', 'name', 'price', 'picture-url', 'action', 'remove', 'save']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -29,23 +29,22 @@ customElements.define('register-item-form-row', class extends RegisterFormCompon
|
||||
<image-input send-original="true"></image-input>
|
||||
<div id="name">
|
||||
<label>Nazwa</label>
|
||||
<input id="name" class="item-name" name="items[none][name]" type="text" required />
|
||||
<input id="name" class="item-name" name="name" type="text" required />
|
||||
</div>
|
||||
<div id="price">
|
||||
<div id="priceWrapper">
|
||||
<label>Cena</label>
|
||||
<price-input id="price" class="item-price" name="items[none][price]" required >
|
||||
<price-input id="price" class="item-price" name="price" required >
|
||||
</price-input>
|
||||
</div>
|
||||
<input id="submit-button" type="submit" value="Zapisz" />
|
||||
<input id="remove-button" type="submit" value="Usuń" />
|
||||
|
||||
<input type="hidden" name="items[none][picture_url]" id="picture_url" />
|
||||
<input type="hidden" name="picture_url" id="picture_url" />
|
||||
<slot name="tail"></slot>
|
||||
</form>
|
||||
</section>
|
||||
`);
|
||||
|
||||
const form = this.shadowRoot.querySelector('form');
|
||||
const imageInput = this.shadowRoot.querySelector('image-input');
|
||||
this.addEventListener('item:removed', () => {
|
||||
this.setAttribute('removed', 'removed');
|
||||
@ -58,17 +57,40 @@ customElements.define('register-item-form-row', class extends RegisterFormCompon
|
||||
ev.stopPropagation();
|
||||
this.picture_url = imageInput.url;
|
||||
});
|
||||
this.shadowRoot.querySelector('form').addEventListener('submit', ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const detail = {
|
||||
name: form.querySelector('.item-name').value,
|
||||
price: form.querySelector('price-input').value,
|
||||
picture_url: this.picture_url,
|
||||
item_order: this.idx,
|
||||
};
|
||||
this.shadowRoot.querySelector('form').addEventListener('change', ev => {
|
||||
ev.stopPropagation();
|
||||
const input = ev.target;
|
||||
const { name, value } = input;
|
||||
|
||||
switch (name) {
|
||||
case 'price': {
|
||||
this.price = parseInt(value);
|
||||
break;
|
||||
}
|
||||
case 'name': {
|
||||
this.name = value;
|
||||
break;
|
||||
}
|
||||
case 'picture_url': {
|
||||
this.picture_url = value;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.shadowRoot.querySelector('form').addEventListener('submit', ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
if (this.reportValidity()) {
|
||||
const detail = {
|
||||
name: this.name,
|
||||
price: this.price,
|
||||
picture_url: this.picture_url,
|
||||
item_order: this.idx,
|
||||
};
|
||||
this.dispatchEvent(new CustomEvent('item:submit', { bubbles: true, composed: true, detail }));
|
||||
}
|
||||
});
|
||||
@ -77,21 +99,13 @@ customElements.define('register-item-form-row', class extends RegisterFormCompon
|
||||
ev.stopPropagation();
|
||||
this.dispatchEvent(new CustomEvent('item:removed', { bubbles: true, composed: false }));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
get submitEventName() {
|
||||
return 'account:items';
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.#updateNames(this.idx);
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
super.attributeChangedCallback(name, oldV, newV);
|
||||
}
|
||||
|
||||
static attr2Field(name) {
|
||||
const field = super.attr2Field(name);
|
||||
if (field === 'remove') return 'showRemove';
|
||||
@ -99,30 +113,6 @@ customElements.define('register-item-form-row', class extends RegisterFormCompon
|
||||
return field;
|
||||
}
|
||||
|
||||
get inputs() {
|
||||
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'),
|
||||
this.shadowRoot.querySelector('.item-price'),
|
||||
this.shadowRoot.querySelector('#picture_url'),
|
||||
];
|
||||
}
|
||||
|
||||
#updateNames() {
|
||||
const idx = this.idx;
|
||||
for (const el of this.#inputs) {
|
||||
el.setAttribute('name', `items[${ idx }][${ el.id }]`);
|
||||
}
|
||||
}
|
||||
|
||||
get idx() {
|
||||
const idx = parseInt(this.getAttribute('idx'));
|
||||
return isNaN(idx) ? null : idx;
|
||||
@ -130,7 +120,6 @@ customElements.define('register-item-form-row', class extends RegisterFormCompon
|
||||
|
||||
set idx(idx) {
|
||||
this.setAttribute('idx', idx);
|
||||
this.#updateNames(idx);
|
||||
}
|
||||
|
||||
get name() {
|
||||
@ -143,12 +132,14 @@ customElements.define('register-item-form-row', class extends RegisterFormCompon
|
||||
}
|
||||
|
||||
get price() {
|
||||
return this.getAttribute('name');
|
||||
return this.getAttribute('price');
|
||||
}
|
||||
|
||||
set price(v) {
|
||||
v = parseInt(v);
|
||||
console.info('set price', v);
|
||||
this.setAttribute('price', v);
|
||||
this.shadowRoot.querySelector('.item-price').value = v;
|
||||
this.shadowRoot.querySelector('#price').value = v / 100.0;
|
||||
}
|
||||
|
||||
get picture_url() {
|
@ -1,9 +1,9 @@
|
||||
import { FORM_STYLE } from "../shared";
|
||||
import { RegisterFormComponent } from "./model";
|
||||
|
||||
const updateItems = (form) => {
|
||||
const updateItems = (rows) => {
|
||||
let idx = 0;
|
||||
for (const el of form.querySelectorAll('register-item-form-row')) {
|
||||
for (const el of rows) {
|
||||
el.idx = idx++;
|
||||
}
|
||||
return idx;
|
||||
@ -20,6 +20,10 @@ customElements.define('register-business-items-form', class extends RegisterForm
|
||||
display: flex;
|
||||
}
|
||||
|
||||
::slotted(register-business-item-form) {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
${ FORM_STYLE }
|
||||
</style>
|
||||
<form>
|
||||
@ -35,11 +39,11 @@ customElements.define('register-business-items-form', class extends RegisterForm
|
||||
`);
|
||||
this.addEventListener('item:removed', ev => {
|
||||
ev.stopPropagation();
|
||||
updateItems(this)
|
||||
updateItems(this.#rows)
|
||||
});
|
||||
this.addEventListener('form:next', ev => {
|
||||
updateItems(this);
|
||||
for (const el of this.querySelectorAll('item-form-row')) {
|
||||
updateItems(this.#rows);
|
||||
for (const el of this.#rows) {
|
||||
if (!el.reportValidity()) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
@ -49,17 +53,25 @@ customElements.define('register-business-items-form', class extends RegisterForm
|
||||
this.shadowRoot.querySelector('#add-item').addEventListener('click', ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.appendChild(document.createElement('register-item-form-row'));
|
||||
updateItems(this);
|
||||
this.appendChild(document.createElement('register-business-item-form'));
|
||||
updateItems(this.#rows);
|
||||
});
|
||||
this.mountFormHandler();
|
||||
this.mountFormHandler(() => this.#emitChange());
|
||||
}
|
||||
|
||||
get submitEventName() {
|
||||
return 'account:items';
|
||||
}
|
||||
|
||||
get elements() {
|
||||
return Array.from(this.querySelectorAll("register-item-form-row")).map(form => form.elements).flat();
|
||||
get #rows() {
|
||||
return Array.from(this.querySelectorAll("register-business-item-form"));
|
||||
}
|
||||
|
||||
#emitChange() {
|
||||
const rows = this.#rows;
|
||||
console.warn(rows);
|
||||
const items = this.#rows.map(({ price, picture_url, name }) => ({ price, picture_url, name }));
|
||||
console.warn(items);
|
||||
this.dispatchEvent(new CustomEvent(this.submitEventName, { bubbles: true, composed: true, detail: { items } }));
|
||||
}
|
||||
});
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { FORM_STYLE, PseudoForm } from "../shared";
|
||||
import { FORM_STYLE, goTo, PseudoForm } from "../shared.js";
|
||||
|
||||
customElements.define('register-business-submit-form', class extends PseudoForm {
|
||||
static get observedAttributes() {
|
||||
return ['name', 'description', 'login', 'email', 'password', 'account_type']
|
||||
return ['name', 'description', 'login', 'email', 'password', 'account-type']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -24,35 +24,36 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
||||
}
|
||||
</style>
|
||||
<form id="step-4" method="post" action="/register">
|
||||
<div id="copied">
|
||||
<input id="hidden-login" name="login" type="hidden" />
|
||||
<input id="hidden-email" name="email" 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>
|
||||
<div>
|
||||
<label>Login</label>
|
||||
<input readonly id="preview-login">
|
||||
<input readonly id="login">
|
||||
</div>
|
||||
<div>
|
||||
<label>Email</label>
|
||||
<input readonly id="preview-email">
|
||||
<input readonly id="email">
|
||||
</div>
|
||||
<div>
|
||||
<label>Password</label>
|
||||
<input readonly id="preview-pass" type="password">
|
||||
<input readonly id="password" type="password">
|
||||
</div>
|
||||
<div>
|
||||
<label>Name</label>
|
||||
<input readonly id="preview-name">
|
||||
<input readonly id="name">
|
||||
</div>
|
||||
<div>
|
||||
<label>Description</label>
|
||||
<input readonly id="preview-description">
|
||||
<input readonly id="description">
|
||||
</div>
|
||||
<input type="hidden" name="account_type" id="account_type" />
|
||||
<input type="hidden" name="account_type" id="account_type" value="Business" />
|
||||
|
||||
<div id="contacts">
|
||||
<h3>Dane kontaktowe</h3>
|
||||
<slot name="contacts"></slot>
|
||||
</div>
|
||||
|
||||
<div id="items">
|
||||
<h3>Produkty/usługi</h3>
|
||||
<slot name="items"></slot>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
@ -61,32 +62,25 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
||||
</div>
|
||||
</form>
|
||||
`);
|
||||
}
|
||||
this.shadowRoot.querySelector('form').addEventListener('submit', async (ev) => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
const res = await fetch('/register', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(this.#form),
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
updateField(name, value) {
|
||||
this.shadowRoot.querySelector(`[id="hidden-${ name }"]`).value = value;
|
||||
this.shadowRoot.querySelector(`[id="preview-${ name }"]`).value = value;
|
||||
}
|
||||
|
||||
set items(items) {
|
||||
const host = this.shadowRoot.querySelector('#items');
|
||||
host.innerHTML = ``;
|
||||
for (const row of items) {
|
||||
const el = host.appendChild(document.createElement('div'));
|
||||
el.className = 'item-view';
|
||||
const [name, price, img] = row;
|
||||
|
||||
el.innerHTML = `
|
||||
${ img.value !== ''
|
||||
? `<img src="${img.value}" alt="" />`
|
||||
: `<span>Brak zdjęcia</span>`
|
||||
}
|
||||
<input type="text" name="${ name.name }" value="${ name.value }" readonly />
|
||||
<input type="hidden" name="${ price.name }" value="${ price.value }" readonly />
|
||||
<input type="hidden" name="${ img.name }" value="${ img.value }" readonly />
|
||||
<price-view value="${ price.value }"></price-view>
|
||||
`;
|
||||
}
|
||||
if (res.ok) {
|
||||
goTo("/account?success");
|
||||
} else {
|
||||
const { error } = await res.json();
|
||||
document.querySelector('error-message').message = error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
set account_type(v) {
|
||||
@ -96,35 +90,70 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
||||
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}`)
|
||||
return this.shadowRoot.querySelector(`#${ selector }`)
|
||||
}
|
||||
|
||||
get #form() {
|
||||
return [
|
||||
'login',
|
||||
'email',
|
||||
'password',
|
||||
'name',
|
||||
'description',
|
||||
'account_type',
|
||||
].reduce((memo, name) => ({
|
||||
...memo,
|
||||
[name]: this.#hidden(name).value,
|
||||
}), {
|
||||
items: Array.from(this.querySelectorAll('local-business-item')).map(({ name, price, picture_url }) => ({
|
||||
name, price, picture_url
|
||||
})),
|
||||
contacts: Array.from(this.querySelectorAll('contact-info')).map(({ content, type }) => ({
|
||||
content,
|
||||
type
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
get payload() {
|
||||
return this.#form
|
||||
}
|
||||
});
|
||||
|
@ -168,11 +168,20 @@ export class Component extends HTMLElement {
|
||||
if (!field) continue;
|
||||
this.#setFieldValue(field, this[field]);
|
||||
}
|
||||
{
|
||||
const listener = this.listenHistory;
|
||||
listener && listener(historyDetails());
|
||||
this.#dropHistory = subscribeHistory(listener);
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.#dropHistory) this.#dropHistory();
|
||||
}
|
||||
|
||||
#dropHistory;
|
||||
listenHistory = null
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV)
|
||||
return;
|
||||
@ -279,3 +288,23 @@ export const runFbReady = (fn) => {
|
||||
};
|
||||
const fbQueue = [];
|
||||
let fbReady = false;
|
||||
|
||||
const historyQueue = new Set;
|
||||
export const goTo = (url) => {
|
||||
history.pushState({}, document.title, url);
|
||||
for (const call of historyQueue) call();
|
||||
document.dispatchEvent(new CustomEvent('history:push', { composed: true, bubbles: true, details: historyDetails() }));
|
||||
}
|
||||
export const historyDetails = () => {
|
||||
const parts = location.pathname.split('/').filter(s => s && s.length)
|
||||
return { parts }
|
||||
}
|
||||
const subscribeHistory = (cb) => {
|
||||
if (cb) {
|
||||
const call = () => {
|
||||
cb(historyDetails());
|
||||
}
|
||||
historyQueue.add(call);
|
||||
return () => historyQueue.delete(call);
|
||||
}
|
||||
}
|
||||
|
33
client/src/shared/error-message.js
Normal file
33
client/src/shared/error-message.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { Component } from "../shared.js";
|
||||
|
||||
customElements.define('error-message', class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ['message'];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(`
|
||||
<style>
|
||||
:host { display: block; }
|
||||
div {
|
||||
width: 1280px;
|
||||
background: #ffe0e0;
|
||||
border: 1px solid var(--red-color);
|
||||
margin: 8px auto auto;
|
||||
padding: 8px;
|
||||
color: var(--red-color);
|
||||
}
|
||||
</style>
|
||||
<div></div>
|
||||
`);
|
||||
}
|
||||
|
||||
get message() {
|
||||
return this.getAttribute('message');
|
||||
}
|
||||
|
||||
set message(m) {
|
||||
this.setAttribute('message', m);
|
||||
this.shadowRoot.querySelector('div').textContent = m;
|
||||
}
|
||||
})
|
@ -188,6 +188,11 @@ customElements.define('image-input', class extends Component {
|
||||
composed: true,
|
||||
detail: path
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: path
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -15,11 +15,5 @@
|
||||
{%- endif %}
|
||||
</account-view>
|
||||
{% when None %}
|
||||
<ow-account
|
||||
mode="form"
|
||||
page="{{register_page.as_str()}}"
|
||||
>
|
||||
|
||||
</ow-account>
|
||||
{% endmatch %}
|
||||
{% endblock %}
|
||||
|
@ -14,8 +14,9 @@
|
||||
<main>
|
||||
{% match error %}
|
||||
{% when Some with (e) %}
|
||||
<p class="error">{{e}}</p>
|
||||
<error-message message="{{e}"></error-message>
|
||||
{% when None %}
|
||||
<error-message></error-message>
|
||||
{% endmatch %}
|
||||
<article>
|
||||
{% include "nav.html" %}
|
||||
|
9
server/assets/templates/register.html
Normal file
9
server/assets/templates/register.html
Normal file
@ -0,0 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<ow-account>
|
||||
<register-form
|
||||
page="{{register_page.as_str()}}"
|
||||
>
|
||||
</register-form>
|
||||
</ow-account>
|
||||
{% endblock %}
|
6
server/assets/templates/sign-in.html
Normal file
6
server/assets/templates/sign-in.html
Normal file
@ -0,0 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<ow-account>
|
||||
<login-form></login-form>
|
||||
</ow-account>
|
||||
{% endblock %}
|
@ -204,6 +204,7 @@ pub struct DeleteNewsArticleInput {
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateContactInfoInput {
|
||||
#[serde(rename = "type")]
|
||||
pub contact_type: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
use tracing::error;
|
||||
|
||||
use crate::model::db::AccountType;
|
||||
use crate::model::view::Page;
|
||||
use crate::model::{db, view};
|
||||
use crate::routes::unrestricted::IndexTemplate;
|
||||
@ -15,198 +16,6 @@ use crate::routes::{HttpResult, Identity};
|
||||
use crate::view::Helper;
|
||||
use crate::{not_xss, queries, routes, utils};
|
||||
|
||||
#[derive(Default, Serialize, Template)]
|
||||
#[template(path = "account.html")]
|
||||
struct AccountTemplate {
|
||||
account: Option<db::Account>,
|
||||
error: Option<String>,
|
||||
#[serde(skip)]
|
||||
page: Page,
|
||||
h: Helper,
|
||||
register_page: RegisterPage,
|
||||
}
|
||||
|
||||
impl AccountTemplate {
|
||||
pub fn error<Error: Into<String>>(error: Error, page: Page) -> Self {
|
||||
AccountTemplate {
|
||||
error: Some(error.into()),
|
||||
page,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bad_request<Error: Into<String>>(
|
||||
req: &HttpRequest,
|
||||
error: Error,
|
||||
page: Page,
|
||||
) -> HttpResult {
|
||||
HttpResult::res(
|
||||
req,
|
||||
StatusCode::BAD_REQUEST,
|
||||
AccountTemplate::error(error, page),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/register")]
|
||||
#[tracing::instrument]
|
||||
async fn register(
|
||||
req: HttpRequest,
|
||||
form: web::Form<RegisterForm>,
|
||||
db: Data<PgPool>,
|
||||
id: Identity,
|
||||
) -> HttpResult {
|
||||
let mut form = form.into_inner();
|
||||
dbg!(&form);
|
||||
process_items(form.items.get_or_insert_default(), form.names);
|
||||
|
||||
let pool = db.into_inner();
|
||||
if form.account_type == db::AccountType::Admin {
|
||||
return HttpResult::err(&req, routes::Error::XSS);
|
||||
}
|
||||
|
||||
let mut t = pool.begin().await.unwrap();
|
||||
|
||||
let pass = match utils::encrypt(&form.password) {
|
||||
Ok(pass) => pass,
|
||||
Err(e) => {
|
||||
error!("{:?}", e);
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return AccountTemplate::bad_request(
|
||||
&req,
|
||||
"Zapisanie hasła nie powiodło się",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let res = queries::create_account(
|
||||
&mut t,
|
||||
db::CreateAccountInput {
|
||||
login: form.login,
|
||||
email: form.email,
|
||||
pass,
|
||||
facebook_id: form.facebook_id,
|
||||
account_type: form.account_type,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let account = match res {
|
||||
Ok(res) => {
|
||||
id.remember(format!("{}", res.id));
|
||||
res
|
||||
}
|
||||
Err(queries::Error::AccountTaken { .. }) => {
|
||||
return AccountTemplate::bad_request(&req, "Adres e-mail jest zajęty", Page::Register);
|
||||
}
|
||||
Err(e) => {
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return AccountTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if matches!(form.account_type, db::AccountType::Business) {
|
||||
let name = form.name.as_deref().unwrap_or_default();
|
||||
let owner_id = account.id;
|
||||
let description = form.description.as_deref().unwrap_or_default();
|
||||
|
||||
not_xss!(&req, name, t);
|
||||
not_xss!(&req, description, t);
|
||||
|
||||
let res =
|
||||
queries::create_local_business(&mut t, name.into(), owner_id, description.into()).await;
|
||||
let business = match res {
|
||||
Ok(business) => business,
|
||||
Err(e) => {
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return HttpResult::res(
|
||||
&req,
|
||||
StatusCode::BAD_REQUEST,
|
||||
AccountTemplate {
|
||||
error: Some("Problem z utworzeniem konta".into()),
|
||||
page: Page::Register,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
for (idx, item) in form.items.unwrap_or_default().into_iter().enumerate() {
|
||||
not_xss!(&req, &item.name, t);
|
||||
not_xss!(&req, &item.picture_url, t);
|
||||
|
||||
let res: sqlx::Result<db::LocalBusinessItem> = sqlx::query_as(
|
||||
r#"
|
||||
INSERT INTO local_business_items (local_business_id, name, price, item_order, picture_url)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, local_business_id, name, price, item_order, picture_url
|
||||
"#,
|
||||
)
|
||||
.bind(business.id)
|
||||
.bind(&item.name)
|
||||
.bind(item.price as i32)
|
||||
.bind(idx as i32)
|
||||
.bind(if item.picture_url.is_empty() {
|
||||
format!("--{}", uuid::Uuid::new_v4())
|
||||
} else {
|
||||
let name = item.picture_url.split('/').last().unwrap_or_default();
|
||||
let dir = utils::item_picture_write_dir(format!("{}", account.id));
|
||||
if let Err(e) = std::fs::create_dir_all(&dir) {
|
||||
error!("{e} {:?}", dir);
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return AccountTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta. Nie można zapisać zdjęcia.",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
let path = dir.join(name);
|
||||
if let Err(e) = std::fs::rename(format!(".{}", item.picture_url), &path) {
|
||||
error!("{e} {:?}", item.picture_url);
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return AccountTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta. Nie można zapisać zdjęcia.",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
let path = path.to_str().map(String::from).unwrap_or_default();
|
||||
path.strip_prefix('.')
|
||||
.map(String::from)
|
||||
.unwrap_or_else(|| path)
|
||||
})
|
||||
.fetch_one(&mut t)
|
||||
.await;
|
||||
match res {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
tracing::error!("{e}");
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return AccountTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.commit().await.unwrap();
|
||||
|
||||
HttpResult::goto(&req, "/account?success")
|
||||
}
|
||||
|
||||
#[post("/logout")]
|
||||
#[tracing::instrument]
|
||||
async fn logout(id: Identity) -> HttpResponse {
|
||||
@ -223,15 +32,38 @@ async fn logout(id: Identity) -> HttpResponse {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Template)]
|
||||
#[template(path = "sign-in.html")]
|
||||
struct SignInTemplate {
|
||||
account: Option<db::Account>,
|
||||
error: Option<String>,
|
||||
#[serde(skip)]
|
||||
page: Page,
|
||||
h: Helper,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct LoginForm {
|
||||
email: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[get("/login")]
|
||||
#[tracing::instrument]
|
||||
async fn perform_sign_in(req: HttpRequest, id: Identity) -> HttpResult {
|
||||
HttpResult::res(
|
||||
&req,
|
||||
StatusCode::OK,
|
||||
SignInTemplate {
|
||||
page: Page::Login,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[post("/login")]
|
||||
#[tracing::instrument]
|
||||
async fn login(
|
||||
async fn display_sign_in_form(
|
||||
req: HttpRequest,
|
||||
form: web::Form<LoginForm>,
|
||||
db: Data<PgPool>,
|
||||
@ -273,33 +105,45 @@ async fn login(
|
||||
)
|
||||
}
|
||||
|
||||
#[post("/upload")]
|
||||
async fn upload(
|
||||
req: HttpRequest,
|
||||
payload: actix_multipart::Multipart,
|
||||
db: Data<PgPool>,
|
||||
id: Identity,
|
||||
) -> HttpResult {
|
||||
let pool = db.into_inner();
|
||||
let mut t = crate::ok_or_internal!(&req, pool.begin().await);
|
||||
let id = match id.identity() {
|
||||
Some(id) => queries::account_by_id(&mut t, id).await.map(|a| a.id),
|
||||
_ => None,
|
||||
};
|
||||
t.commit().await.ok();
|
||||
routes::uploads::hande_upload(&req, payload, id, "accounts").await
|
||||
#[derive(Default, Serialize, Template)]
|
||||
#[template(path = "account.html")]
|
||||
struct AccountTemplate {
|
||||
account: Option<db::Account>,
|
||||
error: Option<String>,
|
||||
#[serde(skip)]
|
||||
page: Page,
|
||||
h: Helper,
|
||||
register_page: RegisterPage,
|
||||
}
|
||||
|
||||
impl AccountTemplate {
|
||||
pub fn error<Error: Into<String>>(error: Error, page: Page) -> Self {
|
||||
Self {
|
||||
error: Some(error.into()),
|
||||
page,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bad_request<Error: Into<String>>(
|
||||
req: &HttpRequest,
|
||||
error: Error,
|
||||
page: Page,
|
||||
) -> HttpResult {
|
||||
HttpResult::res(req, StatusCode::BAD_REQUEST, Self::error(error, page))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/account")]
|
||||
#[tracing::instrument]
|
||||
async fn account_page(req: HttpRequest, id: Identity, db: Data<PgPool>) -> HttpResult {
|
||||
async fn display_account_info(req: HttpRequest, id: Identity, db: Data<PgPool>) -> HttpResult {
|
||||
let pool = db.into_inner();
|
||||
let mut t = crate::ok_or_internal!(&req, pool.begin().await);
|
||||
let account = match id.identity() {
|
||||
Some(id) => queries::account_by_id(&mut t, id).await,
|
||||
_ => {
|
||||
id.forget();
|
||||
None
|
||||
return HttpResult::goto(&req, "/login");
|
||||
}
|
||||
};
|
||||
t.commit().await.ok();
|
||||
@ -314,31 +158,67 @@ async fn account_page(req: HttpRequest, id: Identity, db: Data<PgPool>) -> HttpR
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum RegisterPage {
|
||||
#[default]
|
||||
UserType = 0,
|
||||
Basic = 1,
|
||||
Business = 2,
|
||||
Items = 3,
|
||||
Contact = 4,
|
||||
Submit = 5,
|
||||
User = 40,
|
||||
#[derive(Default, Serialize, Template)]
|
||||
#[template(path = "register.html")]
|
||||
struct RegisterTemplate {
|
||||
account: Option<db::Account>,
|
||||
error: Option<String>,
|
||||
#[serde(skip)]
|
||||
page: Page,
|
||||
h: Helper,
|
||||
register_page: RegisterPage,
|
||||
}
|
||||
|
||||
impl Iterator for RegisterPage {
|
||||
type Item = Self;
|
||||
impl RegisterTemplate {
|
||||
pub fn error<Error: Into<String>>(error: Error, page: Page) -> Self {
|
||||
Self {
|
||||
error: Some(error.into()),
|
||||
page,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bad_request<Error: Into<String>>(
|
||||
req: &HttpRequest,
|
||||
error: Error,
|
||||
page: Page,
|
||||
) -> HttpResult {
|
||||
HttpResult::res(req, StatusCode::BAD_REQUEST, Self::error(error, page))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
enum RegisterPage {
|
||||
#[default]
|
||||
AccountType,
|
||||
UserAccount,
|
||||
BusinessAccount,
|
||||
BusinessDetails,
|
||||
BusinessItems,
|
||||
BusinessContacts,
|
||||
BusinessSubmit,
|
||||
}
|
||||
|
||||
struct RegisterIterator(RegisterPage, AccountType);
|
||||
|
||||
impl Iterator for RegisterIterator {
|
||||
type Item = RegisterPage;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
RegisterPage::UserType => Some(RegisterPage::Basic),
|
||||
RegisterPage::Basic => Some(RegisterPage::Business),
|
||||
RegisterPage::Business => Some(RegisterPage::Items),
|
||||
RegisterPage::Items => Some(RegisterPage::Contact),
|
||||
RegisterPage::Contact => Some(RegisterPage::Submit),
|
||||
RegisterPage::Submit => Some(RegisterPage::User),
|
||||
RegisterPage::User => None,
|
||||
match self.0 {
|
||||
RegisterPage::AccountType if self.1 == AccountType::Business => {
|
||||
Some(RegisterPage::BusinessAccount)
|
||||
}
|
||||
RegisterPage::AccountType => Some(RegisterPage::UserAccount),
|
||||
|
||||
RegisterPage::UserAccount => None,
|
||||
|
||||
RegisterPage::BusinessAccount => Some(RegisterPage::BusinessDetails),
|
||||
RegisterPage::BusinessDetails => Some(RegisterPage::BusinessItems),
|
||||
RegisterPage::BusinessItems => Some(RegisterPage::BusinessContacts),
|
||||
RegisterPage::BusinessContacts => Some(RegisterPage::BusinessSubmit),
|
||||
RegisterPage::BusinessSubmit => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,20 +226,175 @@ impl Iterator for RegisterPage {
|
||||
impl RegisterPage {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
RegisterPage::UserType => "user-type",
|
||||
RegisterPage::User => "user-account",
|
||||
RegisterPage::Basic => "business-account",
|
||||
RegisterPage::Business => "business-details",
|
||||
RegisterPage::Items => "items",
|
||||
RegisterPage::Submit => "submit",
|
||||
RegisterPage::Contact => "contact",
|
||||
RegisterPage::AccountType => "account-type",
|
||||
RegisterPage::UserAccount => "user-account",
|
||||
RegisterPage::BusinessAccount => "business-account",
|
||||
RegisterPage::BusinessDetails => "business-details",
|
||||
RegisterPage::BusinessItems => "business-items",
|
||||
RegisterPage::BusinessContacts => "business-contacts",
|
||||
RegisterPage::BusinessSubmit => "business-submit",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/register")]
|
||||
#[tracing::instrument]
|
||||
async fn save_account_details(
|
||||
req: HttpRequest,
|
||||
form: web::Json<RegisterForm>,
|
||||
db: Data<PgPool>,
|
||||
id: Identity,
|
||||
) -> HttpResult {
|
||||
let mut form = form.into_inner();
|
||||
dbg!(&form);
|
||||
process_items(form.items.get_or_insert_default(), form.names);
|
||||
|
||||
let pool = db.into_inner();
|
||||
if form.account_type == db::AccountType::Admin {
|
||||
return HttpResult::err(&req, routes::Error::XSS);
|
||||
}
|
||||
|
||||
let mut t = pool.begin().await.unwrap();
|
||||
|
||||
let pass = match utils::encrypt(&form.password) {
|
||||
Ok(pass) => pass,
|
||||
Err(e) => {
|
||||
error!("{:?}", e);
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return RegisterTemplate::bad_request(
|
||||
&req,
|
||||
"Zapisanie hasła nie powiodło się",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let res = queries::create_account(
|
||||
&mut t,
|
||||
db::CreateAccountInput {
|
||||
login: form.login,
|
||||
email: form.email,
|
||||
pass,
|
||||
facebook_id: form.facebook_id,
|
||||
account_type: form.account_type,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let account = match res {
|
||||
Ok(res) => {
|
||||
id.remember(format!("{}", res.id));
|
||||
res
|
||||
}
|
||||
Err(queries::Error::AccountTaken { .. }) => {
|
||||
return RegisterTemplate::bad_request(&req, "Adres e-mail jest zajęty", Page::Register);
|
||||
}
|
||||
Err(e) => {
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return RegisterTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if matches!(form.account_type, db::AccountType::Business) {
|
||||
let name = form.name.as_deref().unwrap_or_default();
|
||||
let owner_id = account.id;
|
||||
let description = form.description.as_deref().unwrap_or_default();
|
||||
|
||||
not_xss!(&req, name, t);
|
||||
not_xss!(&req, description, t);
|
||||
|
||||
let res =
|
||||
queries::create_local_business(&mut t, name.into(), owner_id, description.into()).await;
|
||||
let business = match res {
|
||||
Ok(business) => business,
|
||||
Err(e) => {
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return RegisterTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
for (idx, item) in form.items.unwrap_or_default().into_iter().enumerate() {
|
||||
not_xss!(&req, &item.name, t);
|
||||
not_xss!(&req, &item.picture_url, t);
|
||||
|
||||
let res: sqlx::Result<db::LocalBusinessItem> = sqlx::query_as(
|
||||
r#"
|
||||
INSERT INTO local_business_items (local_business_id, name, price, item_order, picture_url)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, local_business_id, name, price, item_order, picture_url
|
||||
"#,
|
||||
)
|
||||
.bind(business.id)
|
||||
.bind(&item.name)
|
||||
.bind(item.price as i32)
|
||||
.bind(idx as i32)
|
||||
.bind(if item.picture_url.is_empty() {
|
||||
format!("--{}", uuid::Uuid::new_v4())
|
||||
} else {
|
||||
let name = item.picture_url.split('/').last().unwrap_or_default();
|
||||
let dir = utils::item_picture_write_dir(format!("{}", account.id));
|
||||
if let Err(e) = std::fs::create_dir_all(&dir) {
|
||||
error!("{e} {:?}", dir);
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return RegisterTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta. Nie można zapisać zdjęcia.",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
let path = dir.join(name);
|
||||
if let Err(e) = std::fs::rename(format!(".{}", item.picture_url), &path) {
|
||||
error!("{e} {:?}", item.picture_url);
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return RegisterTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta. Nie można zapisać zdjęcia.",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
let path = path.to_str().map(String::from).unwrap_or_default();
|
||||
path.strip_prefix('.')
|
||||
.map(String::from)
|
||||
.unwrap_or_else(|| path)
|
||||
})
|
||||
.fetch_one(&mut t)
|
||||
.await;
|
||||
match res {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
dbg!(e);
|
||||
t.rollback().await.unwrap();
|
||||
return RegisterTemplate::bad_request(
|
||||
&req,
|
||||
"Problem z utworzeniem konta",
|
||||
Page::Register,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.commit().await.unwrap();
|
||||
|
||||
HttpResult::goto(&req, "/account?success")
|
||||
}
|
||||
|
||||
#[get("/register/{step}")]
|
||||
#[tracing::instrument]
|
||||
async fn register_page(
|
||||
async fn display_register_page(
|
||||
req: HttpRequest,
|
||||
id: Identity,
|
||||
db: Data<PgPool>,
|
||||
@ -374,13 +409,16 @@ async fn register_page(
|
||||
None
|
||||
}
|
||||
};
|
||||
if account.is_some() {
|
||||
return HttpResult::goto(&req, "/account");
|
||||
}
|
||||
let page = path.into_inner().0;
|
||||
t.commit().await.ok();
|
||||
|
||||
HttpResult::res(
|
||||
&req,
|
||||
StatusCode::OK,
|
||||
AccountTemplate {
|
||||
RegisterTemplate {
|
||||
account,
|
||||
page: Page::Account,
|
||||
register_page: page,
|
||||
@ -451,14 +489,32 @@ fn process_items(items: &mut Vec<view::BusinessItemInput>, names: HashMap<String
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/upload")]
|
||||
async fn upload(
|
||||
req: HttpRequest,
|
||||
payload: actix_multipart::Multipart,
|
||||
db: Data<PgPool>,
|
||||
id: Identity,
|
||||
) -> HttpResult {
|
||||
let pool = db.into_inner();
|
||||
let mut t = crate::ok_or_internal!(&req, pool.begin().await);
|
||||
let id = match id.identity() {
|
||||
Some(id) => queries::account_by_id(&mut t, id).await.map(|a| a.id),
|
||||
_ => None,
|
||||
};
|
||||
t.commit().await.ok();
|
||||
routes::uploads::hande_upload(&req, payload, id, "accounts").await
|
||||
}
|
||||
|
||||
pub fn configure(config: &mut ServiceConfig) {
|
||||
config
|
||||
.service(register)
|
||||
.service(logout)
|
||||
.service(login)
|
||||
.service(display_account_info)
|
||||
.service(perform_sign_in)
|
||||
.service(display_sign_in_form)
|
||||
.service(save_account_details)
|
||||
.service(display_register_page)
|
||||
.service(upload)
|
||||
.service(account_page)
|
||||
.service(register_page);
|
||||
.service(logout);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Loading…
Reference in New Issue
Block a user