refactor(ui): consolidate container and database management under projects

This commit is contained in:
KodekaTeam committed 2026-07-14 23:07:41 +07:00
1 parent 742cae643e
commit dbefb42725
26 files changed
+4 -3018

No files matched your search

+87
View File
@@ -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);
}