feat(admin): redesign storage backend management UI
All checks were successful
Build and Publish Docker Image / deploy (push) Successful in 1m31s
All checks were successful
Build and Publish Docker Image / deploy (push) Successful in 1m31s
Implement a new card-based UI for managing storage backends in the admin panel. This update improves the visual presentation and usability of the storage configuration page. Key changes: - Added comprehensive CSS styles for storage cards, including status indicators, metadata layouts, and action buttons. - Updated the storage admin template to render storage configurations as cards with type-specific details (Local, S3, SFTP, SMB, WebDAV). - Integrated inline actions for testing, editing, disabling, and deleting storage backends. - Enhanced sidebar link alignment with flexbox.
This commit is contained in:
@@ -122,39 +122,128 @@
|
||||
});
|
||||
}
|
||||
|
||||
function syncStorageProvider(select) {
|
||||
const formScope = select.closest("form");
|
||||
if (!formScope) {
|
||||
return;
|
||||
}
|
||||
const provider = select.value;
|
||||
const isContabo = provider === "contabo";
|
||||
formScope.querySelectorAll("[data-provider-fields]").forEach((group) => {
|
||||
const providers = (group.getAttribute("data-provider-fields") || "").split(/\s+/);
|
||||
const active = providers.includes(provider);
|
||||
group.hidden = !active;
|
||||
group.querySelectorAll("input, select, textarea").forEach((input) => {
|
||||
input.disabled = !active;
|
||||
});
|
||||
});
|
||||
const tls = formScope.querySelector('input[name="use_ssl"]');
|
||||
const pathStyle = formScope.querySelector('input[name="path_style"]');
|
||||
if (tls) {
|
||||
tls.checked = isContabo || tls.checked;
|
||||
tls.disabled = isContabo;
|
||||
}
|
||||
if (pathStyle) {
|
||||
pathStyle.checked = isContabo || pathStyle.checked;
|
||||
pathStyle.disabled = isContabo;
|
||||
}
|
||||
}
|
||||
|
||||
if (storageProviderSelects.length > 0) {
|
||||
storageProviderSelects.forEach((select) => {
|
||||
const formScope = select.closest("form");
|
||||
const syncStorageProvider = () => {
|
||||
if (!formScope) {
|
||||
return;
|
||||
}
|
||||
const provider = select.value;
|
||||
const isContabo = provider === "contabo";
|
||||
formScope.querySelectorAll("[data-provider-fields]").forEach((group) => {
|
||||
const providers = (group.getAttribute("data-provider-fields") || "").split(/\s+/);
|
||||
const active = providers.includes(provider);
|
||||
group.hidden = !active;
|
||||
group.querySelectorAll("input, select, textarea").forEach((input) => {
|
||||
input.disabled = !active;
|
||||
});
|
||||
});
|
||||
const tls = formScope.querySelector('input[name="use_ssl"]');
|
||||
const pathStyle = formScope.querySelector('input[name="path_style"]');
|
||||
if (tls) {
|
||||
tls.checked = isContabo || tls.checked;
|
||||
tls.disabled = isContabo;
|
||||
}
|
||||
if (pathStyle) {
|
||||
pathStyle.checked = isContabo || pathStyle.checked;
|
||||
pathStyle.disabled = isContabo;
|
||||
}
|
||||
};
|
||||
select.addEventListener("change", syncStorageProvider);
|
||||
syncStorageProvider();
|
||||
select.addEventListener("change", () => syncStorageProvider(select));
|
||||
syncStorageProvider(select);
|
||||
});
|
||||
}
|
||||
|
||||
/* Storage card edit / cancel toggles */
|
||||
document.querySelectorAll(".storage-edit-trigger").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const card = btn.closest(".storage-card");
|
||||
if (!card) return;
|
||||
card.classList.add("is-editing");
|
||||
const providerSelect = card.querySelector("[data-storage-provider]");
|
||||
if (providerSelect) {
|
||||
syncStorageProvider(providerSelect);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll(".storage-cancel-trigger").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const card = btn.closest(".storage-card");
|
||||
if (!card) return;
|
||||
const form = card.querySelector("form");
|
||||
if (form) form.reset();
|
||||
card.classList.remove("is-editing");
|
||||
});
|
||||
});
|
||||
|
||||
/* Add storage: type picker */
|
||||
const storageAddTrigger = document.querySelector(".storage-add-trigger");
|
||||
const storageTypePicker = document.querySelector(".storage-type-picker");
|
||||
const storageNewCard = document.querySelector(".storage-new-card");
|
||||
|
||||
const providerLabels = {
|
||||
s3: "S3 bucket",
|
||||
contabo: "Contabo Object Storage",
|
||||
sftp: "SFTP",
|
||||
smb: "Samba",
|
||||
webdav: "WebDAV",
|
||||
};
|
||||
|
||||
const providerIconSVGs = {
|
||||
s3: storageNewCard && storageNewCard.querySelector(".storage-new-icon") ? storageNewCard.querySelector(".storage-new-icon").innerHTML : "",
|
||||
contabo: "",
|
||||
sftp: "",
|
||||
smb: "",
|
||||
webdav: "",
|
||||
};
|
||||
|
||||
if (storageAddTrigger && storageTypePicker) {
|
||||
storageAddTrigger.addEventListener("click", () => {
|
||||
storageTypePicker.hidden = !storageTypePicker.hidden;
|
||||
if (storageNewCard && !storageTypePicker.hidden) {
|
||||
storageNewCard.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
storageTypePicker.querySelectorAll(".storage-type-option").forEach((opt) => {
|
||||
opt.addEventListener("click", () => {
|
||||
const provider = opt.dataset.provider;
|
||||
if (!storageNewCard) return;
|
||||
|
||||
const providerSelect = storageNewCard.querySelector("[data-storage-provider]");
|
||||
if (providerSelect) {
|
||||
providerSelect.value = provider;
|
||||
syncStorageProvider(providerSelect);
|
||||
}
|
||||
|
||||
const typeBadge = storageNewCard.querySelector(".storage-new-type-badge");
|
||||
if (typeBadge) typeBadge.textContent = providerLabels[provider] || provider;
|
||||
|
||||
const iconEl = storageNewCard.querySelector(".storage-new-icon");
|
||||
const optIcon = opt.querySelector("svg");
|
||||
if (iconEl && optIcon) {
|
||||
iconEl.innerHTML = optIcon.outerHTML;
|
||||
}
|
||||
|
||||
storageTypePicker.hidden = true;
|
||||
storageNewCard.hidden = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (storageNewCard) {
|
||||
const cancelBtn = storageNewCard.querySelector(".storage-new-cancel");
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener("click", () => {
|
||||
storageNewCard.hidden = true;
|
||||
if (storageTypePicker) storageTypePicker.hidden = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!form || !dropZone || !fileInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user