tetratto/crates/app/src/public/html/littleweb/domain.lisp

275 lines
10 KiB
Common Lisp
Raw Normal View History

2025-07-08 13:35:23 -04:00
(text "{% extends \"root.html\" %} {% block head %}")
(title
(text "My services - {{ config.name }}"))
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
(main
("class" "flex flex-col gap-2")
(text "{% if user -%}")
(div
("class" "pillmenu")
(a ("href" "/services") (str (text "littleweb:label.services")))
(a ("href" "/domains") ("class" "active") (str (text "littleweb:label.domains"))))
(div
("class" "card-nest")
(div
("class" "card small")
(b
(text "{{ domain.name }}.{{ domain.tld|lower }}")))
(div
("class" "flex flex-col gap-2 card")
(code
("class" "w-content")
(a
("href" "atto://{{ domain.name }}.{{ domain.tld|lower }}")
(text "atto://{{ domain.name }}.{{ domain.tld|lower }}")))
(div
("class" "flex gap-2 flex-wrap")
(button
("class" "red lowered")
("onclick" "delete_domain()")
(icon (text "trash"))
(str (text "general:action.delete"))))))
(text "{%- endif %}")
(div
("class" "card-nest w-full")
(div
("class" "card small flex flex-col gap-2")
(div
("class" "flex items-center justify-between gap-2")
(div
("class" "flex items-center gap-2")
(icon (text "panel-top"))
(span
(str (text "littleweb:label.domain_data"))))
(div
("class" "flex gap-2")
(button
("class" "small lowered")
("title" "Help")
("onclick" "document.getElementById('domain_help').classList.toggle('hidden')")
(icon (text "circle-question-mark")))
(button
("class" "small")
("onclick" "document.getElementById('add_data').classList.toggle('hidden')")
(icon (text "plus"))
(str (text "littleweb:action.add")))))
(div
("class" "card w-full lowered flex flex-col gap-2 hidden no_p_margin")
("id" "domain_help")
(p (text "To link your domain to a site, go to the site and press \"Copy ID\"."))
(p (text "After you have the site's ID, click \"Add\" on this page, then paste the ID into the \"value\" field."))
(p (text "If you've ever managed a real domain's DNS, this should be familiar."))))
(div
("class" "card flex flex-col gap-2")
; add data
(form
("id" "add_data")
("class" "card hidden w-full lowered flex flex-col gap-2")
("onsubmit" "add_data_from_form(event)")
(div
("class" "flex gap-2")
(div
("class" "flex w-full flex-col gap-1")
(label
("for" "name")
(str (text "littleweb:label.type")))
(select
("type" "text")
("name" "type")
("id" "type")
("placeholder" "type")
("required" "")
(option ("value" "Service") (text "Site ID"))
(option ("value" "Text") (text "Text"))))
(div
("class" "flex w-full flex-col gap-1")
(label
("for" "name")
(str (text "littleweb:label.name")))
(input
("type" "text")
("name" "name")
("id" "name")
("placeholder" "name")
("minlength" "1")
("maxlength" "32"))
(span ("class" "fade") (text "Use \"@\" for root.")))
(div
("class" "flex w-full flex-col gap-1")
(label
("for" "value")
(str (text "littleweb:label.value")))
(input
("type" "text")
("name" "value")
("id" "value")
("placeholder" "value")
("required" "")
("minlength" "2")
("maxlength" "256"))))
(div
("class" "flex w-full justify-between")
(div)
(button
(icon (text "check"))
(str (text "general:action.save")))))
; data
(table
(thead
(tr
(th (text "Name"))
(th (text "Type"))
(th (text "Value"))
(th (text "Actions"))))
(tbody
(text "{% for item in domain.data -%}")
(tr
(td (text "{{ item[0] }}"))
(text "{% for k,v in item[1] -%}")
(td (text "{{ k }}"))
(td (text "{{ v }}"))
(text "{%- endfor %}")
(td
("style" "overflow: auto")
(div
("class" "dropdown")
(button
("class" "camo small")
("onclick" "trigger('atto::hooks::dropdown', [event])")
("exclude" "dropdown")
(icon (text "ellipsis")))
(div
("class" "inner")
(button
("onclick" "rename_data('{{ item[0] }}')")
(icon (text "pencil"))
(str (text "littleweb:action.rename")))
(button
("class" "red")
("onclick" "remove_data('{{ item[0] }}')")
(icon (text "trash"))
(str (text "general:action.delete")))))))
(text "{%- endfor %}"))))))
(script ("id" "domain_data") ("type" "application/json") (text "{{ domain.data|json_encode()|safe }}"))
(script
(text "globalThis.DOMAIN_DATA = JSON.parse(document.getElementById(\"domain_data\").innerText);
async function save_data() {
await trigger(\"atto::debounce\", [\"domains::update_data\"]);
fetch(\"/api/v1/domains/{{ domain.id }}/data\", {
method: \"POST\",
headers: {
\"Content-Type\": \"application/json\",
},
body: JSON.stringify({
data: DOMAIN_DATA,
}),
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
});
}
async function add_data_from_form(e) {
e.preventDefault();
await trigger(\"atto::debounce\", [\"domains::add_data\"]);
const x = {};
x[e.target.type.selectedOptions[0].value] = e.target.value.value;
if (e.target.name.value === \"\") {
e.target.name.value = \"@\";
}
const name = e.target.name.value.replace(\" \", \"_\");
if (DOMAIN_DATA.find((x) => x[0] === name)) {
return;
}
DOMAIN_DATA.push([name, x]);
await save_data();
e.target.reset();
}
async function delete_data(name) {
e.preventDefault();
await trigger(\"atto::debounce\", [\"domains::delete_data\"]);
delete DOMAIN_DATA.find((x) => x[0] === name);
await save_data();
}
async function delete_domain() {
await trigger(\"atto::debounce\", [\"domains::delete\"]);
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this?\",
]))
) {
return;
}
fetch(\"/api/v1/domains/{{ domain.id }}\", {
method: \"DELETE\",
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
});
}
async function rename_data(selector) {
await trigger(\"atto::debounce\", [\"domains::rename_data\"]);
let name = await trigger(\"atto::prompt\", [\"New name:\"]);
if (!name) {
return;
}
DOMAIN_DATA.find((x) => x[0] === selector)[0] = name.replaceAll(\" \", \"_\");
await save_data();
setTimeout(() => {
window.location.reload();
}, 150);
}
async function remove_data(name) {
await trigger(\"atto::debounce\", [\"domains::remove_data\"]);
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this?\",
]))
) {
return;
}
let i = 0;
DOMAIN_DATA.find((x) => {
i += 1;
return x[0] === name;
});
DOMAIN_DATA.splice(i - 1, 1);
await save_data();
}"))
(text "{% endblock %}")