Admin page refresh. (#6)
Added settings and options. Settings parsing catch. Fix postSetting API responses. Adjusted spinner visibility toggle. Reviewed-on: #6 Co-authored-by: Armored-Dragon <forgejo3829105@armoreddragon.com> Co-committed-by: Armored-Dragon <forgejo3829105@armoreddragon.com>pull/2/head
parent
94424df08f
commit
83da8100dc
|
@ -19,6 +19,12 @@ let settings = {
|
|||
HIDE_LOGIN: false,
|
||||
BLOG_UPLOADING: false,
|
||||
|
||||
CD_RSS: false,
|
||||
CD_AP: false,
|
||||
|
||||
WEBSITE_NAME: "",
|
||||
PLAUSIBLE_URL: "",
|
||||
|
||||
USER_MINIMUM_PASSWORD_LENGTH: 7,
|
||||
|
||||
BLOG_MINIMUM_TITLE_LENGTH: 7,
|
||||
|
@ -372,9 +378,18 @@ async function _getSettings() {
|
|||
let found_value = await prisma.setting.findUnique({ where: { id: key } });
|
||||
if (!found_value) return;
|
||||
|
||||
return (settings[key] = JSON.parse(found_value.value));
|
||||
let value;
|
||||
// Parse JSON if possible
|
||||
try {
|
||||
value = JSON.parse(found_value.value);
|
||||
} catch {
|
||||
value = found_value.value;
|
||||
}
|
||||
|
||||
return (settings[key] = value);
|
||||
});
|
||||
}
|
||||
|
||||
async function getSetting(key, { parse = true }) {
|
||||
if (!settings[key]) return null;
|
||||
|
||||
|
|
|
@ -43,9 +43,7 @@ async function postSetting(request, response) {
|
|||
if (!user.success) return response.json({ success: false, message: user.message });
|
||||
if (user.data.role !== "ADMIN") return response.json({ success: false, message: "User is not permitted" });
|
||||
|
||||
await core.postSetting(request.body.setting_name, request.body.value);
|
||||
|
||||
response.json({ success: true });
|
||||
response.json(await core.postSetting(request.body.setting_name, request.body.value));
|
||||
}
|
||||
async function deleteImage(req, res) {
|
||||
// TODO: Permissions for deleting image
|
||||
|
|
|
@ -2,7 +2,8 @@ const external = require("./core/external_api");
|
|||
const core = require("./core/core");
|
||||
|
||||
function getDefaults(req) {
|
||||
return { logged_in_user: req.session.user, website_name: process.env.WEBSITE_NAME, settings: core.settings };
|
||||
// TODO: Fix reference to website_name
|
||||
return { logged_in_user: req.session.user, website_name: core.settings.WEBSITE_NAME || "Yet-Another-Blog", settings: core.settings };
|
||||
}
|
||||
|
||||
async function index(request, response) {
|
||||
|
|
|
@ -2,6 +2,7 @@ body {
|
|||
margin: 0;
|
||||
background-color: #111;
|
||||
color: white;
|
||||
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
@ -58,11 +59,18 @@ button:focus {
|
|||
background-color: #122d57;
|
||||
}
|
||||
|
||||
button.good {
|
||||
button.good,
|
||||
a.good {
|
||||
background-color: #015b01;
|
||||
}
|
||||
|
||||
button.bad {
|
||||
button.yellow,
|
||||
a.yellow {
|
||||
background-color: #4a4a00;
|
||||
}
|
||||
|
||||
button.bad,
|
||||
a.bad {
|
||||
background-color: #8e0000;
|
||||
}
|
||||
|
||||
|
@ -75,9 +83,78 @@ button.bad {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.page .horizontal-button-container button {
|
||||
width: 100px;
|
||||
.page .horizontal-button-container button,
|
||||
.page .horizontal-button-container a {
|
||||
width: 120px;
|
||||
min-height: 30px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.page .horizontal-button-container button span,
|
||||
.page .horizontal-button-container a span {
|
||||
margin: auto;
|
||||
}
|
||||
.page .horizontal-button-container button:last-of-type,
|
||||
.page .horizontal-button-container a:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
.page .blog-admin {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page .pagination {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.page .pagination a {
|
||||
height: 40px;
|
||||
width: 150px;
|
||||
margin-right: 5px;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
background-color: #222;
|
||||
}
|
||||
.page .pagination a span {
|
||||
margin: auto;
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
}
|
||||
.page .pagination a:last-of-type {
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.page .pagination a.disabled {
|
||||
filter: brightness(50%);
|
||||
}
|
||||
.page .pagination .page-list {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.page .pagination .page-list a {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
background-color: #222;
|
||||
border-radius: 10px;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
.page .pagination .page-list a span {
|
||||
margin: auto;
|
||||
color: white;
|
||||
}
|
||||
.page .pagination .page-list a:first-of-type {
|
||||
margin: auto 10px auto auto;
|
||||
}
|
||||
.page .pagination .page-list a:last-of-type {
|
||||
margin: auto auto auto 0;
|
||||
}
|
||||
.page .pagination .page-list a.active {
|
||||
background-color: #993d00;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
|
@ -86,7 +163,7 @@ button.bad {
|
|||
|
||||
@media screen and (max-width: 1010px) {
|
||||
.page {
|
||||
width: 100%;
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
|
@ -95,32 +172,73 @@ button.bad {
|
|||
padding: 10px 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container .settings-group .settings-row {
|
||||
.container .category-navigation {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.container .category-navigation button {
|
||||
width: 100%;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
}
|
||||
.container .category-navigation button span {
|
||||
margin: auto;
|
||||
color: white;
|
||||
}
|
||||
.container .category-navigation button:not(.active) {
|
||||
background-color: #4c515e;
|
||||
}
|
||||
.container .category-navigation button:hover,
|
||||
.container .category-navigation button:focus {
|
||||
filter: brightness(50%);
|
||||
}
|
||||
.container .setting-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 4px;
|
||||
min-height: 30px;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container .settings-group .settings-row .label {
|
||||
.container .setting-row .setting-title {
|
||||
font-size: 18px;
|
||||
margin: auto auto auto 0;
|
||||
}
|
||||
.container .settings-group .settings-row .button-group {
|
||||
.container .setting-row .setting-toggleable {
|
||||
min-width: 150px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: auto 0 auto auto;
|
||||
}
|
||||
.container .settings-group .settings-row .button-group .spinner {
|
||||
margin: auto 10px auto auto;
|
||||
.container .setting-row .setting-toggleable .spinner {
|
||||
margin: auto 0 auto auto;
|
||||
animation: spin 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
.container .settings-group .settings-row .button-group button {
|
||||
height: 30px;
|
||||
padding: 0px 20px;
|
||||
.container .setting-row .setting-toggleable input {
|
||||
padding: 0;
|
||||
margin: auto 0 auto auto;
|
||||
width: 100px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.container .settings-group .settings-row:nth-child(even) {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
.container .setting-row .setting-toggleable input[type=text] {
|
||||
width: 350px;
|
||||
}
|
||||
.container .setting-row .setting-toggleable button {
|
||||
width: 125px;
|
||||
margin-left: auto;
|
||||
height: 30px;
|
||||
}
|
||||
.container .setting-row:nth-child(even) {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.container .setting-row:nth-child(odd) {
|
||||
background-color: #191919;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
|
|
|
@ -6,35 +6,89 @@
|
|||
padding: 10px 20px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.settings-group {
|
||||
.settings-row {
|
||||
.category-navigation {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
.label {
|
||||
margin: auto auto auto 0;
|
||||
|
||||
span {
|
||||
margin: auto;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
button:not(.active) {
|
||||
background-color: #4c515e;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
filter: brightness(50%);
|
||||
}
|
||||
|
||||
button:active {
|
||||
// display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-height: 30px;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.setting-title {
|
||||
font-size: 18px;
|
||||
margin: auto auto auto 0;
|
||||
}
|
||||
|
||||
.setting-toggleable {
|
||||
min-width: 150px;
|
||||
display: flex;
|
||||
margin: auto 0 auto auto;
|
||||
|
||||
.spinner {
|
||||
margin: auto 0 auto auto;
|
||||
animation: spin 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
input {
|
||||
padding: 0;
|
||||
margin: auto 0 auto auto;
|
||||
.spinner {
|
||||
margin: auto 10px auto auto;
|
||||
animation: spin 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
button {
|
||||
height: 30px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
// width: 100%;
|
||||
width: 100px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 125px;
|
||||
margin-left: auto;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
.settings-row:nth-child(even) {
|
||||
background-color: #00000033;
|
||||
}
|
||||
}
|
||||
.setting-row:nth-child(even) {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.setting-row:nth-child(odd) {
|
||||
background-color: #191919;
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
|
|
|
@ -9,10 +9,11 @@ async function toggleState(setting_name, new_value, element_id) {
|
|||
|
||||
const response = await request("/setting", "POST", form);
|
||||
|
||||
qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.add("hidden");
|
||||
|
||||
// TODO: On failure, notify the user
|
||||
// Check response for errors
|
||||
if (response.body.success) {
|
||||
qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.add("hidden");
|
||||
|
||||
// Update visual to reflect current setting
|
||||
// Class
|
||||
const add_class = new_value ? "good" : "bad";
|
||||
|
@ -30,3 +31,42 @@ async function toggleState(setting_name, new_value, element_id) {
|
|||
qs(`#${element_id}`).setAttribute("onclick", add_function);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateValue(key_pressed, setting_name, new_value, element_id) {
|
||||
if (key_pressed !== 13) return;
|
||||
// Show spinner
|
||||
qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.remove("hidden");
|
||||
|
||||
const form = {
|
||||
setting_name: setting_name,
|
||||
value: new_value,
|
||||
};
|
||||
|
||||
const response = await request("/setting", "POST", form);
|
||||
|
||||
qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.add("hidden");
|
||||
|
||||
// TODO: On failure, notify the user
|
||||
// Check response for errors
|
||||
if (response.body.success) {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleActiveCategory(category_id) {
|
||||
// Pages ----------------
|
||||
// Hide all pages
|
||||
qsa(".category-page").forEach((page) => {
|
||||
page.classList.add("hidden");
|
||||
});
|
||||
// Show requested page
|
||||
qs(`#${category_id}`).classList.remove("hidden");
|
||||
|
||||
// Navigation bar -------
|
||||
// Unactive all buttons
|
||||
qsa(".category-navigation button").forEach((btn) => {
|
||||
btn.classList.remove("active");
|
||||
});
|
||||
// Active current page
|
||||
qs(`#${category_id}-nav-btn`).classList.add("active");
|
||||
qs(`#${category_id}-nav-btn`).blur(); // Unfocus the button
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
const qs = (selector) => document.querySelector(selector);
|
||||
const qsa = (selector) => document.querySelectorAll(selector);
|
||||
|
||||
// TODO: Try/Catch for failed requests
|
||||
async function request(url, method, body) {
|
||||
const response = await fetch(url, {
|
||||
method: method,
|
||||
|
|
|
@ -13,35 +13,38 @@
|
|||
|
||||
<div class="page">
|
||||
<div class="container">
|
||||
<div class="settings-group">
|
||||
<h1>Accounts</h1>
|
||||
<div class="settings-row">
|
||||
<div class="label">Account registration</div>
|
||||
<div class="button-group">
|
||||
<div class="spinner hidden">
|
||||
<div>↺</div>
|
||||
</div>
|
||||
<% if (!settings.ACCOUNT_REGISTRATION ) { %>
|
||||
<button id="registration_toggle" onclick="toggleState('ACCOUNT_REGISTRATION', true, this.id)" class="bad"><div>Disabled</div></button>
|
||||
<% } else { %>
|
||||
<button id="registration_toggle" onclick="toggleState('ACCOUNT_REGISTRATION', false, this.id)" class="good"><div>Enabled</div></button>
|
||||
<%}%>
|
||||
</div>
|
||||
</div>
|
||||
<div class="category-navigation">
|
||||
<button id="accounts-nav-btn" class="active" onclick="toggleActiveCategory('accounts')"><span>Accounts</span></button>
|
||||
<button id="content-delivery-nav-btn" onclick="toggleActiveCategory('content-delivery')"><span>Content Delivery</span></button>
|
||||
<button id="groups-nav-btn" onclick="toggleActiveCategory('groups')"><span>Groups</span></button>
|
||||
<button id="yab-master-nav-btn" onclick="toggleActiveCategory('yab-master')"><span>YAB</span></button>
|
||||
</div>
|
||||
<div id="accounts" class="category-page">
|
||||
<!-- Account registration -->
|
||||
<%- include("partials/admin-setting-toggle.ejs", {setting: {name: 'ACCOUNT_REGISTRATION', name_pretty: 'Account registration', enabled:
|
||||
settings.ACCOUNT_REGISTRATION}}) %>
|
||||
<!-- Hide login -->
|
||||
<%- include("partials/admin-setting-toggle.ejs", {setting: {name: 'HIDE_LOGIN', name_pretty: 'Hide Login', enabled: settings.HIDE_LOGIN}}) %>
|
||||
<!-- Minimum password length -->
|
||||
<%- include("partials/admin-setting-number.ejs", {setting: {name:'USER_MINIMUM_PASSWORD_LENGTH', name_pretty: 'Minimum Password Length', value:
|
||||
settings.USER_MINIMUM_PASSWORD_LENGTH}}) %>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="label">Hide Login</div>
|
||||
<div class="button-group">
|
||||
<div class="spinner hidden">
|
||||
<div>↺</div>
|
||||
</div>
|
||||
<% if (!settings.HIDE_LOGIN ) { %>
|
||||
<button id="hidelogin_toggle" onclick="toggleState('HIDE_LOGIN', true, this.id)" class="bad"><div>Disabled</div></button>
|
||||
<% } else { %>
|
||||
<button id="hidelogin_toggle" onclick="toggleState('HIDE_LOGIN', false, this.id)" class="good"><div>Enabled</div></button>
|
||||
<%}%>
|
||||
</div>
|
||||
</div>
|
||||
<div id="content-delivery" class="category-page hidden">
|
||||
<!-- RSS -->
|
||||
<%- include("partials/admin-setting-toggle.ejs", {setting: {name: 'CD_RSS', name_pretty: 'RSS Feed', enabled: settings.CD_RSS}}) %>
|
||||
<!-- ActivityPub -->
|
||||
<%- include("partials/admin-setting-toggle.ejs", {setting: {name: 'CD_AP', name_pretty: 'Activity Pub Feed', enabled: settings.CD_AP}}) %>
|
||||
<!-- TODO: Add these option automatically to footer on active -->
|
||||
</div>
|
||||
|
||||
<div id="groups" class="category-page hidden">TODO</div>
|
||||
<div id="yab-master" class="category-page hidden">
|
||||
<!-- Website title -->
|
||||
<%- include("partials/admin-setting-text.ejs", {setting: {name: 'WEBSITE_NAME', name_pretty: 'Website Title', value: settings.WEBSITE_NAME}}) %>
|
||||
<!-- Placeholder thumbnail -->
|
||||
<!-- Plausible analytics -->
|
||||
<%- include("partials/admin-setting-text.ejs", {setting: {name: 'PLAUSIBLE_URL', name_pretty: 'Plausible URL', value: settings.PLAUSIBLE_URL}}) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<div class="setting-row">
|
||||
<div class="setting-title"><%=setting.name_pretty%></div>
|
||||
<div class="setting-toggleable">
|
||||
<div class="spinner hidden">
|
||||
<div>↺</div>
|
||||
</div>
|
||||
<input
|
||||
type="number"
|
||||
id="<%=setting.name%>"
|
||||
onkeydown="updateValue(event.keyCode, '<%=setting.name%>', this.value, this.id)"
|
||||
placeholder="<%=setting.name_pretty%>"
|
||||
value="<%=setting.value%>"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
<div class="setting-row">
|
||||
<div class="setting-title"><%=setting.name_pretty%></div>
|
||||
<div class="setting-toggleable">
|
||||
<div class="spinner hidden">
|
||||
<div>↺</div>
|
||||
</div>
|
||||
<input
|
||||
id="<%=setting.name%>"
|
||||
type="text"
|
||||
onkeydown="updateValue(event.keyCode, '<%=setting.name%>', this.value, this.id)"
|
||||
placeholder="<%=setting.name_pretty%>"
|
||||
value="<%=setting.value%>"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<div class="setting-row">
|
||||
<div class="setting-title"><%=setting.name_pretty%></div>
|
||||
<div class="setting-toggleable">
|
||||
<div class="spinner hidden">
|
||||
<div>↺</div>
|
||||
</div>
|
||||
<% if (!setting.enabled) { %>
|
||||
<button id="<%=setting.name%>_toggle" onclick="toggleState('<%=setting.name%>', true, this.id)" class="bad"><div>Disabled</div></button>
|
||||
<% } else { %>
|
||||
<button id="<%=setting.name%>_toggle" onclick="toggleState('<%=setting.name%>', false, this.id)" class="good"><div>Enabled</div></button>
|
||||
<%}%>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue