Improve UI, enable add contacts in Add offer, fix business management
This commit is contained in:
parent
d02deeb732
commit
4e1755895d
48
client/src/api.js
Normal file
48
client/src/api.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
const JSON_HEADER = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const accountContacts = () =>
|
||||||
|
fetch('/contacts/list.json', {
|
||||||
|
headers: {
|
||||||
|
...JSON_HEADER,
|
||||||
|
},
|
||||||
|
method: 'GET',
|
||||||
|
}).then(res => res.json())
|
||||||
|
|
||||||
|
export const createContact = (body) =>
|
||||||
|
fetch('/contacts/create', {
|
||||||
|
headers: {
|
||||||
|
...JSON_HEADER,
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}).then(res => res.json())
|
||||||
|
|
||||||
|
export const updateContact = (body) =>
|
||||||
|
fetch('/contacts/update', {
|
||||||
|
headers: {
|
||||||
|
...JSON_HEADER,
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}).then(res => res.json())
|
||||||
|
|
||||||
|
export const deleteContact = async (body) =>
|
||||||
|
fetch('/contacts/delete', {
|
||||||
|
headers: {
|
||||||
|
...JSON_HEADER,
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}).then(res => res.json())
|
||||||
|
|
||||||
|
export const register = (body) =>
|
||||||
|
fetch('/register', {
|
||||||
|
headers: {
|
||||||
|
...JSON_HEADER,
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}).then(res => res.json())
|
@ -30,6 +30,7 @@ import "./register-form/register-user-account-form.js";
|
|||||||
import "./register-form/register-business-contacts-form.js";
|
import "./register-form/register-business-contacts-form.js";
|
||||||
import "./register-form/register-business-submit-form.js";
|
import "./register-form/register-business-submit-form.js";
|
||||||
|
|
||||||
|
import "./business-editor.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";
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ import "./marketplace/marketplace-offer.js";
|
|||||||
import "./marketplace/marketplace-offers.js";
|
import "./marketplace/marketplace-offers.js";
|
||||||
import "./marketplace/offer-form.js";
|
import "./marketplace/offer-form.js";
|
||||||
import "./marketplace/user-edit-offer.js";
|
import "./marketplace/user-edit-offer.js";
|
||||||
|
import "./marketplace/marketplace-editor.js";
|
||||||
|
|
||||||
import "./terms_and_conditions/terms-and-conditions.js";
|
import "./terms_and_conditions/terms-and-conditions.js";
|
||||||
import "./terms_and_conditions/privacy-policy.js";
|
import "./terms_and_conditions/privacy-policy.js";
|
||||||
|
73
client/src/business-editor.js
Normal file
73
client/src/business-editor.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { Component } from "./shared.js";
|
||||||
|
import * as api from "./api";
|
||||||
|
|
||||||
|
customElements.define('business-editor', class extends Component {
|
||||||
|
constructor() {
|
||||||
|
super(`
|
||||||
|
<style>
|
||||||
|
:host { display: block; }
|
||||||
|
</style>
|
||||||
|
<article>
|
||||||
|
<section>
|
||||||
|
<slot name="items"></slot>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<slot name="contacts"></slot>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
`);
|
||||||
|
|
||||||
|
this.addEventListener('contact:create', async ({ detail }) => {
|
||||||
|
await this.#createContact(detail);
|
||||||
|
});
|
||||||
|
this.addEventListener('contact:update', async ({ detail }) => {
|
||||||
|
await this.#updateContact(detail);
|
||||||
|
});
|
||||||
|
this.addEventListener('contact:delete', async ({ detail }) => {
|
||||||
|
await this.#deleteContact(detail)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async #createContact({ content, type }) {
|
||||||
|
await api.createContact({ content, type });
|
||||||
|
const { contacts } = await api.accountContacts();
|
||||||
|
this.#contacts = this.#formatContacts(contacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #updateContact({ id, content, type }) {
|
||||||
|
console.info(1);
|
||||||
|
await api.updateContact({ id, content, type });
|
||||||
|
console.info(2);
|
||||||
|
const { contacts } = await api.accountContacts();
|
||||||
|
console.info(3, contacts);
|
||||||
|
this.#contacts = this.#formatContacts(contacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #deleteContact({ id }) {
|
||||||
|
await api.deleteContact({ id });
|
||||||
|
const { contacts } = await api.accountContacts();
|
||||||
|
this.#contacts = this.#formatContacts(contacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
#formatContacts(contacts) {
|
||||||
|
return contacts.map(({ id, content, contact_type }) => `
|
||||||
|
<edit-contact-info
|
||||||
|
contact-id="${ id }"
|
||||||
|
type="${ contact_type }"
|
||||||
|
content="${ content }"
|
||||||
|
mode="view"
|
||||||
|
>
|
||||||
|
<contact-info
|
||||||
|
contact-id="${ id }"
|
||||||
|
type="${ contact_type }"
|
||||||
|
content="${ content }"
|
||||||
|
></contact-info>
|
||||||
|
</edit-contact-info>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
set #contacts(html) {
|
||||||
|
this.querySelector('contact-info-editor')
|
||||||
|
.innerHTML = html;
|
||||||
|
}
|
||||||
|
});
|
@ -142,6 +142,7 @@ customElements.define('business-item-editor', class extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const el = this.shadowRoot.querySelector('register-item-form-row');
|
const el = this.shadowRoot.querySelector('register-item-form-row');
|
||||||
|
if (!el) return;
|
||||||
el.idx = this.#idx + 1;
|
el.idx = this.#idx + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, FORM_STYLE } from "../shared";
|
import { Component, FORM_STYLE, onKeyDown } from "../shared.js";
|
||||||
|
|
||||||
customElements.define('contact-info-editor', class extends Component {
|
customElements.define('contact-info-editor', class extends Component {
|
||||||
static get observedAttributes() {
|
static get observedAttributes() {
|
||||||
@ -64,29 +64,30 @@ customElements.define('contact-info-editor', class extends Component {
|
|||||||
</article>
|
</article>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
{
|
onKeyDown(this.shadowRoot.querySelector('#content'), (ev, input) => {
|
||||||
let timeout;
|
|
||||||
const input = this.shadowRoot.querySelector('#content');
|
|
||||||
input.addEventListener('change', ev => {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.#updateContactType(input.value, null);
|
this.#updateContactType(input.value, null);
|
||||||
this.content = input.value;
|
this.content = input.value;
|
||||||
});
|
});
|
||||||
input.addEventListener('keyup', ev => {
|
|
||||||
ev.stopPropagation();
|
|
||||||
if (timeout) clearTimeout(timeout);
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
timeout = null;
|
|
||||||
this.#updateContactType(input.value, null);
|
|
||||||
this.content = input.value;
|
|
||||||
}, 1000 / 3)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.shadowRoot.querySelector('#type').addEventListener('change', ev => {
|
this.shadowRoot.querySelector('#type').addEventListener('change', ev => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.#updateContactType(null, ev.target.value);
|
this.#updateContactType(null, ev.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.shadowRoot.querySelector('form').addEventListener('submit', ev => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.dispatchEvent(new CustomEvent(
|
||||||
|
this.contact_id
|
||||||
|
? 'contact:update'
|
||||||
|
: 'contact:create',
|
||||||
|
{
|
||||||
|
composed: true,
|
||||||
|
bubbles: true,
|
||||||
|
detail: { type: this.type, content: this.content, id: this.contact_id }
|
||||||
|
}
|
||||||
|
));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get type() {
|
get type() {
|
||||||
@ -107,7 +108,8 @@ customElements.define('contact-info-editor', class extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get contact_id() {
|
get contact_id() {
|
||||||
return this.getAttribute('contact-id');
|
const v = parseInt(this.getAttribute('contact-id'));
|
||||||
|
return isNaN(v) ? null : v;
|
||||||
}
|
}
|
||||||
|
|
||||||
set contact_id(v) {
|
set contact_id(v) {
|
||||||
|
@ -20,6 +20,9 @@ customElements.define('edit-contact-info', class extends Component {
|
|||||||
#actions input {
|
#actions input {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
#cancel, #edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
:host([mode = 'view']) contact-info-editor {
|
:host([mode = 'view']) contact-info-editor {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -30,6 +33,12 @@ customElements.define('edit-contact-info', class extends Component {
|
|||||||
display: block;
|
display: block;
|
||||||
min-width: 50%;
|
min-width: 50%;
|
||||||
}
|
}
|
||||||
|
:host([mode = 'edit']) #cancel {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
:host([mode = 'view']) #edit {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
:host([mode = 'view']) ::slotted(contact-info) {
|
:host([mode = 'view']) ::slotted(contact-info) {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@ -58,6 +67,7 @@ customElements.define('edit-contact-info', class extends Component {
|
|||||||
<contact-info-editor></contact-info-editor>
|
<contact-info-editor></contact-info-editor>
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<input type="button" value="Edytuj" id="edit" />
|
<input type="button" value="Edytuj" id="edit" />
|
||||||
|
<input type="button" value="Anuluj" id="cancel" />
|
||||||
<form id="deleteButton" action="/contacts/delete" method="post">
|
<form id="deleteButton" action="/contacts/delete" method="post">
|
||||||
<input type="hidden" name="id" id="remove-id" />
|
<input type="hidden" name="id" id="remove-id" />
|
||||||
<input type="submit" value="Usuń" id="remove" />
|
<input type="submit" value="Usuń" id="remove" />
|
||||||
@ -75,16 +85,32 @@ customElements.define('edit-contact-info', class extends Component {
|
|||||||
const info = this.querySelector('contact-info');
|
const info = this.querySelector('contact-info');
|
||||||
if (!info) return;
|
if (!info) return;
|
||||||
|
|
||||||
form.contact_id = info.contact_id;
|
form.contact_id = this.contact_id;
|
||||||
form.type = info.type;
|
form.type = info.type;
|
||||||
form.content = info.content;
|
form.content = info.content;
|
||||||
|
|
||||||
this.mode = 'edit';
|
this.mode = 'edit';
|
||||||
});
|
});
|
||||||
|
this.shadowRoot.querySelector('#cancel').addEventListener('click', ev => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.mode = 'view';
|
||||||
|
});
|
||||||
|
const deleteForm = this.shadowRoot.querySelector('#deleteButton');
|
||||||
|
deleteForm.addEventListener('submit', ev => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.dispatchEvent(new CustomEvent('contact:delete', {
|
||||||
|
composed: true,
|
||||||
|
bubbles: true,
|
||||||
|
detail: { id: this.contact_id }
|
||||||
|
}))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get contact_id() {
|
get contact_id() {
|
||||||
return this.getAttribute('contact-id');
|
const v = parseInt(this.getAttribute('contact-id'));
|
||||||
|
return isNaN(v) ? null : v;
|
||||||
}
|
}
|
||||||
|
|
||||||
set contact_id(v) {
|
set contact_id(v) {
|
||||||
|
73
client/src/marketplace/marketplace-editor.js
Normal file
73
client/src/marketplace/marketplace-editor.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { Component } from "../shared.js";
|
||||||
|
import * as api from "../api.js";
|
||||||
|
|
||||||
|
customElements.define('marketplace-editor', class extends Component {
|
||||||
|
constructor() {
|
||||||
|
super(`
|
||||||
|
<style>
|
||||||
|
:host { display: block; }
|
||||||
|
</style>
|
||||||
|
<article>
|
||||||
|
<section>
|
||||||
|
<slot name="offers"></slot>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<slot name="contacts"></slot>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
`);
|
||||||
|
|
||||||
|
this.addEventListener('contact:create', async ({ detail }) => {
|
||||||
|
await this.#createContact(detail);
|
||||||
|
});
|
||||||
|
this.addEventListener('contact:update', async ({ detail }) => {
|
||||||
|
await this.#updateContact(detail);
|
||||||
|
});
|
||||||
|
this.addEventListener('contact:delete', async ({ detail }) => {
|
||||||
|
await this.#deleteContact(detail)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async #createContact({ content, type }) {
|
||||||
|
await api.createContact({ content, type });
|
||||||
|
const { contacts } = await api.accountContacts();
|
||||||
|
this.#contacts = this.#formatContacts(contacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #updateContact({ id, content, type }) {
|
||||||
|
await api.updateContact({ id, content, type });
|
||||||
|
const { contacts } = await api.accountContacts();
|
||||||
|
this.#contacts = this.#formatContacts(contacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #deleteContact({ id }) {
|
||||||
|
await api.deleteContact({ id });
|
||||||
|
const { contacts } = await api.accountContacts();
|
||||||
|
this.#contacts = this.#formatContacts(contacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
#formatContacts(contacts) {
|
||||||
|
return contacts.map(({ id, content, contact_type }) => `
|
||||||
|
<edit-contact-info
|
||||||
|
contact-id="${ id }"
|
||||||
|
type="${ contact_type }"
|
||||||
|
content="${ content }"
|
||||||
|
mode="view"
|
||||||
|
>
|
||||||
|
<contact-info
|
||||||
|
contact-id="${ id }"
|
||||||
|
type="${ contact_type }"
|
||||||
|
content="${ content }"
|
||||||
|
></contact-info>
|
||||||
|
</edit-contact-info>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
set #contacts(html) {
|
||||||
|
for (const el of this.querySelectorAll('edit-contact-info'))
|
||||||
|
el.remove();
|
||||||
|
const fragment = document.createElement('template');
|
||||||
|
fragment.innerHTML = html;
|
||||||
|
this.appendChild(fragment.content);
|
||||||
|
}
|
||||||
|
});
|
@ -17,12 +17,15 @@ customElements.define('register-business-contacts-form', class extends RegisterF
|
|||||||
::slotted(edit-contact-info) {
|
::slotted(edit-contact-info) {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
#form {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
${ BUTTON_STYLE }
|
${ BUTTON_STYLE }
|
||||||
</style>
|
</style>
|
||||||
<article>
|
<article>
|
||||||
<h2>Edycja listy danych kontaktowych</h2>
|
<h2>Edycja listy danych kontaktowych</h2>
|
||||||
<form>
|
<form>
|
||||||
<section>
|
<section id="form">
|
||||||
<contact-info-editor
|
<contact-info-editor
|
||||||
save="false"
|
save="false"
|
||||||
>
|
>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { FORM_STYLE, Router, PseudoForm } from "../shared.js";
|
import { FORM_STYLE, PseudoForm } from "../shared.js";
|
||||||
import { ErrorMessage } from "../shared/error-message.js";
|
import { ErrorMessage } from "../shared/error-message.js";
|
||||||
|
import * as api from "../api.js";
|
||||||
|
|
||||||
customElements.define('register-business-submit-form', class extends PseudoForm {
|
customElements.define('register-business-submit-form', class extends PseudoForm {
|
||||||
static get observedAttributes() {
|
static get observedAttributes() {
|
||||||
@ -17,7 +18,21 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
form > .field, form > .field > label, form > .field > input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
@media only screen and (min-device-width: 1000px) {
|
@media only screen and (min-device-width: 1000px) {
|
||||||
|
form > .field {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
form > .field > label {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
form > .field > input {
|
||||||
|
align-self: stretch;
|
||||||
|
width: calc(100% - 220px);
|
||||||
|
}
|
||||||
.item-view {
|
.item-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -28,24 +43,24 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<form id="step-4" method="post" action="/register">
|
<form method="post" action="/register">
|
||||||
<div>
|
<div class="field">
|
||||||
<label>Login</label>
|
<label>Login</label>
|
||||||
<input readonly id="login">
|
<input readonly id="login">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="field">
|
||||||
<label>Email</label>
|
<label>Email</label>
|
||||||
<input readonly id="email">
|
<input readonly id="email">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="field">
|
||||||
<label>Password</label>
|
<label>Password</label>
|
||||||
<input readonly id="password" type="password">
|
<input readonly id="password" type="password">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="field">
|
||||||
<label>Name</label>
|
<label>Name</label>
|
||||||
<input readonly id="name">
|
<input readonly id="name">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="field">
|
||||||
<label>Description</label>
|
<label>Description</label>
|
||||||
<input readonly id="description">
|
<input readonly id="description">
|
||||||
</div>
|
</div>
|
||||||
@ -70,20 +85,13 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
|||||||
this.shadowRoot.querySelector('form').addEventListener('submit', async (ev) => {
|
this.shadowRoot.querySelector('form').addEventListener('submit', async (ev) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const res = await fetch('/register', {
|
const { error } = await api.register(this.#form);
|
||||||
method: 'POST',
|
console.info(error);
|
||||||
body: JSON.stringify(this.#form),
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.ok) {
|
if (error) {
|
||||||
// Router.goTo("/account?success");
|
// Router.goTo("/account?success");
|
||||||
location.href = '/account?success';
|
location.href = '/account?success';
|
||||||
} else {
|
} else {
|
||||||
const { error } = await res.json();
|
|
||||||
ErrorMessage.errorMessage = error;
|
ErrorMessage.errorMessage = error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -307,3 +307,22 @@ export class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('popstate', () => Router.onChange());
|
window.addEventListener('popstate', () => Router.onChange());
|
||||||
|
|
||||||
|
export const onKeyDown = (input, callback) => {
|
||||||
|
let timeout;
|
||||||
|
input.addEventListener('change', ev => {
|
||||||
|
if (timeout) clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
|
||||||
|
ev.stopPropagation();
|
||||||
|
callback(ev, input)
|
||||||
|
});
|
||||||
|
input.addEventListener('keyup', ev => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (timeout) clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
timeout = null;
|
||||||
|
callback(ev, input);
|
||||||
|
}, 1000 / 3)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
101
client/src/shared/popup-window.js
Normal file
101
client/src/shared/popup-window.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { Component, BUTTON_STYLE } from "../shared.js";
|
||||||
|
|
||||||
|
customElements.define('popup-window', class extends Component {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['index', 'required', 'open'];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(`
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
article {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#bg {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background: rgba(100,100,100, .6);
|
||||||
|
}
|
||||||
|
#content {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
${ BUTTON_STYLE }
|
||||||
|
</style>
|
||||||
|
<article>
|
||||||
|
<section id="bg"> </section>
|
||||||
|
<section id="content">
|
||||||
|
<div><slot></slot></div>
|
||||||
|
<div id="required"><input id="submit" value="Zakończ" /></div>
|
||||||
|
<div id="optional">
|
||||||
|
<input id="yes" value="Tak" />
|
||||||
|
<input id="no" value="Nie" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
static attr2Field(name) {
|
||||||
|
if (name === 'open')
|
||||||
|
return 'is_open';
|
||||||
|
super.attr2Field(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
get required() {
|
||||||
|
return this.getAttribute('required') === 'yes';
|
||||||
|
}
|
||||||
|
|
||||||
|
set required(v) {
|
||||||
|
if (v === true || v === 'yes')
|
||||||
|
this.setAttribute('required', 'yes');
|
||||||
|
else
|
||||||
|
this.removeAttribute('required');
|
||||||
|
}
|
||||||
|
|
||||||
|
get index() {
|
||||||
|
const v = parseInt(this.getAttribute('index'));
|
||||||
|
return isNaN(v) ? null : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
set index(v) {
|
||||||
|
v = parseInt(v);
|
||||||
|
if (isNaN(v)) return;
|
||||||
|
this.setAttribute('index', v.toString());
|
||||||
|
this.style.zIndex = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
get is_open() {
|
||||||
|
return this.getAttribute('open') === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
set is_open(v) {
|
||||||
|
if (v === 'true' || v === true)
|
||||||
|
this.setAttribute('open', 'true');
|
||||||
|
else
|
||||||
|
this.removeAttribute('open');
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.is_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
this.is_open = true;
|
||||||
|
}
|
||||||
|
});
|
@ -1,21 +1,21 @@
|
|||||||
{% extends "../base.html" %}
|
{% extends "../base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<contact-info-editor type="email" value="{{h.email(account)}}" {{h.account_id_tag(account)}}>
|
<business-editor {{h.account_id_tag(account)}}>
|
||||||
<contact-info-list>
|
<contact-info-editor slot="contacts" type="email" value="{{h.email(account)}}" {{h.account_id_tag(account)}}>
|
||||||
{% for contact in contacts %}
|
{% for contact in contacts %}
|
||||||
<edit-contact-info contact-id="{{contact.id}}" mode="view">
|
<edit-contact-info contact-id="{{contact.id}}" mode="view">
|
||||||
<contact-info
|
<contact-info
|
||||||
contact-id="{{contact.id}}"
|
contact-id="{{contact.id}}"
|
||||||
contact-type="{{contact.contact_type}}"
|
type="{{contact.contact_type}}"
|
||||||
content="{{contact.content}}"
|
content="{{contact.content}}"
|
||||||
></contact-info>
|
></contact-info>
|
||||||
</edit-contact-info>
|
</edit-contact-info>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</contact-info-list>
|
|
||||||
</contact-info-editor>
|
</contact-info-editor>
|
||||||
|
|
||||||
<business-item-editor
|
<business-item-editor
|
||||||
|
slot="items"
|
||||||
business-id="{{business.id}}"
|
business-id="{{business.id}}"
|
||||||
name="{{business.name}}"
|
name="{{business.name}}"
|
||||||
description="{{business.description}}"
|
description="{{business.description}}"
|
||||||
@ -32,4 +32,5 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</business-item-editor>
|
</business-item-editor>
|
||||||
|
</business-editor>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
{% extends "../base.html" %}
|
{% extends "../base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<marketplace-editor {{h.account_id_tag(account)}}>
|
||||||
|
<section slot="offers">
|
||||||
<h2>Tworzenie oferty</h2>
|
<h2>Tworzenie oferty</h2>
|
||||||
<offer-form {{h.account_id_tag(account)}}></offer-form>
|
<offer-form {{h.account_id_tag(account)}}></offer-form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section slot="contacts">
|
||||||
<h3>Lista danych kontaktowych</h3>
|
<h3>Lista danych kontaktowych</h3>
|
||||||
|
<p>
|
||||||
|
Możesz dodać dodatkowe dane kontaktowe jak numer telefonu lub link do profilu Facebook.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Numer telefonu zostanie zabezpieczony i będzie wymagał wciśnięcia ikony w celu odszyfrowania.
|
||||||
|
</p>
|
||||||
<contact-info-editor {{h.account_id_tag(account)}}>
|
<contact-info-editor {{h.account_id_tag(account)}}>
|
||||||
{% for contact in contacts -%}
|
{% for contact in contacts -%}
|
||||||
<edit-contact-info
|
<edit-contact-info
|
||||||
@ -20,4 +30,6 @@
|
|||||||
</edit-contact-info>
|
</edit-contact-info>
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
</contact-info-editor>
|
</contact-info-editor>
|
||||||
|
</section>
|
||||||
|
</marketplace-editor>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -212,6 +212,7 @@ pub struct CreateContactInfoInput {
|
|||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct UpdateContactInfoInput {
|
pub struct UpdateContactInfoInput {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
#[serde(rename = "type")]
|
||||||
pub contact_type: String,
|
pub contact_type: String,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ FROM
|
|||||||
contacts
|
contacts
|
||||||
WHERE
|
WHERE
|
||||||
owner_id = $1
|
owner_id = $1
|
||||||
|
ORDER BY contact_type, id ASC
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(account_id)
|
.bind(account_id)
|
||||||
|
@ -349,9 +349,7 @@ impl Responder for HttpResult {
|
|||||||
HttpResult::GoTo {
|
HttpResult::GoTo {
|
||||||
location,
|
location,
|
||||||
content_type: ContentType::Json,
|
content_type: ContentType::Json,
|
||||||
} => HttpResponse::SeeOther()
|
} => HttpResponse::SeeOther().body(format!("{{\"location\":{:?}}}", location.as_str())),
|
||||||
.append_header(("Location", location.as_str()))
|
|
||||||
.body(format!("{{\"location\":{:?}}}", location.as_str())),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,14 +55,14 @@ async fn handle_business_items_page(
|
|||||||
t: &mut sqlx::Transaction<'_, sqlx::postgres::Postgres>,
|
t: &mut sqlx::Transaction<'_, sqlx::postgres::Postgres>,
|
||||||
id: Identity,
|
id: Identity,
|
||||||
) -> HttpResult {
|
) -> HttpResult {
|
||||||
let account = authorize!(&req, t, id);
|
let account = authorize!(req, t, id);
|
||||||
|
|
||||||
let business = crate::ok_or_internal!(
|
let business = crate::ok_or_internal!(
|
||||||
&req,
|
req,
|
||||||
queries::account_business_by_owner_id(t, account.id).await
|
queries::account_business_by_owner_id(t, account.id).await
|
||||||
);
|
);
|
||||||
let items = queries::account_items(t, account.id).await;
|
let items = queries::account_items(t, account.id).await;
|
||||||
let contacts = crate::ok_or_internal!(&req, queries::account_contacts(t, account.id).await);
|
let contacts = crate::ok_or_internal!(req, queries::account_contacts(t, account.id).await);
|
||||||
|
|
||||||
HttpResult::res(
|
HttpResult::res(
|
||||||
req,
|
req,
|
||||||
|
@ -1,22 +1,44 @@
|
|||||||
use actix_web::web::{Data, Form, ServiceConfig};
|
use actix_http::StatusCode;
|
||||||
use actix_web::{post, web, HttpRequest};
|
use actix_web::web::{Data, ServiceConfig};
|
||||||
|
use actix_web::{get, post, web, HttpRequest};
|
||||||
|
use serde::Serialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::model::{db, view};
|
use crate::model::{db, view};
|
||||||
use crate::routes::{HttpResult, Identity};
|
use crate::routes::{HttpResult, Identity};
|
||||||
use crate::{authorize, not_xss, ok_or_internal, queries, routes};
|
use crate::{authorize, not_xss, ok_or_internal, queries, routes};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
struct ContactListTemplate {
|
||||||
|
contacts: Vec<db::ContactInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/list.json")]
|
||||||
|
async fn contact_list(req: HttpRequest, id: Identity, db: Data<PgPool>) -> HttpResult {
|
||||||
|
let pool = db.into_inner();
|
||||||
|
let mut t = ok_or_internal!(&req, pool.begin().await);
|
||||||
|
let account = authorize!(&req, &mut t, id);
|
||||||
|
|
||||||
|
let contacts = queries::account_contacts(&mut t, account.id)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
t.commit().await.ok();
|
||||||
|
|
||||||
|
HttpResult::json(StatusCode::OK, ContactListTemplate { contacts })
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/create")]
|
#[post("/create")]
|
||||||
async fn create_contact(
|
async fn create_contact(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
id: Identity,
|
id: Identity,
|
||||||
db: Data<PgPool>,
|
db: Data<PgPool>,
|
||||||
form: Form<view::CreateContactInfoInput>,
|
form: web::Json<view::CreateContactInfoInput>,
|
||||||
) -> HttpResult {
|
) -> HttpResult {
|
||||||
let form = form.into_inner();
|
let form = form.into_inner();
|
||||||
dbg!(&form);
|
dbg!(&form);
|
||||||
let pool = db.into_inner();
|
let pool = db.into_inner();
|
||||||
let mut t = crate::ok_or_internal!(&req, pool.begin().await);
|
let mut t = ok_or_internal!(&req, pool.begin().await);
|
||||||
let account = authorize!(&req, &mut t, id);
|
let account = authorize!(&req, &mut t, id);
|
||||||
not_xss!(&req, &form.contact_type, t);
|
not_xss!(&req, &form.contact_type, t);
|
||||||
not_xss!(&req, &form.content, t);
|
not_xss!(&req, &form.content, t);
|
||||||
@ -48,7 +70,7 @@ async fn update_contact(
|
|||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
id: Identity,
|
id: Identity,
|
||||||
db: Data<PgPool>,
|
db: Data<PgPool>,
|
||||||
form: Form<view::UpdateContactInfoInput>,
|
form: web::Json<view::UpdateContactInfoInput>,
|
||||||
) -> HttpResult {
|
) -> HttpResult {
|
||||||
let form = form.into_inner();
|
let form = form.into_inner();
|
||||||
dbg!(&form);
|
dbg!(&form);
|
||||||
@ -86,7 +108,7 @@ async fn delete_contact(
|
|||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
id: Identity,
|
id: Identity,
|
||||||
db: Data<PgPool>,
|
db: Data<PgPool>,
|
||||||
form: Form<view::DeleteContactInfoInput>,
|
form: web::Json<view::DeleteContactInfoInput>,
|
||||||
) -> HttpResult {
|
) -> HttpResult {
|
||||||
let form = form.into_inner();
|
let form = form.into_inner();
|
||||||
dbg!(&form);
|
dbg!(&form);
|
||||||
@ -110,6 +132,7 @@ async fn delete_contact(
|
|||||||
pub fn configure(config: &mut ServiceConfig) {
|
pub fn configure(config: &mut ServiceConfig) {
|
||||||
config.service(
|
config.service(
|
||||||
web::scope("contacts")
|
web::scope("contacts")
|
||||||
|
.service(contact_list)
|
||||||
.service(create_contact)
|
.service(create_contact)
|
||||||
.service(update_contact)
|
.service(update_contact)
|
||||||
.service(delete_contact),
|
.service(delete_contact),
|
||||||
|
Loading…
Reference in New Issue
Block a user