refactor: enhance environment selector with service presence checks and alert notifications; update navigation links to include environment context for improved user experience

This commit is contained in:
Mauricio Siu
2025-09-01 21:18:56 -06:00
parent 59cbc8ee0d
commit 1a9f131d39
12 changed files with 28 additions and 17 deletions

View File

@@ -24,6 +24,7 @@ import { Textarea } from "@/components/ui/textarea";
import { toast } from "sonner";
import { ChevronDownIcon, PlusIcon, PencilIcon, TrashIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { AlertBlock } from "@/components/shared/alert-block";
interface Environment {
environmentId: string;
@@ -54,6 +55,11 @@ export const AdvancedEnvironmentSelector = ({
const [description, setDescription] = useState("");
// API mutations
const { data: environment } = api.environment.one.useQuery({ environmentId: currentEnvironmentId || "" },{
enabled: !!currentEnvironmentId,
});
const haveServices = environment && (environment?.mariadb?.length > 0 || environment?.mongo?.length > 0 || environment?.mysql?.length > 0 || environment?.postgres?.length > 0 || environment?.redis?.length > 0 || environment?.applications?.length > 0 || environment?.compose?.length > 0);
const createEnvironment = api.environment.create.useMutation();
const updateEnvironment = api.environment.update.useMutation();
const deleteEnvironment = api.environment.remove.useMutation();
@@ -356,6 +362,12 @@ export const AdvancedEnvironmentSelector = ({
This action cannot be undone and will also delete all services in this environment.
</DialogDescription>
</DialogHeader>
{haveServices && (
<AlertBlock type="warning">
This environment have active services, please delete them first.
</AlertBlock>
)}
<DialogFooter>
<Button
@@ -370,7 +382,7 @@ export const AdvancedEnvironmentSelector = ({
<Button
variant="destructive"
onClick={handleDeleteEnvironment}
disabled={deleteEnvironment.isLoading}
disabled={deleteEnvironment.isLoading || haveServices}
>
{deleteEnvironment.isLoading ? "Deleting..." : "Delete"}
</Button>

View File

@@ -216,7 +216,7 @@ export const ShowProjects = () => {
className="w-full lg:max-w-md"
>
<Link
href={`/dashboard/project/${project.projectId}`}
href={`/dashboard/project/${project.projectId}/environment/${project?.environments?.[0]?.environmentId}`}
>
<Card className="group relative w-full h-full bg-transparent transition-colors hover:bg-border">
{haveServicesWithDomains ? (

View File

@@ -13,7 +13,7 @@ import { SidebarTrigger } from "@/components/ui/sidebar";
interface Props {
list: {
name: string;
href: string;
href?: string;
}[];
}
@@ -29,11 +29,11 @@ export const BreadcrumbSidebar = ({ list }: Props) => {
{list.map((item, index) => (
<Fragment key={item.name}>
<BreadcrumbItem className="block">
<BreadcrumbLink href={item.href} asChild={!!item.href}>
<BreadcrumbLink href={item?.href} asChild={!!item?.href}>
{item.href ? (
<Link href={item.href}>{item.name}</Link>
<Link href={item?.href}>{item?.name}</Link>
) : (
item.name
item?.name
)}
</BreadcrumbLink>
</BreadcrumbItem>

View File

@@ -757,7 +757,6 @@ const EnvironmentPage = (
},
{
name: currentEnvironment.name,
href: `/dashboard/project/${projectId}/environment/${environmentId}`,
},
]}
/>

View File

@@ -106,7 +106,6 @@ const Service = (
},
{
name: data?.name || "",
href: `/dashboard/project/${projectId}/environment/${environmentId}/services/application/${applicationId}`,
},
]}
/>

View File

@@ -97,7 +97,6 @@ const Service = (
},
{
name: data?.name || "",
href: `/dashboard/project/${projectId}/environment/${environmentId}/services/compose/${composeId}`,
},
]}
/>

View File

@@ -78,7 +78,6 @@ const Mariadb = (
},
{
name: data?.name || "",
href: `/dashboard/project/${projectId}/environment/${environmentId}/services/mariadb/${mariadbId}`,
},
]}
/>

View File

@@ -78,7 +78,6 @@ const Mongo = (
},
{
name: data?.name || "",
href: `/dashboard/project/${projectId}/environment/${environmentId}/services/mongo/${mongoId}`,
},
]}
/>

View File

@@ -77,7 +77,6 @@ const MySql = (
},
{
name: data?.name || "",
href: `/dashboard/project/${projectId}/environment/${environmentId}/services/mysql/${mysqlId}`,
},
]}
/>

View File

@@ -78,8 +78,6 @@ const Postgresql = (
},
{
name: data?.name || "",
href:
`/dashboard/project/${projectId}/environment/${environmentId}/services/postgres/${postgresId}`,
},
]}
/>

View File

@@ -78,7 +78,6 @@ const Redis = (
},
{
name: data?.name || "",
href: `/dashboard/project/${projectId}/environment/${environmentId}/services/redis/${redisId}`,
},
]}
/>

View File

@@ -62,13 +62,21 @@ export const findEnvironmentsByProjectId = async (projectId: string) => {
};
export const deleteEnvironment = async (environmentId: string) => {
const environment = await db
const currentEnvironment = await findEnvironmentById(environmentId);
if (currentEnvironment.name === "production") {
throw new TRPCError({
code: "BAD_REQUEST",
message: "You cannot delete the production environment",
});
}
const deletedEnvironment = await db
.delete(environments)
.where(eq(environments.environmentId, environmentId))
.returning()
.then((value) => value[0]);
return environment;
return deletedEnvironment;
};
export const updateEnvironmentById = async (