Commit 756c31ba authored by Gaël Berthaud-Müller's avatar Gaël Berthaud-Müller
Browse files

wip frontend

parent db82f856
......@@ -9,12 +9,12 @@ function apiGet(url) {
throw new Error('Not ok');
}
return res.json();
})
});
}
function getRecords(zone) {
return apiGet(`zones/${zone}/records`)
return apiGet(`zones/${zone}/records`);
}
export {
......
......@@ -11,7 +11,7 @@ const rdataInputProperties = {
MasterServerName: {label: 'serveur primaire', type: 'text'},
Expire: {label: 'expiration', type: 'number'},
Target: {label: 'cible', type: 'text'},
}
};
const Editable = createContext(false);
......@@ -33,10 +33,10 @@ function RDataInput({ name, value = '', index = 0 }) {
} else {
return html`
<div>
<span class=label>${label}:</span>
<span class=value>${value}</span>
<dt>${label}:</dt>
<dd>${value}</dd>
</div>
`
`;
}
}
}
......@@ -56,7 +56,20 @@ function Record({name, ttl, type, rdata, index = 0}) {
<td class=domain>${name}</div>
<td class=type>${type}</div>
<td class=ttl>${ttl}</div>
<td class=rdata><${RData} rdata=${rdata} index=${index}/></div>
<td class=rdata>
<${Editable.Consumer}>
${
(editable) => {
if (editable) {
return html`<${RData} rdata=${rdata} index=${index}/>`
} else {
return html`<dl><${RData} rdata=${rdata} index=${index}/></dl>`
}
}
}
<//>
</div>
</tr>
`;
}
......@@ -70,13 +83,13 @@ function RecordList({ zone }) {
useEffect(() => {
getRecords(zone)
.then((res) => setRecords(res));
}, [])
}, []);
return html`
<${Editable.Provider} value=${editable}>
<button onclick=${toggleEdit}>${ editable ? 'Save' : 'Edit'}</button>
<table class=record-list>
<table>
<thead>
<tr>
<th>Nom</th>
......
body {
color: #2e2033;
display: flex;
height: 100%;
width: 100%;
margin: 0;
}
main {
flex-grow: 1;
}
.record-list {
zone-content table {
border-collapse: collapse;
width: 100%;
}
.record-list .rdata {
zone-content .rdata {
display: flex;
flex-wrap: wrap;
}
.record-list th, .record-list td {
zone-content th, .zone-content td {
font-weight: normal;
text-align: left;
vertical-align: top;
padding: 0.25rem;
}
.record-list thead {
zone-content thead {
background: #ccb9ff;
color: #39004d;
}
.record-list tbody tr:nth-child(even) td {
zone-content tbody tr:nth-child(even) td {
background: #ccb9ff3d;
}
.record-list tbody tr .rdata span.label,
.record-list tbody tr .rdata label {
zone-content tbody tr .rdata dt,
zone-content tbody tr .rdata label {
display: inline-block;
padding: 0.1em 0.5em;
background: #cecece;
......@@ -35,7 +45,18 @@ body {
border-radius: 0.5em;
margin-right: 0.1rem;
}
zone-content tbody tr .rdata dd {
margin: 0;
}
zone-content tbody tr .rdata dl {
display: flex;
flex-wrap: wrap;
margin: 0;
}
.record-list tbody tr .rdata > div {
zone-content tbody tr .rdata div {
margin: 0.1rem 0.5rem 0.1rem 0;
display: flex;
align-items: baseline;
}
......@@ -102,6 +102,7 @@ impl NomiloCommand for RunServerCommand {
.mount("/", routes![
ui::get_login_page,
ui::post_login_page,
ui::get_zones_page,
ui::get_zone_records_page,
])
.mount("/", static_files)
......
......@@ -18,7 +18,7 @@ pub struct LoginPage {
#[get("/login")]
pub async fn get_login_page() -> Template<'static, LoginPage> {
Template::new("login.html", LoginPage { error: None })
Template::new("pages/login.html", LoginPage { error: None })
}
#[post("/login", data = "<auth_request>")]
......@@ -38,7 +38,7 @@ pub async fn post_login_page(
match res {
Ok(_) => Ok(Redirect::to(uri!("/zones"))),
Err(models::UserError::BadCreds) => Err(Either::Left(Template::new(
"login.html",
"pages/login.html",
LoginPage {
error: Some("Identifants incorrects".to_string())
}
......
use serde_json::{Value, json};
use serde::Serialize;
use rocket::http::{Status};
use rocket::http::uri::Origin;
use crate::template::Template;
use crate::models;
......@@ -11,20 +13,52 @@ pub struct RecordsPage {
zone: String
}
// TODO: Check if origin changes if application mounted on different path
#[get("/zone/<zone>/records")]
pub async fn get_zone_records_page(user_info: models::UserInfo, zone: models::AbsoluteName, conn: DbConn) -> Result<Template<'static, RecordsPage>, Status> {
pub async fn get_zone_records_page(user_info: models::UserInfo, zone: models::AbsoluteName, conn: DbConn, origin: &Origin<'_>) -> Result<Template<'static, Value>, Status> {
let zone_name = zone.to_utf8();
conn.run(move |c| {
let zones = conn.run(move |c| {
if user_info.is_admin() {
models::Zone::get_by_name(c, &zone_name)?;
models::Zone::get_all(c)
} else {
user_info.get_zone(c, &zone_name)?;
user_info.get_zones(c)
}
}).await.map_err(|e| models::ErrorResponse::from(e).status)?;
Ok(Template::new(
"pages/zone/records.html",
json!({
"current_zone": zone.to_utf8(),
"zones": zones,
"nav_page": origin.clone().into_normalized().path().as_str(),
"nav_sections": vec!["zones", zone.to_utf8().as_str()],
})
))
}
#[get("/zones")]
pub async fn get_zones_page(user_info: models::UserInfo, conn: DbConn, origin: &Origin<'_>) -> Result<Template<'static, Value>, Status> {
let zones = conn.run(move |c| {
if user_info.is_admin() {
models::Zone::get_by_name(c, &zone_name)
models::Zone::get_all(c)
} else {
user_info.get_zone(c, &zone_name)
user_info.get_zones(c)
}
}).await.map_err(|e| models::ErrorResponse::from(e).status)?;
Ok(Template::new("zone/records.html", RecordsPage {
zone: zone.to_utf8(),
}))
Ok(Template::new(
"pages/zones.html",
json!({
"zones": zones,
"nav_page": origin.clone().into_normalized().path().as_str(),
"nav_sections": vec!["zones"],
})
))
}
{% extends "bases/base.html" %}
{% import "macros.html" as macros %}
{% block content %}
<nav aria-label="Principal">
<ul>
<li><a href="/profil">Mon profile</a></li>
<li>
{{ macros::nav_link(
content="Mes zones",
href="/zones",
current_page=nav_page,
section="zones",
current_sections=nav_sections,
) }}
<ul>
{% for zone in zones %}
<li>
{{ macros::nav_link(
content=zone.name,
href="/zone/" ~ zone.name,
current_page=nav_page,
section=zone.name,
current_sections=nav_sections,
) }}
{% endfor %}
</ul>
</li>
</ul>
</nav>
<main>
{% block main %}{% endblock main %}
</main>
{% endblock content %}
{% macro nav_link(content, href, current_page, section=False, current_sections=False, props='') %}
<a
href="{{ href }}"
{{ props }}
{% if current_page == href %}
aria-current="page"
{% elif section and section in current_sections %}
aria-current="location"
{% endif %}
>{{ content }}</a>
{% endmacro nav_link %}
{% extends "base.html" %}
{% extends "bases/base.html" %}
{% block title %}Login ⋅ {% endblock title %}
......
{% extends "bases/app.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ current_zone }} ⋅ Records ⋅ {% endblock title %}
{% block main %}
<h1>Gestion la zone {{ current_zone }}</h1>
<nav aria-label="Secondaire">
<ul>
<li>
{{ macros::nav_link(
content="Enregistrements",
href="/zone/" ~ current_zone ~ "/records",
current_page=nav_page,
) }}
</li>
<li>
{{ macros::nav_link(
content="Membres",
href="/zone/" ~ current_zone ~ "/members",
current_page=nav_page,
) }}
</li>
</ul>
</nav>
<section>
<h2>Enregistrements</h2>
<zone-content>
</zone-content>
</section>
<aside>
<h2>Aide</h2>
</aside>
{% endblock main %}
{% block scripts %}
<script type="module">
const zoneName = '{{ current_zone }}';
import { RecordList } from '/scripts/records.js';
import { html, render } from 'https://unpkg.com/htm/preact/standalone.mjs';
render(html`<${RecordList} zone=${zoneName} />`, document.querySelector('zone-content'));
</script>
{% endblock scripts %}
{% extends "bases/app.html" %}
{% block title %}Zones ⋅ {% endblock title %}
{% extends "base.html" %}
{% block title %}{{ zone }} ⋅ Records ⋅ {% endblock title %}
{% block content %}
<main></main>
{% endblock content %}
{% block scripts %}
<script type="module">
const zoneName = "{{ zone }}";
import { RecordList } from '/scripts/records.js';
import { html, render } from 'https://unpkg.com/htm/preact/standalone.mjs';
render(html`<${RecordList} zone=${zoneName} />`, document.querySelector('main'));
</script>
{% endblock scripts %}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment