All checks were successful
Check / check (pull_request) Successful in 11m29s
Split static/js/app.js (581 lines) into 5 focused modules: - utils.js: global utilities store + legacy compat - components.js: reusable Alpine.js components (copy, confirm, dismiss, time) - app-detail.js: app detail page logic (status polling, logs) - deployment.js: deployment card + deployments history page - dashboard.js: dashboard relative time updates Closes #128
144 lines
4.3 KiB
JavaScript
144 lines
4.3 KiB
JavaScript
/**
|
|
* upaas - Global Utilities Store
|
|
*
|
|
* Shared formatting, status helpers, and clipboard utilities used across all pages.
|
|
*/
|
|
|
|
document.addEventListener("alpine:init", () => {
|
|
Alpine.store("utils", {
|
|
/**
|
|
* Format a date string as relative time (e.g., "5 minutes ago")
|
|
*/
|
|
formatRelativeTime(dateStr) {
|
|
if (!dateStr) return "";
|
|
const date = new Date(dateStr);
|
|
const now = new Date();
|
|
const diffMs = now - date;
|
|
const diffSec = Math.floor(diffMs / 1000);
|
|
const diffMin = Math.floor(diffSec / 60);
|
|
const diffHour = Math.floor(diffMin / 60);
|
|
const diffDay = Math.floor(diffHour / 24);
|
|
|
|
if (diffSec < 60) return "just now";
|
|
if (diffMin < 60)
|
|
return diffMin + (diffMin === 1 ? " minute ago" : " minutes ago");
|
|
if (diffHour < 24)
|
|
return diffHour + (diffHour === 1 ? " hour ago" : " hours ago");
|
|
if (diffDay < 7)
|
|
return diffDay + (diffDay === 1 ? " day ago" : " days ago");
|
|
return date.toLocaleDateString();
|
|
},
|
|
|
|
/**
|
|
* Get the badge class for a given status
|
|
*/
|
|
statusBadgeClass(status) {
|
|
if (status === "running" || status === "success") return "badge-success";
|
|
if (status === "building" || status === "deploying")
|
|
return "badge-warning";
|
|
if (status === "failed" || status === "error") return "badge-error";
|
|
return "badge-neutral";
|
|
},
|
|
|
|
/**
|
|
* Format status for display (capitalize first letter)
|
|
*/
|
|
statusLabel(status) {
|
|
if (!status) return "";
|
|
return status.charAt(0).toUpperCase() + status.slice(1);
|
|
},
|
|
|
|
/**
|
|
* Check if status indicates active deployment
|
|
*/
|
|
isDeploying(status) {
|
|
return status === "building" || status === "deploying";
|
|
},
|
|
|
|
/**
|
|
* Scroll an element to the bottom
|
|
*/
|
|
scrollToBottom(el) {
|
|
if (el) {
|
|
requestAnimationFrame(() => {
|
|
el.scrollTop = el.scrollHeight;
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check if a scrollable element is at (or near) the bottom.
|
|
* Tolerance of 30px accounts for rounding and partial lines.
|
|
*/
|
|
isScrolledToBottom(el, tolerance = 30) {
|
|
if (!el) return true;
|
|
return el.scrollHeight - el.scrollTop - el.clientHeight <= tolerance;
|
|
},
|
|
|
|
/**
|
|
* Copy text to clipboard
|
|
*/
|
|
async copyToClipboard(text, button) {
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
return true;
|
|
} catch (err) {
|
|
// Fallback for older browsers
|
|
const textArea = document.createElement("textarea");
|
|
textArea.value = text;
|
|
textArea.style.position = "fixed";
|
|
textArea.style.left = "-9999px";
|
|
document.body.appendChild(textArea);
|
|
textArea.select();
|
|
try {
|
|
document.execCommand("copy");
|
|
document.body.removeChild(textArea);
|
|
return true;
|
|
} catch (e) {
|
|
document.body.removeChild(textArea);
|
|
return false;
|
|
}
|
|
}
|
|
},
|
|
});
|
|
});
|
|
|
|
// ============================================
|
|
// Legacy support - expose utilities globally
|
|
// ============================================
|
|
window.upaas = {
|
|
// These are kept for backwards compatibility but templates should use Alpine.js
|
|
formatRelativeTime(dateStr) {
|
|
if (!dateStr) return "";
|
|
const date = new Date(dateStr);
|
|
const now = new Date();
|
|
const diffMs = now - date;
|
|
const diffSec = Math.floor(diffMs / 1000);
|
|
const diffMin = Math.floor(diffSec / 60);
|
|
const diffHour = Math.floor(diffMin / 60);
|
|
const diffDay = Math.floor(diffHour / 24);
|
|
|
|
if (diffSec < 60) return "just now";
|
|
if (diffMin < 60)
|
|
return diffMin + (diffMin === 1 ? " minute ago" : " minutes ago");
|
|
if (diffHour < 24)
|
|
return diffHour + (diffHour === 1 ? " hour ago" : " hours ago");
|
|
if (diffDay < 7)
|
|
return diffDay + (diffDay === 1 ? " day ago" : " days ago");
|
|
return date.toLocaleDateString();
|
|
},
|
|
// Placeholder functions - templates should migrate to Alpine.js
|
|
initAppDetailPage() {},
|
|
initDeploymentsPage() {},
|
|
};
|
|
|
|
// Update relative times on page load for non-Alpine elements
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
document.querySelectorAll(".relative-time[data-time]").forEach((el) => {
|
|
const time = el.getAttribute("data-time");
|
|
if (time) {
|
|
el.textContent = window.upaas.formatRelativeTime(time);
|
|
}
|
|
});
|
|
});
|