Ui improvements and bug fixes

This commit is contained in:
2026-03-08 14:51:39 -04:00
parent adf3183cb8
commit b46c70a221
7 changed files with 294 additions and 21 deletions

View File

@@ -194,7 +194,10 @@ const DESCRIPTIONS = {
groups_description_max_length: "Max characters for group descriptions (30500). Longer text is truncated.",
groups_card_bg_dark: "Space card background for dark mode.",
groups_card_bg_light: "Space card background for light mode.",
splits_background_image_url: "Background image for the splits section (Groups + FAQ row).",
splits_background_image_url: "Background image for the splits section (Groups + FAQ container).",
splits_bg_dark: "Background color for the splits section (dark mode).",
splits_bg_light: "Background color for the splits section (light mode).",
splits_min_height: "Minimum height in pixels for the splits section. 0 = auto.",
// ── FAQ ──
faq_enabled: "Show FAQ accordion alongside the Spaces section. One item opens at a time.",
@@ -204,6 +207,7 @@ const DESCRIPTIONS = {
faq_items: 'FAQ items as JSON array: [{\"q\":\"Question\",\"a\":\"Answer\"}]. HTML supported in answers.',
faq_card_bg_dark: "FAQ card background for dark mode.",
faq_card_bg_light: "FAQ card background for light mode.",
faq_mobile_max_height: "Max height of FAQ container on mobile (px). Scrollable if content overflows. 0 = no limit.",
// ── App CTA ──
show_app_ctas: "Show App Download CTA: gradient banner with headline, badges, and promo image.",
@@ -343,23 +347,19 @@ const TABS = [
])
},
{
id: "groups",
label: "Groups",
id: "splits",
label: "Splits",
settings: new Set([
"splits_background_image_url",
"splits_bg_dark", "splits_bg_light", "splits_min_height",
"groups_enabled", "groups_title_enabled", "groups_title", "groups_title_size",
"groups_count", "groups_selected",
"groups_show_description", "groups_description_max_length",
"groups_card_bg_dark", "groups_card_bg_light",
"splits_background_image_url"
])
},
{
id: "faq",
label: "FAQ",
settings: new Set([
"faq_enabled", "faq_title_enabled", "faq_title", "faq_title_size",
"faq_items",
"faq_card_bg_dark", "faq_card_bg_light"
"faq_card_bg_dark", "faq_card_bg_light",
"faq_mobile_max_height"
])
},
{
@@ -403,8 +403,6 @@ const TAB_ENABLE_SETTINGS = {
stats: { setting: "stats_enabled", label: "Stats" },
about: { setting: "about_enabled", label: "About" },
topics: { setting: "topics_enabled", label: "Topics" },
groups: { setting: "groups_enabled", label: "Groups" },
faq: { setting: "faq_enabled", label: "FAQ" },
appcta: { setting: "show_app_ctas", label: "App CTA" },
};
@@ -903,22 +901,139 @@ function updatePreviewThumbnail(wrapper, settingName) {
row.querySelector(".setting-value textarea");
const url = input ? input.value.trim() : "";
const preview = wrapper.querySelector(".cl-upload-preview");
if (!preview) return;
const cfg = IMAGE_UPLOAD_SETTINGS[settingName];
if (cfg && cfg.multi) {
// For multi-image, show the last image in the list
const urls = url.split("|").filter(Boolean);
const lastUrl = urls.length > 0 ? urls[urls.length - 1] : "";
preview.src = lastUrl;
preview.style.display = lastUrl ? "" : "none";
// Multi-image: render a sortable image list
renderMultiImageList(wrapper, row, input, settingName);
// Hide single preview if it exists
const preview = wrapper.querySelector(".cl-upload-preview");
if (preview) preview.style.display = "none";
} else {
const preview = wrapper.querySelector(".cl-upload-preview");
if (!preview) return;
preview.src = url;
preview.style.display = url ? "" : "none";
}
}
function renderMultiImageList(wrapper, row, input, settingName) {
let list = wrapper.querySelector(".cl-multi-image-list");
if (!list) {
list = document.createElement("div");
list.className = "cl-multi-image-list";
wrapper.appendChild(list);
}
const raw = input ? input.value.trim() : "";
const urls = raw.split(/[\n\r]+/).map((u) => u.trim()).filter(Boolean);
list.innerHTML = "";
if (urls.length === 0) return;
urls.forEach((url, idx) => {
const item = document.createElement("div");
item.className = "cl-multi-image-item";
item.draggable = true;
item.dataset.idx = idx;
const thumb = document.createElement("img");
thumb.src = url;
thumb.alt = `Image ${idx + 1}`;
thumb.className = "cl-multi-image-thumb";
item.appendChild(thumb);
const label = document.createElement("span");
label.className = "cl-multi-image-label";
label.textContent = `${idx + 1}`;
item.appendChild(label);
const actions = document.createElement("span");
actions.className = "cl-multi-image-actions";
// Move up
if (idx > 0) {
const upBtn = document.createElement("button");
upBtn.type = "button";
upBtn.className = "cl-multi-image-move";
upBtn.innerHTML = "▲";
upBtn.title = "Move up";
upBtn.addEventListener("click", () => {
[urls[idx - 1], urls[idx]] = [urls[idx], urls[idx - 1]];
input.value = urls.join("\n");
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
renderMultiImageList(wrapper, row, input, settingName);
});
actions.appendChild(upBtn);
}
// Move down
if (idx < urls.length - 1) {
const downBtn = document.createElement("button");
downBtn.type = "button";
downBtn.className = "cl-multi-image-move";
downBtn.innerHTML = "&#9660;";
downBtn.title = "Move down";
downBtn.addEventListener("click", () => {
[urls[idx], urls[idx + 1]] = [urls[idx + 1], urls[idx]];
input.value = urls.join("\n");
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
renderMultiImageList(wrapper, row, input, settingName);
});
actions.appendChild(downBtn);
}
// Remove
const removeBtn = document.createElement("button");
removeBtn.type = "button";
removeBtn.className = "cl-multi-image-remove";
removeBtn.innerHTML = "&times;";
removeBtn.title = "Remove image";
removeBtn.addEventListener("click", () => {
urls.splice(idx, 1);
input.value = urls.join("\n");
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
renderMultiImageList(wrapper, row, input, settingName);
});
actions.appendChild(removeBtn);
item.appendChild(actions);
// Drag-and-drop reorder
item.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("text/plain", idx.toString());
item.classList.add("cl-dragging");
});
item.addEventListener("dragend", () => {
item.classList.remove("cl-dragging");
});
item.addEventListener("dragover", (e) => {
e.preventDefault();
item.classList.add("cl-drag-over");
});
item.addEventListener("dragleave", () => {
item.classList.remove("cl-drag-over");
});
item.addEventListener("drop", (e) => {
e.preventDefault();
item.classList.remove("cl-drag-over");
const fromIdx = parseInt(e.dataTransfer.getData("text/plain"), 10);
const toIdx = idx;
if (fromIdx === toIdx) return;
const [moved] = urls.splice(fromIdx, 1);
urls.splice(toIdx, 0, moved);
input.value = urls.join("\n");
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
renderMultiImageList(wrapper, row, input, settingName);
});
list.appendChild(item);
});
}
function injectUploadButtons() {
const container = getContainer();
if (!container) return;

View File

@@ -448,3 +448,93 @@ html.dark-scheme .admin-detail:not(.cl-tabs-active) .row.setting[data-setting="f
color: var(--primary-medium);
font-style: italic;
}
/* ── Multi-image list (hero_image_urls) ── */
.cl-multi-image-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
width: 100%;
margin-top: 8px;
}
.cl-multi-image-item {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
background: var(--primary-very-low);
border: 1px solid var(--primary-low);
border-radius: 6px;
cursor: grab;
transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.cl-multi-image-item:hover {
border-color: var(--primary-low-mid);
}
.cl-multi-image-item.cl-dragging {
opacity: 0.4;
}
.cl-multi-image-item.cl-drag-over {
border-color: var(--tertiary);
box-shadow: 0 0 0 2px var(--tertiary-low);
}
.cl-multi-image-thumb {
width: 48px;
height: 36px;
object-fit: cover;
border-radius: 4px;
border: 1px solid var(--primary-low);
background: var(--secondary);
}
.cl-multi-image-label {
font-size: var(--font-down-2);
font-weight: 600;
color: var(--primary-medium);
min-width: 14px;
text-align: center;
}
.cl-multi-image-actions {
display: flex;
gap: 2px;
}
.cl-multi-image-move {
padding: 2px 5px;
font-size: 10px;
line-height: 1;
background: none;
border: 1px solid var(--primary-low);
border-radius: 3px;
cursor: pointer;
color: var(--primary-medium);
transition: background 0.1s ease;
}
.cl-multi-image-move:hover {
background: var(--primary-very-low);
color: var(--primary);
}
.cl-multi-image-remove {
padding: 2px 6px;
font-size: 14px;
line-height: 1;
background: none;
border: 1px solid var(--danger-low);
border-radius: 3px;
cursor: pointer;
color: var(--danger);
transition: background 0.1s ease;
}
.cl-multi-image-remove:hover {
background: var(--danger-low);
}

View File

@@ -2096,4 +2096,43 @@ html {
.cl-faq__answer {
animation: none;
}
}
/* ═══════════════════════════════════════════════════════════════════
MOBILE — tighter padding, no horizontal overflow
═══════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
.cl-container {
padding: 0 1rem;
}
.cl-hero__inner {
padding: 0 1rem;
}
.cl-navbar__inner {
padding: 0 1rem;
}
.cl-about__left {
padding: 1.25rem 1rem;
}
.cl-about__right {
padding: 1.25rem 1.25rem;
}
.cl-app-cta__inner {
padding: 1.5rem 1.25rem;
align-items: center;
text-align: center;
}
.cl-app-cta__image {
display: none;
}
.cl-app-cta__badges {
justify-content: center;
}
}