Sign in form
This commit is contained in:
parent
5469fda5de
commit
277aae9341
@ -1,64 +1,69 @@
|
||||
/* @media (min-width: 1200px) {
|
||||
.bg {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background:
|
||||
radial-gradient(circle, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 45%, rgba(255,255,255,1) 100%),
|
||||
no-repeat center image-set(url("/assets/images/background.webp"), url("/assets/images/background.jpeg"));
|
||||
height: 200px;
|
||||
}
|
||||
.bg {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background:
|
||||
radial-gradient(circle, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 45%, rgba(255,255,255,1) 100%),
|
||||
no-repeat center image-set(url("/assets/images/background.webp"), url("/assets/images/background.jpeg"));
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.bg h1 {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 50px;
|
||||
text-shadow: 2px 2px 2px #c5d1d8;
|
||||
color: #FFF;
|
||||
padding-top: 60px;
|
||||
}
|
||||
.bg h1 {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 50px;
|
||||
text-shadow: 2px 2px 2px #c5d1d8;
|
||||
color: #FFF;
|
||||
padding-top: 60px;
|
||||
}
|
||||
}*/
|
||||
|
||||
@import url('http://fonts.cdnfonts.com/css/noto-sans');
|
||||
|
||||
* {
|
||||
--hover-color: #f18902;
|
||||
--border-slim-color: #495057;
|
||||
}
|
||||
|
||||
main {
|
||||
font-family: 'Noto Sans', sans-serif;
|
||||
font-family: 'Noto Sans', sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: 'Noto Sans', sans-serif;
|
||||
font-family: 'Noto Sans', sans-serif;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
article {
|
||||
width: 1280px;
|
||||
margin: auto auto;
|
||||
}
|
||||
.bg {
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.bg::after {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 1%, rgba(255,255,255,0) 100%),
|
||||
no-repeat center image-set(url("/assets/images/background.webp"), url("/assets/images/background.jpeg"));
|
||||
height: 200px;
|
||||
width: calc(100% - 300px);
|
||||
max-width: 1200px;
|
||||
content: ' ';
|
||||
}
|
||||
article {
|
||||
width: 1280px;
|
||||
margin: auto auto;
|
||||
}
|
||||
.bg {
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.bg::after {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 1%, rgba(255,255,255,0) 100%),
|
||||
no-repeat center image-set(url("/assets/images/background.webp"), url("/assets/images/background.jpeg"));
|
||||
height: 200px;
|
||||
width: calc(100% - 300px);
|
||||
max-width: 1200px;
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
.bg h1 {
|
||||
font-weight: bold;
|
||||
font-size: 50px;
|
||||
text-shadow: 2px 2px 2px #c5d1d8;
|
||||
text-align: center;
|
||||
display: block;
|
||||
width: 300px;
|
||||
line-height: 4;
|
||||
}
|
||||
.bg h1 {
|
||||
font-weight: bold;
|
||||
font-size: 50px;
|
||||
text-shadow: 2px 2px 2px #c5d1d8;
|
||||
text-align: center;
|
||||
display: block;
|
||||
width: 300px;
|
||||
line-height: 4;
|
||||
}
|
||||
}
|
||||
|
852
assets/js/app.js
852
assets/js/app.js
@ -1,207 +1,705 @@
|
||||
const S = Symbol();
|
||||
|
||||
customElements.define('ow-nav', class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
section {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
align-content: stretch;
|
||||
|
||||
margin-bottom: 12px;
|
||||
color: #2b2727;
|
||||
font-family: Raleway, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
}
|
||||
section::after {
|
||||
display: block;
|
||||
content: ' ';
|
||||
border: none;
|
||||
padding: 10px 18px;
|
||||
text-decoration: none;
|
||||
color: #2b2727;
|
||||
text-transform: uppercase;
|
||||
align-self: stretch;
|
||||
flex-shrink: 20;
|
||||
flex-grow: 20;
|
||||
}
|
||||
</style>
|
||||
<section>
|
||||
<slot></slot>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
customElements.define('ow-path', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['selected', 'path'];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
a {
|
||||
display: block;
|
||||
padding: 10px 18px;
|
||||
text-decoration: none;
|
||||
color: #495057;
|
||||
text-transform: uppercase;
|
||||
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--border-slim-color);
|
||||
}
|
||||
a:hover {
|
||||
color: var(--hover-color);
|
||||
}
|
||||
:host(:not([selected])) a {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<a><slot></slot></a>
|
||||
`;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.selected = this.getAttribute('selected');
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
case 'selected':
|
||||
return this.selected = newV;
|
||||
case 'path':
|
||||
return this.path = newV;
|
||||
}
|
||||
}
|
||||
|
||||
get selected() {
|
||||
return this.getAttribute('selected') === 'selected';
|
||||
}
|
||||
|
||||
set selected(value) {
|
||||
if (value === 'selected') this.setAttribute('selected', 'selected');
|
||||
else this.removeAttribute('selected');
|
||||
}
|
||||
|
||||
get path() {
|
||||
return this.getAttribute('path') || ''
|
||||
}
|
||||
|
||||
set path(value) {
|
||||
if (!value || value === '') {
|
||||
this.removeAttribute('path');
|
||||
return;
|
||||
}
|
||||
this.setAttribute('path', value);
|
||||
this[S].querySelector('a').setAttribute('href', value);
|
||||
}
|
||||
});
|
||||
|
||||
customElements.define('local-services', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['filter']
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
::slotted(local-service[local-services-visible="invisible"]) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<section>
|
||||
<input type="text" id="filter" />
|
||||
</section>
|
||||
<section id="items">
|
||||
<slot name="services"></slot>
|
||||
</section>
|
||||
`;
|
||||
{
|
||||
const filter = shadow.querySelector('#filter');
|
||||
let t = null;
|
||||
filter.addEventListener('change', ev => {
|
||||
ev.stopPropagation();
|
||||
this.filter = ev.target.value;
|
||||
});
|
||||
filter.addEventListener('keyup', ev => {
|
||||
ev.stopPropagation();
|
||||
if (t) clearTimeout(t);
|
||||
t = setTimeout(() => {
|
||||
this.filter = ev.target.value;
|
||||
t = null;
|
||||
}, 1000 / 3);
|
||||
});
|
||||
}
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['filter']
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.filter = this.getAttribute('filter');
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
::slotted(local-service[local-services-visible="invisible"]) {
|
||||
display: none;
|
||||
}
|
||||
input {
|
||||
font-size: 20pt;
|
||||
line-height: 2.6em;
|
||||
height: 2.6em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
border:none;
|
||||
outline:none;
|
||||
display: block;
|
||||
background: transparent;
|
||||
border-bottom: 1px solid #ccc;
|
||||
text-indent: 20px;
|
||||
}
|
||||
</style>
|
||||
<section>
|
||||
<input type="text" id="filter" placeholder="Filtruj" />
|
||||
</section>
|
||||
<section id="items">
|
||||
<slot name="services"></slot>
|
||||
</section>
|
||||
`;
|
||||
{
|
||||
const filter = shadow.querySelector('#filter');
|
||||
let t = null;
|
||||
filter.addEventListener('change', ev => {
|
||||
ev.stopPropagation();
|
||||
this.filter = ev.target.value;
|
||||
});
|
||||
filter.addEventListener('keyup', ev => {
|
||||
ev.stopPropagation();
|
||||
const value = ev.target.value;
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV == newV) return;
|
||||
switch (name) {
|
||||
case 'filter': return this.filter = newV;
|
||||
}
|
||||
}
|
||||
if (t) clearTimeout(t);
|
||||
t = setTimeout(() => {
|
||||
this.filter = value;
|
||||
t = null;
|
||||
}, 1000 / 3);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get filter() {
|
||||
return this.getAttribute('filter');
|
||||
}
|
||||
connectedCallback() {
|
||||
this.filter = this.getAttribute('filter');
|
||||
}
|
||||
|
||||
set filter(value) {
|
||||
this.setAttribute('filter', value);
|
||||
for (const el of this.querySelectorAll('local-service')) {
|
||||
if (!el.name) continue;
|
||||
if (el.name.includes(value)) {
|
||||
el.setAttribute('local-services-visible', 'visible');
|
||||
} else {
|
||||
el.setAttribute('local-services-visible', 'invisible');
|
||||
}
|
||||
}
|
||||
}
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
case 'filter':
|
||||
return this.filter = newV;
|
||||
}
|
||||
}
|
||||
|
||||
get filter() {
|
||||
return this.getAttribute('filter');
|
||||
}
|
||||
|
||||
set filter(value) {
|
||||
if (!value || value === '') {
|
||||
this.removeAttribute('filter');
|
||||
for (const el of this.querySelectorAll('local-service')) {
|
||||
el.removeAttribute('local-services-visible');
|
||||
}
|
||||
} else {
|
||||
this.setAttribute('filter', value);
|
||||
for (const el of this.querySelectorAll('local-service')) {
|
||||
if (!el.name) continue;
|
||||
if (el.name.includes(value)) {
|
||||
el.setAttribute('local-services-visible', 'visible');
|
||||
} else {
|
||||
el.setAttribute('local-services-visible', 'invisible');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
customElements.define('local-service', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['name', 'service-id', 'state']
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
</style>
|
||||
<h2 id="name"></h2>
|
||||
<slot name="description"></slot>
|
||||
<section id="items">
|
||||
<slot name="item"></slot>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['name', 'service-id', 'state']
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this[S].querySelector('#name').textContent = this.getAttribute('name');
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
#items {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
<h2 id="name"></h2>
|
||||
<slot name="description"></slot>
|
||||
<section id="items">
|
||||
<slot name="item"></slot>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
switch (name) {
|
||||
case 'name': return this[S].querySelector('#name').textContent = newV;
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this[S].querySelector('#name').textContent = this.getAttribute('name');
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.getAttribute('name') || ''
|
||||
}
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
case 'name':
|
||||
return this[S].querySelector('#name').textContent = newV;
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.getAttribute('name') || ''
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
customElements.define('local-service-item', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['name', 'price']
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
#item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
h3 {
|
||||
font-weight: normal;
|
||||
}
|
||||
#price {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<section id="item">
|
||||
<h3 id="name"></h3>
|
||||
<ow-price id="price" />
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['name', 'price']
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this[S].querySelector('#name').textContent = this.getAttribute('name');
|
||||
this[S].querySelector('#price').value = this.price();
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
#item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
h3 {
|
||||
font-weight: normal;
|
||||
}
|
||||
#price {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<section id="item">
|
||||
<h3 id="name"></h3>
|
||||
<ow-price id="price" />
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
switch (name) {
|
||||
case 'name': return this[S].querySelector('#name').textContent = newV;
|
||||
case 'price': return this[S].querySelector('#price').value = newV;
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this[S].querySelector('#name').textContent = this.getAttribute('name');
|
||||
this[S].querySelector('#price').value = this.price();
|
||||
}
|
||||
|
||||
price(s) {
|
||||
const n = parseInt(s || this.getAttribute('price'));
|
||||
return isNaN(n) ? 0 : n;
|
||||
}
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
case 'name':
|
||||
return this[S].querySelector('#name').textContent = newV;
|
||||
case 'price':
|
||||
return this[S].querySelector('#price').value = newV;
|
||||
}
|
||||
}
|
||||
|
||||
price(s) {
|
||||
const n = parseInt(s || this.getAttribute('price'));
|
||||
return isNaN(n) ? 0 : n;
|
||||
}
|
||||
});
|
||||
|
||||
customElements.define('ow-price', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['value', 'currency']
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
#price {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<span id="price"></span>
|
||||
`;
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return ['value', 'currency']
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this[S].querySelector('#price').textContent = this.formatted;
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
#price {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<span id="price"></span>
|
||||
`;
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
switch (name) {
|
||||
case 'price': {
|
||||
this.value = newV;
|
||||
this[S].querySelector('#price').textContent = this.formatted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this[S].querySelector('#price').textContent = this.formatted;
|
||||
}
|
||||
|
||||
get formatted() {
|
||||
let v = this.value;
|
||||
let major = Math.ceil(v / 100);
|
||||
let minor = v % 100;
|
||||
let formatted = `${major},${ minor < 10 ? `0${minor}` : minor }`;
|
||||
return `${formatted}${this.currency}`
|
||||
}
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
case 'price': {
|
||||
this.value = newV;
|
||||
this[S].querySelector('#price').textContent = this.formatted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get value() {
|
||||
const n = parseInt(this.getAttribute('value'));
|
||||
return isNaN(n) ? 0 : n;
|
||||
}
|
||||
get formatted() {
|
||||
let v = this.value;
|
||||
let major = Math.ceil(v / 100);
|
||||
let minor = v % 100;
|
||||
let formatted = `${ major },${ minor < 10 ? `0${ minor }` : minor }`;
|
||||
return `${ formatted }${ this.currency }`
|
||||
}
|
||||
|
||||
set value(v) {
|
||||
this.setAttribute('value', v)
|
||||
}
|
||||
get value() {
|
||||
const n = parseInt(this.getAttribute('value'));
|
||||
return isNaN(n) ? 0 : n;
|
||||
}
|
||||
|
||||
get currency() {
|
||||
return this.getAttribute('currency') || 'PLN'
|
||||
}
|
||||
set value(v) {
|
||||
this.setAttribute('value', v)
|
||||
}
|
||||
|
||||
get currency() {
|
||||
return this.getAttribute('currency') || 'PLN'
|
||||
}
|
||||
});
|
||||
|
||||
customElements.define('ow-account', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['mode']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
article > * {
|
||||
display: none;
|
||||
}
|
||||
:host([mode="login"]) login-form, :host([mode="login"]) #switch-register {
|
||||
display: block;
|
||||
}
|
||||
:host([mode="register"]) register-form, :host([mode="register"]) #switch-login {
|
||||
display: block;
|
||||
}
|
||||
a{
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
a:hover {
|
||||
color: var(--hover-color);
|
||||
border-bottom-color: var(--hover-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
<article>
|
||||
<login-form></login-form>
|
||||
<register-form></register-form>
|
||||
<section id="switch-login">
|
||||
<a>Nie masz konta? Utwórz nowe</a>
|
||||
</section>
|
||||
<section id="switch-register">
|
||||
<a>Masz konta? Zaloguj się</a>
|
||||
</section>
|
||||
</article>
|
||||
`;
|
||||
shadow.querySelector('#switch-login > a').addEventListener('click', ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.mode = 'login';
|
||||
});
|
||||
shadow.querySelector('#switch-register > a').addEventListener('click', ev => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.mode = 'register';
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.mode = 'login';
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
}
|
||||
}
|
||||
|
||||
get mode() {
|
||||
return this.getAttribute('mode');
|
||||
}
|
||||
|
||||
set mode(value) {
|
||||
value = value === 'login' || value === 'register' ? value : 'login';
|
||||
this.setAttribute('mode', value);
|
||||
}
|
||||
});
|
||||
|
||||
const FORM_STYLE = `
|
||||
form {
|
||||
display: block;
|
||||
}
|
||||
form > div {
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
form > div > input, form > div > textarea {
|
||||
font-size: 16px;
|
||||
|
||||
border: none;
|
||||
border-bottom-style: none;
|
||||
border-bottom-width: medium;
|
||||
border-bottom: 1px solid rgba(0,0,0,.1);
|
||||
border-radius: 2px;
|
||||
padding: 0;
|
||||
|
||||
height: 36px;
|
||||
background: #fff;
|
||||
color: rgba(0,0,0,.8);
|
||||
font-size: 14px;
|
||||
|
||||
box-shadow: none !important;
|
||||
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: calc(1.5em + 0.75rem + 2px);
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-clip: padding-box;
|
||||
transition: border-color .15s ease-in-out , -webkit-box-shadow .15s ease-in-out;
|
||||
transition: border-color .15s ease-in-out , box-shadow .15s ease-in-out;
|
||||
transition: border-color .15s ease-in-out , box-shadow .15s ease-in-out , -webkit-box-shadow .15s ease-in-out;
|
||||
}
|
||||
form > div > input[type="text"],
|
||||
form > div > input[type="number"],
|
||||
form > div > input[type="email"],
|
||||
form > div > input[type="password"],
|
||||
form > div > textarea {
|
||||
width: calc(100% - 1.5rem - 2px);
|
||||
}
|
||||
form > div > label {
|
||||
color: #000;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
|
||||
display: inline-block;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
form > div > input[type="button"], form > div > input[type="submit"] {
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-width: 1px;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
box-shadow: 0 10px 20px -6px rgba(0,0,0,.12);
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
transition: .3s;
|
||||
|
||||
background: #46b5d1;
|
||||
color: #fff;
|
||||
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
transition: color .15s ease-in-out,
|
||||
background-color .15s ease-in-out,
|
||||
border-color .15s ease-in-out,
|
||||
box-shadow .15s ease-in-out,
|
||||
-webkit-box-shadow .15s ease-in-out;
|
||||
|
||||
width: auto;
|
||||
height: calc(1.5em + 0.75rem + 2px);
|
||||
padding: .375rem .75rem;
|
||||
}
|
||||
`;
|
||||
|
||||
customElements.define('register-form', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['step']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
article > form { display: none; }
|
||||
:host([step="1"]) #step-1 { display: block; }
|
||||
:host([step="2"]) #step-2 { display: block; }
|
||||
:host([step="3"]) #step-3 { display: block; }
|
||||
|
||||
${ FORM_STYLE }
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.actions > input:first-child {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.actions > input:last-child {
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
<article>
|
||||
<form id="step-1">
|
||||
<div>
|
||||
<label>Login</label>
|
||||
<input id="login" name="login" placeholder="Login" type="text" required />
|
||||
</div>
|
||||
<div>
|
||||
<label>E-Mail</label>
|
||||
<input name="email" placeholder="Email" type="email" required />
|
||||
</div>
|
||||
<div>
|
||||
<label>Hasło</label>
|
||||
<input name="pass" placeholder="Hasło" type="password" required />
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Następny" />
|
||||
</div>
|
||||
</form>
|
||||
<form id="step-2">
|
||||
<div>
|
||||
<input name="name" placeholder="Nazwa usługi" type="text" required />
|
||||
</div>
|
||||
<div>
|
||||
<label>description</label>
|
||||
<textarea name="description" required></textarea>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<input type="button" value="Wróć" />
|
||||
<input type="submit" value="Następny" />
|
||||
</div>
|
||||
</form>
|
||||
<form id="step-3">
|
||||
<input id="hidden-login" name="login" type="hidden" />
|
||||
<input id="hidden-email" name="email" type="hidden" />
|
||||
<input id="hidden-pass" name="pass" type="hidden" />
|
||||
<input id="hidden-name" name="name" type="hidden" />
|
||||
|
||||
<div class="actions">
|
||||
<input type="button" value="Wróć" />
|
||||
<input type="submit" value="Dodaj usługi/produkty" />
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
`;
|
||||
|
||||
const copyForm = (form) => {
|
||||
for (const el of form.elements) {
|
||||
if (el.name === '') continue;
|
||||
finalForm.querySelector(`[id="hidden-${el.name}"]`).value = el.value;
|
||||
}
|
||||
};
|
||||
const finalForm = shadow.querySelector('#step-3');
|
||||
{
|
||||
const el = shadow.querySelector('#step-1');
|
||||
el.addEventListener('submit', ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
copyForm(el);
|
||||
this.step = 2;
|
||||
});
|
||||
}
|
||||
{
|
||||
const el = shadow.querySelector('#step-2');
|
||||
el.addEventListener('submit', ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
copyForm(el);
|
||||
this.step = 3;
|
||||
});
|
||||
el.querySelector('.actions > input[type="button"]').addEventListener('click', ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.step = 1;
|
||||
});
|
||||
}
|
||||
{
|
||||
const el = finalForm;
|
||||
el.addEventListener('submit', ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
copyForm(el);
|
||||
let content = [
|
||||
...shadow.querySelector('#step-1').elements,
|
||||
...shadow.querySelector('#step-2').elements,
|
||||
...shadow.querySelector('#step-3').elements,
|
||||
]
|
||||
.filter(el => el && el.name !== '')
|
||||
.reduce((mem, el) => ({ ...mem, [el.name]: el.value }), {});
|
||||
console.info(content);
|
||||
});
|
||||
el.querySelector('.actions > input[type="button"]').addEventListener('click', ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.step = 2;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.step = 1;
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
}
|
||||
}
|
||||
|
||||
get step() {
|
||||
const step = parseInt(this.getAttribute('step'));
|
||||
return isNaN(step) ? 1 : step;
|
||||
}
|
||||
|
||||
set step(n) {
|
||||
this.setAttribute('step', n);
|
||||
}
|
||||
});
|
||||
|
||||
customElements.define('login-form', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return []
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
${ FORM_STYLE }
|
||||
</style>
|
||||
<form>
|
||||
<div>
|
||||
<label>Login</label>
|
||||
<input name="login" placeholder="Login" type="text" required />
|
||||
</div>
|
||||
<div>
|
||||
<label>Hasło</label>
|
||||
<input name="pass" placeholder="Hasło" type="password" required />
|
||||
</div>
|
||||
<div>
|
||||
<input type="button" value="Zaloguj" />
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
29
assets/templates/account.html
Normal file
29
assets/templates/account.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<title>OS Wilno</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="/assets/css/reset.css" rel="stylesheet"/>
|
||||
<link href="/assets/css/app.css" rel="stylesheet"/>
|
||||
<script type="module" src=/assets/js/app.js></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<div class="bg">
|
||||
<h1>OS Wilno</h1>
|
||||
</div>
|
||||
</header>
|
||||
<article>
|
||||
<ow-nav>
|
||||
<ow-path path="/">Lokalne Usługi</ow-path>
|
||||
<ow-path path="/">Aktualności</ow-path>
|
||||
<ow-path path="/account" selected="selected">Konto</ow-path>
|
||||
</ow-nav>
|
||||
<ow-account>
|
||||
</ow-account>
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -1,38 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OSWilno</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="/assets/css/reset.css" rel="stylesheet" />
|
||||
<link href="/assets/css/app.css" rel="stylesheet" />
|
||||
<script type="module" src=/assets/js/app.js></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<div class="bg">
|
||||
<h1>OS Wilno</h1>
|
||||
</div>
|
||||
</header>
|
||||
<article>
|
||||
<h1>Lokalne usługi</h1>
|
||||
<local-services>
|
||||
{% for service in services %}
|
||||
<local-service slot="services" service-id="{{service.id}}" name="{{service.name}}" state="{{service.state.to_str()}}">
|
||||
{% for line in service.description.lines() %}
|
||||
<p slot="description">{{line}}</p>
|
||||
{% endfor %}
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<title>OS Wilno</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="/assets/css/reset.css" rel="stylesheet"/>
|
||||
<link href="/assets/css/app.css" rel="stylesheet"/>
|
||||
<script type="module" src=/assets/js/app.js></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<div class="bg">
|
||||
<h1>OS Wilno</h1>
|
||||
</div>
|
||||
</header>
|
||||
<article>
|
||||
<ow-nav>
|
||||
<ow-path path="/" selected="selected">Lokalne Usługi</ow-path>
|
||||
<ow-path path="/">Aktualności</ow-path>
|
||||
<ow-path path="/account">Konto</ow-path>
|
||||
</ow-nav>
|
||||
<local-services>
|
||||
{% for service in services %}
|
||||
<local-service
|
||||
slot="services"
|
||||
service-id="{{service.id}}"
|
||||
name="{{service.name}}"
|
||||
state="{{service.state.as_str()}}"
|
||||
>
|
||||
{% for line in service.description.lines() %}
|
||||
<p slot="description">{{line}}</p>
|
||||
{% endfor %}
|
||||
|
||||
{% for item in service.items %}
|
||||
<local-service-item slot="item" name="{{item.name}}" price="{{item.price}}">
|
||||
</local-service-item>
|
||||
{% endfor %}
|
||||
{% for item in service.items %}
|
||||
<local-service-item slot="item" name="{{item.name}}" price="{{item.price}}">
|
||||
</local-service-item>
|
||||
{% endfor %}
|
||||
|
||||
</local-service>
|
||||
{% endfor %}
|
||||
</local-services>
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</local-service>
|
||||
{% endfor %}
|
||||
</local-services>
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
18
db/seed/001_init.psql
Normal file
18
db/seed/001_init.psql
Normal file
@ -0,0 +1,18 @@
|
||||
INSERT INTO accounts (login, pass, email)
|
||||
VALUES ('Foo', 'Bar', 'foo@example.com');
|
||||
|
||||
INSERT INTO local_services (name, description, owner_id)
|
||||
VALUES ('Cheap Tees', 'Unlimited possiblities! You can move the water masks to create your own frame. It gives you an opportunity to fit the layers to your image. Create great final effect with seconds', 1),
|
||||
('Tema Model Agency Ltd', 'Special for website developers and app ui designers, to preview their apps in a professional way, showcasing details and focus on Responsive Design for Website and apps, Vol 09.', 1),
|
||||
('Neet Online Test Series - Nots', 'Advanced, easy to edit mockup. It contains everything you need to create a realistic look of your project. Guarantees the a good look for bright and dark designs and perfect fit to the shape. Easy to navigate, well described layers, friendly help file.', 1);
|
||||
|
||||
INSERT INTO local_service_items (name, price, local_service_id, item_order)
|
||||
VALUES ('Water Frame', 23423, 1, 1),
|
||||
('Macbook Laptop Display 2.0', 927, 1, 2),
|
||||
('Paper Band', 920, 1, 3),
|
||||
('Artbox', 2500, 2, 1),
|
||||
('School Backpacks', 2150, 2, 2),
|
||||
('Premium Paper Cup', 2090, 3, 1),
|
||||
('Beer Black Bottle', 5000, 3, 2),
|
||||
('Circle Photo Frame', 2300, 3, 3),
|
||||
('Grey T-Shirt', 3430, 3, 4);
|
1
migrations/20220704123702_add_email.sql
Normal file
1
migrations/20220704123702_add_email.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE accounts ADD COLUMN email text not null default '' unique;
|
@ -8,6 +8,7 @@ use uuid::Uuid;
|
||||
pub struct Account {
|
||||
pub id: i32,
|
||||
pub login: String,
|
||||
pub email: String,
|
||||
pub pass: String,
|
||||
}
|
||||
|
||||
@ -19,15 +20,15 @@ pub enum Role {
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Type)]
|
||||
pub enum LocalServiceState {
|
||||
Pending,
|
||||
Approved,
|
||||
Banned,
|
||||
Pinned,
|
||||
Internal,
|
||||
Pending,
|
||||
Approved,
|
||||
Banned,
|
||||
Pinned,
|
||||
Internal,
|
||||
}
|
||||
|
||||
impl LocalServiceState {
|
||||
pub fn to_str(&self) -> &str {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Pending => "Pending",
|
||||
Self::Approved => "Approved",
|
||||
|
@ -66,10 +66,25 @@ FROM local_service_items
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "account.html")]
|
||||
struct AccountTemplate;
|
||||
|
||||
#[get("/account")]
|
||||
async fn account() -> HttpResponse {
|
||||
HttpResponse::Ok().body(AccountTemplate.render().unwrap())
|
||||
}
|
||||
|
||||
pub fn configure(config: &mut ServiceConfig) {
|
||||
config
|
||||
.service(Files::new("/assets/images", "./assets/images"))
|
||||
.service(Files::new("/assets/css", "./assets/css"))
|
||||
.service(Files::new("/assets/js", "./assets/js").use_etag(true).prefer_utf8(true).show_files_listing())
|
||||
.service(index);
|
||||
.service(
|
||||
Files::new("/assets/js", "./assets/js")
|
||||
.use_etag(true)
|
||||
.prefer_utf8(true)
|
||||
.show_files_listing(),
|
||||
)
|
||||
.service(index)
|
||||
.service(account);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user