feat: add last deployment date to services and update sorting logic

- Introduced `lastDeployDate` property to track the most recent deployment for applications and compose services.
- Updated the `extractServicesFromEnvironment` function to calculate and include the last deployment date.
- Modified sorting logic to allow sorting by last deployment date, enhancing the user experience on the environment dashboard.
- Adjusted local storage default sort preference to prioritize last deployment date.
This commit is contained in:
Mauricio Siu
2025-11-13 22:35:16 -06:00
parent 62474c1222
commit d549aa6a62
2 changed files with 108 additions and 25 deletions
@@ -115,6 +115,7 @@ export type Services = {
id: string; id: string;
createdAt: string; createdAt: string;
status?: "idle" | "running" | "done" | "error"; status?: "idle" | "running" | "done" | "error";
lastDeployDate?: Date | null;
}; };
type Project = Awaited<ReturnType<typeof findProjectById>>; type Project = Awaited<ReturnType<typeof findProjectById>>;
@@ -128,16 +129,34 @@ export const extractServicesFromEnvironment = (
const allServices: Services[] = []; const allServices: Services[] = [];
const applications: Services[] = const applications: Services[] =
environment.applications?.map((item) => ({ environment.applications?.map((item) => {
appName: item.appName, // Get the most recent deployment date
name: item.name, let lastDeployDate: Date | null = null;
type: "application", const deployments = (item as any).deployments;
id: item.applicationId, if (deployments && deployments.length > 0) {
createdAt: item.createdAt, for (const deployment of deployments) {
status: item.applicationStatus, const deployDate = new Date(
description: item.description, deployment.finishedAt ||
serverId: item.serverId, deployment.startedAt ||
})) || []; deployment.createdAt,
);
if (!lastDeployDate || deployDate > lastDeployDate) {
lastDeployDate = deployDate;
}
}
}
return {
appName: item.appName,
name: item.name,
type: "application",
id: item.applicationId,
createdAt: item.createdAt,
status: item.applicationStatus,
description: item.description,
serverId: item.serverId,
lastDeployDate,
};
}) || [];
const mariadb: Services[] = const mariadb: Services[] =
environment.mariadb?.map((item) => ({ environment.mariadb?.map((item) => ({
@@ -200,16 +219,34 @@ export const extractServicesFromEnvironment = (
})) || []; })) || [];
const compose: Services[] = const compose: Services[] =
environment.compose?.map((item) => ({ environment.compose?.map((item) => {
appName: item.appName, // Get the most recent deployment date
name: item.name, let lastDeployDate: Date | null = null;
type: "compose", const deployments = (item as any).deployments;
id: item.composeId, if (deployments && deployments.length > 0) {
createdAt: item.createdAt, for (const deployment of deployments) {
status: item.composeStatus, const deployDate = new Date(
description: item.description, deployment.finishedAt ||
serverId: item.serverId, deployment.startedAt ||
})) || []; deployment.createdAt,
);
if (!lastDeployDate || deployDate > lastDeployDate) {
lastDeployDate = deployDate;
}
}
}
return {
appName: item.appName,
name: item.name,
type: "compose",
id: item.composeId,
createdAt: item.createdAt,
status: item.composeStatus,
description: item.description,
serverId: item.serverId,
lastDeployDate,
};
}) || [];
allServices.push( allServices.push(
...applications, ...applications,
@@ -237,9 +274,9 @@ const EnvironmentPage = (
const { data: auth } = api.user.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const [sortBy, setSortBy] = useState<string>(() => { const [sortBy, setSortBy] = useState<string>(() => {
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
return localStorage.getItem("servicesSort") || "createdAt-desc"; return localStorage.getItem("servicesSort") || "lastDeploy-desc";
} }
return "createdAt-desc"; return "lastDeploy-desc";
}); });
useEffect(() => { useEffect(() => {
@@ -261,10 +298,45 @@ const EnvironmentPage = (
comparison = comparison =
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
break; break;
case "lastDeploy": {
const aLastDeploy = a.lastDeployDate;
const bLastDeploy = b.lastDeployDate;
if (direction === "desc") {
// For "desc" (newest first): services with deployments first, then those without
if (!aLastDeploy && !bLastDeploy) {
comparison = 0;
} else if (!aLastDeploy) {
comparison = 1; // a (no deploy) goes after b (has deploy)
} else if (!bLastDeploy) {
comparison = -1; // a (has deploy) goes before b (no deploy)
} else {
// Both have deployments: newest first (negative if a is newer)
comparison = bLastDeploy.getTime() - aLastDeploy.getTime();
}
} else {
// For "asc" (oldest first): services with deployments first, then those without
if (!aLastDeploy && !bLastDeploy) {
comparison = 0;
} else if (!aLastDeploy) {
comparison = 1; // a (no deploy) goes after b (has deploy)
} else if (!bLastDeploy) {
comparison = -1; // a (has deploy) goes before b (no deploy)
} else {
// Both have deployments: oldest first
comparison = aLastDeploy.getTime() - bLastDeploy.getTime();
}
}
break;
}
default: default:
comparison = 0; comparison = 0;
} }
return direction === "asc" ? comparison : -comparison; // For other fields, apply direction normally
if (field !== "lastDeploy") {
return direction === "asc" ? comparison : -comparison;
}
return comparison;
}); });
}; };
@@ -1217,6 +1289,9 @@ const EnvironmentPage = (
<SelectValue placeholder="Sort by..." /> <SelectValue placeholder="Sort by..." />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="lastDeploy-desc">
Recently deployed
</SelectItem>
<SelectItem value="createdAt-desc"> <SelectItem value="createdAt-desc">
Newest first Newest first
</SelectItem> </SelectItem>
+10 -2
View File
@@ -34,13 +34,21 @@ export const findEnvironmentById = async (environmentId: string) => {
const environment = await db.query.environments.findFirst({ const environment = await db.query.environments.findFirst({
where: eq(environments.environmentId, environmentId), where: eq(environments.environmentId, environmentId),
with: { with: {
applications: true, applications: {
with: {
deployments: true,
},
},
mariadb: true, mariadb: true,
mongo: true, mongo: true,
mysql: true, mysql: true,
postgres: true, postgres: true,
redis: true, redis: true,
compose: true, compose: {
with: {
deployments: true,
},
},
project: true, project: true,
}, },
}); });