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-submit-form.js";
|
||||
|
||||
import "./business-editor.js";
|
||||
import "./business-items/business-item.js";
|
||||
import "./business-items/business-item-editor.js";
|
||||
|
||||
@ -46,6 +47,7 @@ import "./marketplace/marketplace-offer.js";
|
||||
import "./marketplace/marketplace-offers.js";
|
||||
import "./marketplace/offer-form.js";
|
||||
import "./marketplace/user-edit-offer.js";
|
||||
import "./marketplace/marketplace-editor.js";
|
||||
|
||||
import "./terms_and_conditions/terms-and-conditions.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');
|
||||
if (!el) return;
|
||||
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 {
|
||||
static get observedAttributes() {
|
||||
@ -64,29 +64,30 @@ customElements.define('contact-info-editor', class extends Component {
|
||||
</article>
|
||||
`);
|
||||
|
||||
{
|
||||
let timeout;
|
||||
const input = this.shadowRoot.querySelector('#content');
|
||||
input.addEventListener('change', ev => {
|
||||
ev.stopPropagation();
|
||||
this.#updateContactType(input.value, null);
|
||||
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)
|
||||
});
|
||||
}
|
||||
onKeyDown(this.shadowRoot.querySelector('#content'), (ev, input) => {
|
||||
this.#updateContactType(input.value, null);
|
||||
this.content = input.value;
|
||||
});
|
||||
|
||||
this.shadowRoot.querySelector('#type').addEventListener('change', ev => {
|
||||
ev.stopPropagation();
|
||||
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() {
|
||||
@ -107,7 +108,8 @@ customElements.define('contact-info-editor', class extends Component {
|
||||
}
|
||||
|
||||
get contact_id() {
|
||||
return this.getAttribute('contact-id');
|
||||
const v = parseInt(this.getAttribute('contact-id'));
|
||||
return isNaN(v) ? null : v;
|
||||
}
|
||||
|
||||
set contact_id(v) {
|
||||
|
@ -20,6 +20,9 @@ customElements.define('edit-contact-info', class extends Component {
|
||||
#actions input {
|
||||
margin-right: 8px;
|
||||
}
|
||||
#cancel, #edit {
|
||||
display: none;
|
||||
}
|
||||
:host([mode = 'view']) contact-info-editor {
|
||||
display: none;
|
||||
}
|
||||
@ -30,6 +33,12 @@ customElements.define('edit-contact-info', class extends Component {
|
||||
display: block;
|
||||
min-width: 50%;
|
||||
}
|
||||
:host([mode = 'edit']) #cancel {
|
||||
display: block;
|
||||
}
|
||||
:host([mode = 'view']) #edit {
|
||||
display: block;
|
||||
}
|
||||
:host([mode = 'view']) ::slotted(contact-info) {
|
||||
display: block;
|
||||
}
|
||||
@ -58,6 +67,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" />
|
||||
<input type="button" value="Anuluj" id="cancel" />
|
||||
<form id="deleteButton" action="/contacts/delete" method="post">
|
||||
<input type="hidden" name="id" id="remove-id" />
|
||||
<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');
|
||||
if (!info) return;
|
||||
|
||||
form.contact_id = info.contact_id;
|
||||
form.contact_id = this.contact_id;
|
||||
form.type = info.type;
|
||||
form.content = info.content;
|
||||
|
||||
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() {
|
||||
return this.getAttribute('contact-id');
|
||||
const v = parseInt(this.getAttribute('contact-id'));
|
||||
return isNaN(v) ? null : 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) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
#form {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
${ BUTTON_STYLE }
|
||||
</style>
|
||||
<article>
|
||||
<h2>Edycja listy danych kontaktowych</h2>
|
||||
<form>
|
||||
<section>
|
||||
<section id="form">
|
||||
<contact-info-editor
|
||||
save="false"
|
||||
>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { FORM_STYLE, Router, PseudoForm } from "../shared.js";
|
||||
import { ErrorMessage } from "../shared/error-message.js";
|
||||
import { FORM_STYLE, PseudoForm } from "../shared.js";
|
||||
import { ErrorMessage } from "../shared/error-message.js";
|
||||
import * as api from "../api.js";
|
||||
|
||||
customElements.define('register-business-submit-form', class extends PseudoForm {
|
||||
static get observedAttributes() {
|
||||
@ -17,7 +18,21 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
||||
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) {
|
||||
form > .field {
|
||||
display: flex;
|
||||
}
|
||||
form > .field > label {
|
||||
min-width: 200px;
|
||||
}
|
||||
form > .field > input {
|
||||
align-self: stretch;
|
||||
width: calc(100% - 220px);
|
||||
}
|
||||
.item-view {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -28,24 +43,24 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<form id="step-4" method="post" action="/register">
|
||||
<div>
|
||||
<form method="post" action="/register">
|
||||
<div class="field">
|
||||
<label>Login</label>
|
||||
<input readonly id="login">
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label>Email</label>
|
||||
<input readonly id="email">
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label>Password</label>
|
||||
<input readonly id="password" type="password">
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label>Name</label>
|
||||
<input readonly id="name">
|
||||
</div>
|
||||
<div>
|
||||
<div class="field">
|
||||
<label>Description</label>
|
||||
<input readonly id="description">
|
||||
</div>
|
||||
@ -70,20 +85,13 @@ customElements.define('register-business-submit-form', class extends PseudoForm
|
||||
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',
|
||||
},
|
||||
});
|
||||
const { error } = await api.register(this.#form);
|
||||
console.info(error);
|
||||
|
||||
if (res.ok) {
|
||||
if (error) {
|
||||
// Router.goTo("/account?success");
|
||||
location.href ='/account?success';
|
||||
location.href = '/account?success';
|
||||
} else {
|
||||
const { error } = await res.json();
|
||||
ErrorMessage.errorMessage = error;
|
||||
}
|
||||
});
|
||||
|
@ -307,3 +307,22 @@ export class Router {
|
||||
}
|
||||
|
||||
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,35 +1,36 @@
|
||||
{% extends "../base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<contact-info-editor type="email" value="{{h.email(account)}}" {{h.account_id_tag(account)}}>
|
||||
<contact-info-list>
|
||||
<business-editor {{h.account_id_tag(account)}}>
|
||||
<contact-info-editor slot="contacts" type="email" value="{{h.email(account)}}" {{h.account_id_tag(account)}}>
|
||||
{% for contact in contacts %}
|
||||
<edit-contact-info contact-id="{{contact.id}}" mode="view">
|
||||
<contact-info
|
||||
contact-id="{{contact.id}}"
|
||||
contact-type="{{contact.contact_type}}"
|
||||
type="{{contact.contact_type}}"
|
||||
content="{{contact.content}}"
|
||||
></contact-info>
|
||||
</edit-contact-info>
|
||||
{% endfor %}
|
||||
</contact-info-list>
|
||||
</contact-info-editor>
|
||||
</contact-info-editor>
|
||||
|
||||
<business-item-editor
|
||||
business-id="{{business.id}}"
|
||||
name="{{business.name}}"
|
||||
description="{{business.description}}"
|
||||
>
|
||||
{% for item in items %}
|
||||
<business-item
|
||||
item-id="{{item.id}}"
|
||||
name="{{item.name}}"
|
||||
price="{{item.price}}"
|
||||
picture-url="{{item.picture_url}}"
|
||||
item-order="{{item.item_order}}"
|
||||
<business-item-editor
|
||||
slot="items"
|
||||
business-id="{{business.id}}"
|
||||
name="{{business.name}}"
|
||||
description="{{business.description}}"
|
||||
>
|
||||
</business-item>
|
||||
{% endfor %}
|
||||
{% for item in items %}
|
||||
<business-item
|
||||
item-id="{{item.id}}"
|
||||
name="{{item.name}}"
|
||||
price="{{item.price}}"
|
||||
picture-url="{{item.picture_url}}"
|
||||
item-order="{{item.item_order}}"
|
||||
>
|
||||
</business-item>
|
||||
{% endfor %}
|
||||
|
||||
</business-item-editor>
|
||||
</business-item-editor>
|
||||
</business-editor>
|
||||
{% endblock %}
|
||||
|
@ -1,23 +1,35 @@
|
||||
{% extends "../base.html" %}
|
||||
{% block content %}
|
||||
<h2>Tworzenie oferty</h2>
|
||||
<offer-form {{h.account_id_tag(account)}}></offer-form>
|
||||
<marketplace-editor {{h.account_id_tag(account)}}>
|
||||
<section slot="offers">
|
||||
<h2>Tworzenie oferty</h2>
|
||||
<offer-form {{h.account_id_tag(account)}}></offer-form>
|
||||
</section>
|
||||
|
||||
<h3>Lista danych kontaktowych</h3>
|
||||
<contact-info-editor {{h.account_id_tag(account)}}>
|
||||
{% for contact in contacts -%}
|
||||
<edit-contact-info
|
||||
contact-id="{{contact.id}}"
|
||||
type="{{contact.contact_type}}"
|
||||
content="{{contact.content}}"
|
||||
mode="view"
|
||||
>
|
||||
<contact-info
|
||||
contact-id="{{contact.id}}"
|
||||
type="{{contact.contact_type}}"
|
||||
content="{{contact.content}}"
|
||||
></contact-info>
|
||||
</edit-contact-info>
|
||||
{%- endfor %}
|
||||
</contact-info-editor>
|
||||
<section slot="contacts">
|
||||
<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)}}>
|
||||
{% for contact in contacts -%}
|
||||
<edit-contact-info
|
||||
contact-id="{{contact.id}}"
|
||||
type="{{contact.contact_type}}"
|
||||
content="{{contact.content}}"
|
||||
mode="view"
|
||||
>
|
||||
<contact-info
|
||||
contact-id="{{contact.id}}"
|
||||
type="{{contact.contact_type}}"
|
||||
content="{{contact.content}}"
|
||||
></contact-info>
|
||||
</edit-contact-info>
|
||||
{%- endfor %}
|
||||
</contact-info-editor>
|
||||
</section>
|
||||
</marketplace-editor>
|
||||
{% endblock %}
|
||||
|
@ -212,6 +212,7 @@ pub struct CreateContactInfoInput {
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct UpdateContactInfoInput {
|
||||
pub id: i32,
|
||||
#[serde(rename = "type")]
|
||||
pub contact_type: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ FROM
|
||||
contacts
|
||||
WHERE
|
||||
owner_id = $1
|
||||
ORDER BY contact_type, id ASC
|
||||
"#,
|
||||
)
|
||||
.bind(account_id)
|
||||
|
@ -349,9 +349,7 @@ impl Responder for HttpResult {
|
||||
HttpResult::GoTo {
|
||||
location,
|
||||
content_type: ContentType::Json,
|
||||
} => HttpResponse::SeeOther()
|
||||
.append_header(("Location", location.as_str()))
|
||||
.body(format!("{{\"location\":{:?}}}", location.as_str())),
|
||||
} => HttpResponse::SeeOther().body(format!("{{\"location\":{:?}}}", location.as_str())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,14 +55,14 @@ async fn handle_business_items_page(
|
||||
t: &mut sqlx::Transaction<'_, sqlx::postgres::Postgres>,
|
||||
id: Identity,
|
||||
) -> HttpResult {
|
||||
let account = authorize!(&req, t, id);
|
||||
let account = authorize!(req, t, id);
|
||||
|
||||
let business = crate::ok_or_internal!(
|
||||
&req,
|
||||
req,
|
||||
queries::account_business_by_owner_id(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(
|
||||
req,
|
||||
|
@ -1,22 +1,44 @@
|
||||
use actix_web::web::{Data, Form, ServiceConfig};
|
||||
use actix_web::{post, web, HttpRequest};
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::web::{Data, ServiceConfig};
|
||||
use actix_web::{get, post, web, HttpRequest};
|
||||
use serde::Serialize;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::model::{db, view};
|
||||
use crate::routes::{HttpResult, Identity};
|
||||
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")]
|
||||
async fn create_contact(
|
||||
req: HttpRequest,
|
||||
id: Identity,
|
||||
db: Data<PgPool>,
|
||||
form: Form<view::CreateContactInfoInput>,
|
||||
form: web::Json<view::CreateContactInfoInput>,
|
||||
) -> HttpResult {
|
||||
let form = form.into_inner();
|
||||
dbg!(&form);
|
||||
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);
|
||||
not_xss!(&req, &form.contact_type, t);
|
||||
not_xss!(&req, &form.content, t);
|
||||
@ -48,7 +70,7 @@ async fn update_contact(
|
||||
req: HttpRequest,
|
||||
id: Identity,
|
||||
db: Data<PgPool>,
|
||||
form: Form<view::UpdateContactInfoInput>,
|
||||
form: web::Json<view::UpdateContactInfoInput>,
|
||||
) -> HttpResult {
|
||||
let form = form.into_inner();
|
||||
dbg!(&form);
|
||||
@ -86,7 +108,7 @@ async fn delete_contact(
|
||||
req: HttpRequest,
|
||||
id: Identity,
|
||||
db: Data<PgPool>,
|
||||
form: Form<view::DeleteContactInfoInput>,
|
||||
form: web::Json<view::DeleteContactInfoInput>,
|
||||
) -> HttpResult {
|
||||
let form = form.into_inner();
|
||||
dbg!(&form);
|
||||
@ -110,6 +132,7 @@ async fn delete_contact(
|
||||
pub fn configure(config: &mut ServiceConfig) {
|
||||
config.service(
|
||||
web::scope("contacts")
|
||||
.service(contact_list)
|
||||
.service(create_contact)
|
||||
.service(update_contact)
|
||||
.service(delete_contact),
|
||||
|
Loading…
Reference in New Issue
Block a user