do some frontend crap
This commit is contained in:
parent
588cac6e95
commit
aa26e70fbd
8 changed files with 226 additions and 41 deletions
32
main.pl
32
main.pl
|
@ -1,16 +1,28 @@
|
||||||
:- use_module(library(http/http_server)).
|
:- use_module(library(http/html_head)).
|
||||||
|
:- use_module(library(http/html_write)).
|
||||||
:- use_module(library(http/http_client)).
|
:- use_module(library(http/http_client)).
|
||||||
:- use_module(library(http/http_json)).
|
|
||||||
:- use_module(library(http/http_header)).
|
:- use_module(library(http/http_header)).
|
||||||
:- use_module(library(http/json_convert)).
|
:- use_module(library(http/http_json)).
|
||||||
|
:- use_module(library(http/http_server)).
|
||||||
|
:- use_module(library(http/http_session)).
|
||||||
|
:- use_module(library(http/js_write)).
|
||||||
:- use_module(library(http/json)).
|
:- use_module(library(http/json)).
|
||||||
|
:- use_module(library(http/json_convert)).
|
||||||
:- use_module(library(persistency)).
|
:- use_module(library(persistency)).
|
||||||
|
|
||||||
|
:- consult('routes/products.pl').
|
||||||
|
:- consult('routes/login.pl').
|
||||||
|
:- consult('routes/users.pl').
|
||||||
|
:- consult('routes/cart.pl').
|
||||||
|
:- consult('views/products.pl').
|
||||||
|
:- consult('views/login.pl').
|
||||||
|
|
||||||
:- initialization
|
:- initialization
|
||||||
http_server([port(8080)]).
|
http_server([port(8080)]).
|
||||||
:- initialization
|
:- initialization
|
||||||
absolute_file_name('database.db', File, [access(write)]),
|
absolute_file_name('database.db', File, [access(write)]),
|
||||||
db_attach(File, []).
|
db_attach(File, []),
|
||||||
|
db_sync_all(gc(always)).
|
||||||
|
|
||||||
:- json_object
|
:- json_object
|
||||||
product(name:atom, price:integer).
|
product(name:atom, price:integer).
|
||||||
|
@ -30,9 +42,15 @@
|
||||||
:- persistent
|
:- persistent
|
||||||
token(username:atom, token:atom).
|
token(username:atom, token:atom).
|
||||||
|
|
||||||
|
:- persistent
|
||||||
|
session(session:atom, username:atom).
|
||||||
|
|
||||||
|
:- persistent
|
||||||
|
cart_entry(session:atom, items:atom).
|
||||||
|
|
||||||
:- json_object
|
:- json_object
|
||||||
error(error:atom).
|
error(error:atom).
|
||||||
|
|
||||||
:- consult('routes/products.pl').
|
reply_prolog(Term) :-
|
||||||
:- consult('routes/login.pl').
|
prolog_to_json(Term, Json),
|
||||||
:- consult('routes/users.pl').
|
reply_json(Json).
|
||||||
|
|
8
routes/cart.pl
Normal file
8
routes/cart.pl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
:- http_handler(root(cart/add/ProductName), cart_add(ProductName), []).
|
||||||
|
|
||||||
|
% GET /cart/add/ProductName
|
||||||
|
cart_add(ProductName, Request) :-
|
||||||
|
http_session_id(SessionId),
|
||||||
|
assert_cart_entry(SessionId, ProductName),
|
||||||
|
http_redirect(see_other, root(.), Request).
|
||||||
|
|
|
@ -1,25 +1,4 @@
|
||||||
:- http_handler(
|
:- http_handler(root(api/login), login_route, []).
|
||||||
root(login),
|
|
||||||
login_route,
|
|
||||||
[]
|
|
||||||
).
|
|
||||||
|
|
||||||
% Handles logging in, returning the correct Json in Reply.
|
|
||||||
handle_login(Username, Password, Reply) :-
|
|
||||||
user(Username, Hash),
|
|
||||||
crypto_password_hash(Password, Hash), !,
|
|
||||||
crypto_n_random_bytes(16, Bytes),
|
|
||||||
hex_bytes(Token, Bytes),
|
|
||||||
assert_token(Username, Token),
|
|
||||||
prolog_to_json(token(Username, Token), Reply).
|
|
||||||
|
|
||||||
handle_login(Username, _, Reply) :-
|
|
||||||
user(Username, _), !,
|
|
||||||
prolog_to_json(error(incorrect_password), Reply).
|
|
||||||
|
|
||||||
handle_login(_, _, Reply) :-
|
|
||||||
prolog_to_json(error(unknown_user), Reply).
|
|
||||||
|
|
||||||
|
|
||||||
% POST /login
|
% POST /login
|
||||||
login_route(Request) :-
|
login_route(Request) :-
|
||||||
|
@ -27,4 +6,4 @@ login_route(Request) :-
|
||||||
http_read_data(Request, Json, []),
|
http_read_data(Request, Json, []),
|
||||||
json_to_prolog(Json, login(Username, Password)),
|
json_to_prolog(Json, login(Username, Password)),
|
||||||
handle_login(Username, Password, Reply),
|
handle_login(Username, Password, Reply),
|
||||||
reply_json(Reply).
|
reply_prolog(Reply).
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
:- http_handler(
|
:- http_handler(root(api/products), products_page, []).
|
||||||
root(products),
|
|
||||||
products_page,
|
|
||||||
[]
|
|
||||||
).
|
|
||||||
|
|
||||||
% POST /products
|
% POST /products
|
||||||
products_page(Request) :-
|
products_page(Request) :-
|
||||||
|
@ -10,10 +6,9 @@ products_page(Request) :-
|
||||||
http_read_data(Request, Json, []),
|
http_read_data(Request, Json, []),
|
||||||
json_to_prolog(Json, product(Name, Price)),
|
json_to_prolog(Json, product(Name, Price)),
|
||||||
assert_product(Name, Price),
|
assert_product(Name, Price),
|
||||||
reply_json(Json).
|
reply_prolog(product(Name, Price)).
|
||||||
|
|
||||||
% GET /products
|
% GET /products
|
||||||
products_page(_Request) :-
|
products_page(_Request) :-
|
||||||
bagof(product(Name, Price), product(Name, Price), Products),
|
bagof(product(Name, Price), product(Name, Price), Products),
|
||||||
prolog_to_json(Products, Json),
|
reply_prolog(Products).
|
||||||
reply_json(Json, []).
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
:- http_handler(root(users), users_route, []).
|
:- http_handler(root(api/users), users_route, []).
|
||||||
|
|
||||||
% GET /users
|
% GET /users
|
||||||
users_route(_Request) :-
|
users_route(_Request) :-
|
||||||
bagof(user(Username), user(Username, _), Users),
|
bagof(user(Username), user(Username, _), Users),
|
||||||
prolog_to_json(Users, Json),
|
reply_prolog(Users).
|
||||||
reply_json(Json).
|
|
||||||
|
|
65
style.css
Normal file
65
style.css
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.products {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||||
|
grid-gap: 10px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product {
|
||||||
|
height: 110px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
border: 1px solid rgba(0, 0, 0, .2);
|
||||||
|
border-radius: 5px;
|
||||||
|
color: rgba(0, 0, 0, .87);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product__name {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product__price {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #27ae60;
|
||||||
|
color: #fff;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart__entry {
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, .12);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart__entry:last-child {
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, .12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart__entry > div {
|
||||||
|
margin: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart__name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart__price {
|
||||||
|
width: 65px;
|
||||||
|
}
|
64
views/login.pl
Normal file
64
views/login.pl
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
:- http_handler(root(login), login_view(Method), [method(Method), methods([get, post])]).
|
||||||
|
:- http_handler(root('style.css'), http_reply_file('style.css', []), []).
|
||||||
|
|
||||||
|
login_view(get, _Request) :-
|
||||||
|
reply_html_page(
|
||||||
|
[
|
||||||
|
title('point of shit'),
|
||||||
|
link([
|
||||||
|
type('text/css'),
|
||||||
|
rel('stylesheet'),
|
||||||
|
href('style.css')
|
||||||
|
])
|
||||||
|
],
|
||||||
|
[
|
||||||
|
h1('Login'),
|
||||||
|
form([method=post], [
|
||||||
|
div([
|
||||||
|
label(for=username, 'Username'),
|
||||||
|
input([
|
||||||
|
type=text,
|
||||||
|
placeholder='Username',
|
||||||
|
name=username,
|
||||||
|
id=username
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
div([
|
||||||
|
label(for=password, 'Password'),
|
||||||
|
input([
|
||||||
|
type=password,
|
||||||
|
placeholder='Username',
|
||||||
|
name=password,
|
||||||
|
id=password
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
div(
|
||||||
|
input([
|
||||||
|
type=submit,
|
||||||
|
value='Log in'
|
||||||
|
])
|
||||||
|
)
|
||||||
|
])
|
||||||
|
]
|
||||||
|
).
|
||||||
|
|
||||||
|
login_view(post, Request) :-
|
||||||
|
http_read_data(Request, [username=Username, password=Password], []),
|
||||||
|
handle_login(Username, Password, Reply),
|
||||||
|
reply_prolog(Reply).
|
||||||
|
|
||||||
|
|
||||||
|
% Handles logging in, returning the correct Json in Reply.
|
||||||
|
handle_login(Username, Password, Reply) :-
|
||||||
|
user(Username, Hash),
|
||||||
|
crypto_password_hash(Password, Hash), !,
|
||||||
|
assert_session(Username, Token),
|
||||||
|
Reply = true.
|
||||||
|
|
||||||
|
handle_login(Username, _, Reply) :-
|
||||||
|
user(Username, _), !,
|
||||||
|
Reply = incorrect_password.
|
||||||
|
|
||||||
|
handle_login(_, _, incorrect_user).
|
57
views/products.pl
Normal file
57
views/products.pl
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
:- http_handler(root(.), products_view, []).
|
||||||
|
:- http_handler(root('style.css'), http_reply_file('style.css', []), []).
|
||||||
|
|
||||||
|
price_str(Price, PriceStr) :-
|
||||||
|
Euro is Price / 100,
|
||||||
|
format(atom(PriceStr), '€ ~2f', Euro).
|
||||||
|
|
||||||
|
products_list([]) --> [].
|
||||||
|
products_list([product(Name, Price)|Products]) -->
|
||||||
|
{price_str(Price, PriceStr)},
|
||||||
|
html([
|
||||||
|
a([
|
||||||
|
class(product),
|
||||||
|
'data-name'(Name),
|
||||||
|
href('/cart/add/'+Name)
|
||||||
|
], [
|
||||||
|
div(class(product__name), [Name]),
|
||||||
|
div(class(product__price), [PriceStr])
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
products_list(Products).
|
||||||
|
|
||||||
|
cart([]) --> [].
|
||||||
|
cart([Name|Items]) -->
|
||||||
|
{
|
||||||
|
product(Name, Cents),
|
||||||
|
price_str(Cents, Price)
|
||||||
|
},
|
||||||
|
html([
|
||||||
|
div(class(cart__entry), [
|
||||||
|
div(class(cart__name), Name),
|
||||||
|
div(class(cart__price), Price)
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
cart(Items).
|
||||||
|
|
||||||
|
products_view(_Request) :-
|
||||||
|
http_session_id(SessionId),
|
||||||
|
bagof(product(X, Y), product(X, Y), Products),
|
||||||
|
(bagof(Name, cart_entry(SessionId, Name), CartEntries); CartEntries = []),
|
||||||
|
reply_html_page(
|
||||||
|
[
|
||||||
|
title('point of shit'),
|
||||||
|
link([
|
||||||
|
type('text/css'),
|
||||||
|
rel('stylesheet'),
|
||||||
|
href('style.css')
|
||||||
|
])
|
||||||
|
],
|
||||||
|
[
|
||||||
|
h1('Products'),
|
||||||
|
main([
|
||||||
|
div(class(products), \products_list(Products)),
|
||||||
|
div(class(cart), \cart(CartEntries))
|
||||||
|
])
|
||||||
|
]
|
||||||
|
).
|
Loading…
Reference in a new issue