Sign in form
This commit is contained in:
parent
5469fda5de
commit
277aae9341
@ -21,6 +21,11 @@
|
||||
|
||||
@import url('http://fonts.cdnfonts.com/css/noto-sans');
|
||||
|
||||
* {
|
||||
--hover-color: #f18902;
|
||||
--border-slim-color: #495057;
|
||||
}
|
||||
|
||||
main {
|
||||
font-family: 'Noto Sans', sans-serif;
|
||||
}
|
||||
|
512
assets/js/app.js
512
assets/js/app.js
@ -1,9 +1,119 @@
|
||||
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' });
|
||||
@ -14,9 +124,23 @@ customElements.define('local-services', class extends HTMLElement {
|
||||
::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" />
|
||||
<input type="text" id="filter" placeholder="Filtruj" />
|
||||
</section>
|
||||
<section id="items">
|
||||
<slot name="services"></slot>
|
||||
@ -31,9 +155,11 @@ customElements.define('local-services', class extends HTMLElement {
|
||||
});
|
||||
filter.addEventListener('keyup', ev => {
|
||||
ev.stopPropagation();
|
||||
const value = ev.target.value;
|
||||
|
||||
if (t) clearTimeout(t);
|
||||
t = setTimeout(() => {
|
||||
this.filter = ev.target.value;
|
||||
this.filter = value;
|
||||
t = null;
|
||||
}, 1000 / 3);
|
||||
});
|
||||
@ -45,9 +171,10 @@ customElements.define('local-services', class extends HTMLElement {
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV == newV) return;
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
case 'filter': return this.filter = newV;
|
||||
case 'filter':
|
||||
return this.filter = newV;
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +183,12 @@ customElements.define('local-services', class extends HTMLElement {
|
||||
}
|
||||
|
||||
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;
|
||||
@ -66,12 +199,14 @@ customElements.define('local-services', class extends HTMLElement {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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' });
|
||||
@ -79,6 +214,9 @@ customElements.define('local-service', class extends HTMLElement {
|
||||
<style>
|
||||
:host { display: block; }
|
||||
* { font-family: 'Noto Sans', sans-serif; }
|
||||
#items {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
<h2 id="name"></h2>
|
||||
<slot name="description"></slot>
|
||||
@ -93,8 +231,10 @@ customElements.define('local-service', class extends HTMLElement {
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
case 'name': return this[S].querySelector('#name').textContent = newV;
|
||||
case 'name':
|
||||
return this[S].querySelector('#name').textContent = newV;
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,6 +248,7 @@ customElements.define('local-service-item', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['name', 'price']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
@ -139,9 +280,12 @@ customElements.define('local-service-item', class extends HTMLElement {
|
||||
}
|
||||
|
||||
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;
|
||||
case 'name':
|
||||
return this[S].querySelector('#name').textContent = newV;
|
||||
case 'price':
|
||||
return this[S].querySelector('#price').value = newV;
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,6 +299,7 @@ customElements.define('ow-price', class extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ['value', 'currency']
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this[S] = this.attachShadow({ mode: 'closed' });
|
||||
@ -175,6 +320,7 @@ customElements.define('ow-price', class extends HTMLElement {
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {
|
||||
if (oldV === newV) return;
|
||||
switch (name) {
|
||||
case 'price': {
|
||||
this.value = newV;
|
||||
@ -205,3 +351,355 @@ customElements.define('ow-price', class extends HTMLElement {
|
||||
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,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<title>OS Wilno</title>
|
||||
<meta charset="UTF-8">
|
||||
@ -16,10 +16,19 @@
|
||||
</div>
|
||||
</header>
|
||||
<article>
|
||||
<h1>Lokalne usługi</h1>
|
||||
<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.to_str()}}">
|
||||
<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 %}
|
||||
|
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,
|
||||
}
|
||||
|
||||
@ -27,7 +28,7 @@ pub enum LocalServiceState {
|
||||
}
|
||||
|
||||
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