refactor(ui): consolidate container and database management under projects
This commit is contained in:
1 parent
742cae643e
commit
dbefb42725
26 files changed
+4
-3018
No files matched your search
@@ -0,0 +1,87 @@
|
||||
import { AlertTriangle, Clock, Square, XCircle } from "lucide-react";
|
||||
|
||||
interface StatusBadgeProps {
|
||||
status: string;
|
||||
}
|
||||
|
||||
export default function StatusBadge({ status }: StatusBadgeProps) {
|
||||
const cfg: Record<
|
||||
string,
|
||||
{ bg: string; color: string; border: string; label: string }
|
||||
> = {
|
||||
RUNNING: {
|
||||
bg: "rgba(16,185,129,0.1)",
|
||||
color: "#10b981",
|
||||
border: "rgba(16,185,129,0.3)",
|
||||
label: "Running",
|
||||
},
|
||||
STOPPED: {
|
||||
bg: "rgba(100,116,139,0.1)",
|
||||
color: "#64748b",
|
||||
border: "rgba(100,116,139,0.3)",
|
||||
label: "Stopped",
|
||||
},
|
||||
STARTING: {
|
||||
bg: "rgba(245,158,11,0.1)",
|
||||
color: "#f59e0b",
|
||||
border: "rgba(245,158,11,0.3)",
|
||||
label: "Starting",
|
||||
},
|
||||
STOPPING: {
|
||||
bg: "rgba(249,115,22,0.1)",
|
||||
color: "#f97316",
|
||||
border: "rgba(249,115,22,0.3)",
|
||||
label: "Stopping",
|
||||
},
|
||||
PAUSED: {
|
||||
bg: "rgba(168,85,247,0.1)",
|
||||
color: "#a855f7",
|
||||
border: "rgba(168,85,247,0.3)",
|
||||
label: "Paused",
|
||||
},
|
||||
ERROR: {
|
||||
bg: "rgba(239,68,68,0.1)",
|
||||
color: "#ef4444",
|
||||
border: "rgba(239,68,68,0.3)",
|
||||
label: "Error",
|
||||
},
|
||||
};
|
||||
|
||||
const current = cfg[status] || cfg.STOPPED;
|
||||
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
background: current.bg,
|
||||
color: current.color,
|
||||
border: `1px solid ${current.border}`,
|
||||
padding: "3px 10px",
|
||||
borderRadius: 6,
|
||||
fontSize: 11,
|
||||
fontWeight: 600,
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 5,
|
||||
}}
|
||||
>
|
||||
{status === "RUNNING" && (
|
||||
<span
|
||||
style={{
|
||||
width: 5,
|
||||
height: 5,
|
||||
borderRadius: "50%",
|
||||
background: "currentColor",
|
||||
display: "inline-block",
|
||||
}}
|
||||
className="animate-pulse-dot"
|
||||
/>
|
||||
)}
|
||||
{status === "STOPPED" && <XCircle size={10} />}
|
||||
{status === "STARTING" && <Clock size={10} />}
|
||||
{status === "STOPPING" && <Clock size={10} />}
|
||||
{status === "PAUSED" && <Square size={10} />}
|
||||
{status === "ERROR" && <AlertTriangle size={10} />}
|
||||
{current.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,136 @@
|
||||
"use client";
|
||||
|
||||
import type {
|
||||
ProcessLogStep,
|
||||
ProcessLogsModalState,
|
||||
} from "@/components/ProcessLogsModal";
|
||||
|
||||
type ProcessLogOpenHandler = (state: ProcessLogsModalState) => void;
|
||||
type ProcessLogUpdateHandler = (state: Partial<ProcessLogsModalState>) => void;
|
||||
|
||||
const deployTimelineSteps = [
|
||||
{ id: "validate", label: "Validating deployment input", progress: 10 },
|
||||
{ id: "prepare", label: "Preparing deployment payload", progress: 25 },
|
||||
{
|
||||
id: "deploy",
|
||||
label: "Waiting for deployment process",
|
||||
progress: "Running",
|
||||
},
|
||||
{ id: "sync", label: "Syncing container inventory", progress: 85 },
|
||||
{ id: "complete", label: "Deployment completed", progress: 100 },
|
||||
];
|
||||
|
||||
function createDeployTimeline(activeStep: number): ProcessLogStep[] {
|
||||
return deployTimelineSteps.map((step, index) => ({
|
||||
...step,
|
||||
status:
|
||||
index < activeStep
|
||||
? ("success" as const)
|
||||
: index === activeStep
|
||||
? ("running" as const)
|
||||
: ("pending" as const),
|
||||
}));
|
||||
}
|
||||
|
||||
function createFailedDeployTimeline(activeStep: number): ProcessLogStep[] {
|
||||
return createDeployTimeline(activeStep).map((step, index) =>
|
||||
index === activeStep
|
||||
? { ...step, status: "error" as const }
|
||||
: index < activeStep
|
||||
? { ...step, status: "success" as const }
|
||||
: { ...step, status: "pending" as const },
|
||||
);
|
||||
}
|
||||
|
||||
function createCompletedDeployTimeline(): ProcessLogStep[] {
|
||||
return createDeployTimeline(deployTimelineSteps.length - 1).map((step) => ({
|
||||
...step,
|
||||
status: "success" as const,
|
||||
}));
|
||||
}
|
||||
|
||||
export function openDeployTimelineModal({
|
||||
onProcessOpen,
|
||||
name,
|
||||
terminalLogs,
|
||||
}: {
|
||||
onProcessOpen?: ProcessLogOpenHandler;
|
||||
name: string;
|
||||
terminalLogs: string[];
|
||||
}) {
|
||||
onProcessOpen?.({
|
||||
title: `Deployment Logs - ${name || "New Container"}`,
|
||||
description:
|
||||
"Deployment progress, inventory sync, and terminal-style output for this request.",
|
||||
imageUrl: "/assets/images/img-chibi-fixing.png",
|
||||
imageAlt: "Illustration of a character fixing something",
|
||||
timelineLogs: createDeployTimeline(0),
|
||||
terminalLogs,
|
||||
initialTab: "timeline",
|
||||
statusLabel: "Starting",
|
||||
});
|
||||
}
|
||||
|
||||
export function updateDeployTimelineModal({
|
||||
onProcessUpdate,
|
||||
activeStep,
|
||||
terminalLogs,
|
||||
statusLabel,
|
||||
}: {
|
||||
onProcessUpdate?: ProcessLogUpdateHandler;
|
||||
activeStep: number;
|
||||
terminalLogs: string[];
|
||||
statusLabel: string;
|
||||
}) {
|
||||
onProcessUpdate?.({
|
||||
timelineLogs: createDeployTimeline(activeStep),
|
||||
terminalLogs,
|
||||
statusLabel,
|
||||
});
|
||||
}
|
||||
|
||||
export function createDeployTimelineUpdate({
|
||||
activeStep,
|
||||
statusLabel,
|
||||
}: {
|
||||
activeStep: number;
|
||||
statusLabel: string;
|
||||
}): Partial<ProcessLogsModalState> {
|
||||
return {
|
||||
timelineLogs: createDeployTimeline(activeStep),
|
||||
statusLabel,
|
||||
};
|
||||
}
|
||||
|
||||
export function completeDeployTimelineModal({
|
||||
onProcessUpdate,
|
||||
terminalLogs,
|
||||
}: {
|
||||
onProcessUpdate?: ProcessLogUpdateHandler;
|
||||
terminalLogs: string[];
|
||||
}) {
|
||||
onProcessUpdate?.({
|
||||
timelineLogs: createCompletedDeployTimeline(),
|
||||
terminalLogs,
|
||||
statusLabel: "100%",
|
||||
});
|
||||
}
|
||||
|
||||
export function failDeployTimelineModal({
|
||||
onProcessUpdate,
|
||||
name,
|
||||
message,
|
||||
}: {
|
||||
onProcessUpdate?: ProcessLogUpdateHandler;
|
||||
name: string;
|
||||
message: string;
|
||||
}) {
|
||||
onProcessUpdate?.({
|
||||
timelineLogs: createFailedDeployTimeline(2),
|
||||
terminalLogs: [
|
||||
`[deploy] Deployment failed for ${name || "new container"}`,
|
||||
`[error] ${message}`,
|
||||
],
|
||||
statusLabel: "Failed",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
export function formatBytes(bytes?: number | null): string {
|
||||
if (bytes === null || bytes === undefined || Number.isNaN(bytes)) return "—";
|
||||
if (bytes >= 1024 ** 4) return `${(bytes / 1024 ** 4).toFixed(2)} TB`;
|
||||
if (bytes >= 1024 ** 3) return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
||||
if (bytes >= 1024 ** 2) return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
|
||||
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
return `${bytes} B`;
|
||||
}
|
||||
|
||||
export function formatTimestamp(value: string | null): string {
|
||||
if (!value) return "-";
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return value;
|
||||
return new Intl.DateTimeFormat("en-US", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short",
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
export function joinContainerPath(basePath: string, name: string): string {
|
||||
const cleanName = name.replace(/^\/+/, "");
|
||||
if (!cleanName) return basePath || "/";
|
||||
if (!basePath || basePath === "/") return `/${cleanName}`;
|
||||
return `${basePath.replace(/\/+$/, "")}/${cleanName}`;
|
||||
}
|
||||
|
||||
export function decodeBase64ToBlob(base64: string): Blob {
|
||||
const binary = atob(base64);
|
||||
const bytes = new Uint8Array(binary.length);
|
||||
for (let index = 0; index < binary.length; index += 1) {
|
||||
bytes[index] = binary.charCodeAt(index);
|
||||
}
|
||||
return new Blob([bytes]);
|
||||
}
|
||||
|
||||
export function decodeBase64ToText(base64: string): string {
|
||||
return atob(base64);
|
||||
}
|
||||
Reference in new issue
Block a user