tetratto/crates/app/src/public/html/developer/app.lisp
2025-06-14 20:26:54 -04:00

348 lines
14 KiB
Common Lisp

(text "{% extends \"root.html\" %} {% block head %}")
(title
(text "{{ app.title }} - {{ config.name }}"))
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
(main
("class" "flex flex-col gap-2")
(div
("class" "w-full flex flex-col gap-2")
(div
("id" "manage_fields")
("class" "card lowered flex flex-col gap-2")
(text "{% if is_helper -%}")
(div
("class" "card-nest")
(div
("class" "card small")
(b (str (text "developer:label.change_quota_status"))))
(div
("class" "card")
(select
("onchange" "save_quota_status(event)")
(option
("value" "Limited")
("selected" "{% if app.quota_status == 'Limited' -%}true{% else %}false{%- endif %}")
(text "Limited"))
(option
("value" "Unlimited")
("selected" "{% if app.quota_status == 'Unlimited' -%}true{% else %}false{%- endif %}")
(text "Unlimited")))))
(text "{%- endif %}")
(div
("class" "card-nest")
(div
("class" "card small")
(b (str (text "developer:label.change_title"))))
(form
("class" "card flex flex-col gap-2")
("onsubmit" "change_title(event)")
(div
("class" "flex flex-col gap-1")
(label
("for" "title")
(text "{{ text \"communities:label.new_title\" }}"))
(input
("type" "text")
("name" "title")
("id" "title")
("placeholder" "new title")
("required" "")
("minlength" "2")))
(button
("class" "primary")
(text "{{ icon \"check\" }}")
(span
(text "{{ text \"general:action.save\" }}")))))
(div
("class" "card-nest")
(div
("class" "card small")
(b (str (text "developer:label.change_homepage"))))
(form
("class" "card flex flex-col gap-2")
("onsubmit" "change_homepage(event)")
(div
("class" "flex flex-col gap-1")
(label
("for" "homepage")
(text "{{ text \"developer:label.homepage\" }}"))
(input
("type" "url")
("name" "homepage")
("id" "homepage")
("placeholder" "new homepage")
("required" "")
("minlength" "2")))
(button
("class" "primary")
(text "{{ icon \"check\" }}")
(span
(text "{{ text \"general:action.save\" }}")))))
(div
("class" "card-nest")
(div
("class" "card small")
(b (str (text "developer:label.change_redirect"))))
(form
("class" "card flex flex-col gap-2")
("onsubmit" "change_redirect(event)")
(div
("class" "flex flex-col gap-1")
(label
("for" "redirect")
(text "{{ text \"developer:label.redirect\" }}"))
(input
("type" "url")
("name" "redirect")
("id" "redirect")
("placeholder" "new redirect URL")
("required" "")
("minlength" "2")))
(button
("class" "primary")
(text "{{ icon \"check\" }}")
(span
(text "{{ text \"general:action.save\" }}")))))
(div
("class" "card-nest")
(div
("class" "card small")
(b (str (text "developer:label.manage_scopes"))))
(form
("class" "card flex flex-col gap-2")
("onsubmit" "change_scopes(event)")
(div
("class" "flex flex-col gap-1")
(label
("for" "scopes")
(text "{{ text \"developer:label.scopes\" }}"))
(input
("type" "text")
("name" "scopes")
("id" "scopes")
("placeholder" "new scopes")
("required" "")
("minlength" "2")
("value" "{% for scope in app.scopes -%} {{ scope }} {% endfor %}")))
(pre ("class" "hidden red w-full") (code ("id" "scope_error_message") ("style" "white-space: pre-wrap")))
(details
(summary ("class" "button lowered small") (icon (text "circle-help")) (text "Help"))
(div
("class" "card flex flex-col gap-1")
(span ("class" "fade") (text "Scopes should be separated by a single space."))
(a
("class" "button")
("href" "https://tetratto.com/reference/tetratto/model/oauth/enum.AppScope.html#variants")
("target" "_blank")
(icon (text "external-link")) (text "Docs"))))
(button
("class" "primary")
(text "{{ icon \"check\" }}")
(span
(text "{{ text \"general:action.save\" }}"))))))
(div
("class" "card flex flex-col gap-2")
(ul
(li (b (text "Homepage: ")) (text "{{ app.homepage }}"))
(li (b (text "Redirect URL: ")) (text "{{ app.redirect }}"))
(li (b (text "Quota status: ")) (text "{{ app.quota_status }}"))
(li (b (text "User grants: ")) (text "{{ app.grants }}"))
(li (b (text "Grant URL: ")) (text "{{ config.host }}/auth/connections_link/app/{{ app.id }}")))
(a
("class" "button")
("href" "https://tetratto.com/reference/tetratto/model/apps/struct.ThirdPartyApp.html#structfield.redirect")
("target" "_blank")
(icon (text "external-link")) (text "Docs")))
(div
("class" "card-nest")
(div
("class" "card small flex gap-1 items-center red")
(text "{{ icon \"skull\" }}")
(b
(text "{{ text \"communities:label.danger_zone\" }}")))
(div
("class" "card flex flex-wrap gap-2")
(button
("class" "red lowered")
("onclick" "delete_app()")
(text "{{ icon \"trash\" }}")
(span (str (text "developer:action.delete"))))))
(div
("class" "flex gap-2 flex-wrap")
(a
("href" "/developer")
("class" "button secondary")
(text "{{ icon \"arrow-left\" }}")
(span
(text "{{ text \"general:action.back\" }}"))))))
(script
(text "setTimeout(() => {
globalThis.save_quota_status = (event) => {
const selected = event.target.selectedOptions[0];
fetch(\"/api/v1/apps/{{ app.id }}/quota_status\", {
method: \"POST\",
headers: {
\"Content-Type\": \"application/json\",
},
body: JSON.stringify({
quota_status: selected.value,
}),
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
});
};
globalThis.change_title = async (e) => {
e.preventDefault();
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this?\",
]))
) {
return;
}
fetch(\"/api/v1/apps/{{ app.id }}/title\", {
method: \"POST\",
headers: {
\"Content-Type\": \"application/json\",
},
body: JSON.stringify({
title: e.target.title.value,
}),
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
});
};
globalThis.change_homepage = async (e) => {
e.preventDefault();
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this?\",
]))
) {
return;
}
fetch(\"/api/v1/apps/{{ app.id }}/homepage\", {
method: \"POST\",
headers: {
\"Content-Type\": \"application/json\",
},
body: JSON.stringify({
homepage: e.target.homepage.value,
}),
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
});
};
globalThis.change_redirect = async (e) => {
e.preventDefault();
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this?\",
]))
) {
return;
}
fetch(\"/api/v1/apps/{{ app.id }}/redirect\", {
method: \"POST\",
headers: {
\"Content-Type\": \"application/json\",
},
body: JSON.stringify({
redirect: e.target.redirect.value,
}),
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
});
};
globalThis.change_scopes = async (e) => {
e.preventDefault();
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this? This will only impact new grants.\",
]))
) {
return;
}
fetch(\"/api/v1/apps/{{ app.id }}/scopes\", {
method: \"POST\",
headers: {
\"Content-Type\": \"application/json\",
},
body: JSON.stringify({
scopes: e.target.scopes.value.trim().split(\" \"),
}),
})
.then((res) => res.text())
.then((res) => {
if (res.startsWith(\"{\")) {
const r = JSON.parse(res);
trigger(\"atto::toast\", [r.ok ? \"success\" : \"error\", r.message]);
document.getElementById(\"scope_error_message\").parentElement.classList.add(\"hidden\");
} else {
document.getElementById(\"scope_error_message\").innerText = res;
document.getElementById(\"scope_error_message\").parentElement.classList.remove(\"hidden\");
document.getElementById(\"scope_error_message\").parentElement.parentElement.querySelector(\"details\").setAttribute(\"open\", \"\");
}
});
};
globalThis.delete_app = async () => {
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this? This action is permanent.\",
]))
) {
return;
}
fetch(`/api/v1/apps/{{ app.id }}`, {
method: \"DELETE\",
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
});
};
}, 250);"))
(text "{% endblock %}")