diff --git a/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/active-deployment-card-empty.tsx b/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/active-deployment-card-empty.tsx
new file mode 100644
index 0000000000..290a8d990f
--- /dev/null
+++ b/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/active-deployment-card-empty.tsx
@@ -0,0 +1,48 @@
+import { Cloud, Plus } from "@unkey/icons";
+import { Button, Card } from "@unkey/ui";
+import { cn } from "@unkey/ui/src/lib/utils";
+
+type Props = {
+ onCreateDeployment?: () => void;
+ className?: string;
+};
+
+export const ActiveDeploymentCardEmpty = ({ onCreateDeployment, className }: Props) => (
+
+
+ {/* Icon with subtle animation */}
+
+
+ {/* Content */}
+
+
No active deployments
+
+ Your deployments will appear here once you push code to your connected repository or
+ trigger a manual deployment.
+
+
+
+ {/* Action button */}
+ {onCreateDeployment && (
+
+ )}
+
+
+);
diff --git a/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/hooks/use-deployment-logs.tsx b/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/hooks/use-deployment-logs.tsx
index 6fb74ff464..7640dcba46 100644
--- a/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/hooks/use-deployment-logs.tsx
+++ b/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/hooks/use-deployment-logs.tsx
@@ -23,7 +23,7 @@ type LogEntry = {
type LogFilter = "all" | "warnings" | "errors";
type UseDeploymentLogsProps = {
- deploymentId: string;
+ deploymentId: string | null;
showBuildSteps: boolean;
};
@@ -61,9 +61,12 @@ export function useDeploymentLogs({
const { queryTime: timestamp } = useQueryTime();
const { data: buildData, isLoading: buildLoading } = trpc.deploy.deployment.buildSteps.useQuery(
- { deploymentId },
{
- enabled: showBuildSteps && isExpanded,
+ // without this check TS yells at us
+ deploymentId: deploymentId ?? "",
+ },
+ {
+ enabled: showBuildSteps && isExpanded && Boolean(deploymentId),
refetchInterval: BUILD_STEPS_REFETCH_INTERVAL,
},
);
diff --git a/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/index.tsx b/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/index.tsx
index 8868af4fb8..f82e7f4dec 100644
--- a/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/index.tsx
+++ b/apps/dashboard/app/(app)/projects/[projectId]/details/active-deployment-card/index.tsx
@@ -18,6 +18,7 @@ import { cn } from "@unkey/ui/src/lib/utils";
import { format } from "date-fns";
import { useProjectLayout } from "../../layout-provider";
import { Card } from "../card";
+import { ActiveDeploymentCardEmpty } from "./active-deployment-card-empty";
import { FilterButton } from "./filter-button";
import { Avatar } from "./git-avatar";
import { useDeploymentLogs } from "./hooks/use-deployment-logs";
@@ -68,15 +69,17 @@ export const statusIndicator = (
};
type Props = {
- deploymentId: string;
+ deploymentId: string | null;
};
export const ActiveDeploymentCard = ({ deploymentId }: Props) => {
const { collections } = useProjectLayout();
- const { data } = useLiveQuery((q) =>
- q
- .from({ deployment: collections.deployments })
- .where(({ deployment }) => eq(deployment.id, deploymentId)),
+ const { data, isLoading } = useLiveQuery(
+ (q) =>
+ q
+ .from({ deployment: collections.deployments })
+ .where(({ deployment }) => eq(deployment.id, deploymentId)),
+ [deploymentId],
);
const deployment = data.at(0);
@@ -101,9 +104,12 @@ export const ActiveDeploymentCard = ({ deploymentId }: Props) => {
showBuildSteps,
});
- if (!deployment) {
+ if (isLoading) {
return ;
}
+ if (!deployment) {
+ return ;
+ }
const statusConfig = statusIndicator(deployment.status);
diff --git a/apps/dashboard/app/(app)/projects/[projectId]/details/domain-row.tsx b/apps/dashboard/app/(app)/projects/[projectId]/details/domain-row.tsx
index ebe3d05f08..b51404bb6d 100644
--- a/apps/dashboard/app/(app)/projects/[projectId]/details/domain-row.tsx
+++ b/apps/dashboard/app/(app)/projects/[projectId]/details/domain-row.tsx
@@ -1,6 +1,7 @@
import { CircleCheck, Link4, ShareUpRight } from "@unkey/icons";
import { Badge } from "@unkey/ui";
import Link from "next/link";
+import { Card } from "./card";
type DomainRowProps = {
domain: string;
@@ -28,3 +29,47 @@ export function DomainRow({ domain }: DomainRowProps) {
);
}
+
+export const DomainRowSkeleton = () => {
+ return (
+
+ );
+};
+
+export const DomainRowEmpty = () => (
+
+
+ {/* Icon with subtle animation */}
+
+ {/* Content */}
+
+
No domains found
+
+ Your configured domains will appear here once they're set up and verified.
+
+
+
+
+);
diff --git a/apps/dashboard/app/(app)/projects/[projectId]/layout.tsx b/apps/dashboard/app/(app)/projects/[projectId]/layout.tsx
index f36b35cc83..8fc0d55165 100644
--- a/apps/dashboard/app/(app)/projects/[projectId]/layout.tsx
+++ b/apps/dashboard/app/(app)/projects/[projectId]/layout.tsx
@@ -1,5 +1,6 @@
"use client";
import { collectionManager } from "@/lib/collections";
+import { eq, useLiveQuery } from "@tanstack/react-db";
import { DoubleChevronLeft } from "@unkey/icons";
import { Button, InfoTooltip } from "@unkey/ui";
import { useState } from "react";
@@ -25,10 +26,23 @@ type ProjectLayoutProps = {
const ProjectLayout = ({ projectId, children }: ProjectLayoutProps) => {
const [tableDistanceToTop, setTableDistanceToTop] = useState(0);
- const [isDetailsOpen, setIsDetailsOpen] = useState(true);
+ const [isDetailsOpen, setIsDetailsOpen] = useState(false);
const collections = collectionManager.getProjectCollections(projectId);
+ const projects = useLiveQuery((q) =>
+ q.from({ project: collections.projects }).where(({ project }) => eq(project.id, projectId)),
+ );
+
+ const liveDeploymentId = projects.data.at(0)?.liveDeploymentId;
+
+ const getTooltipContent = () => {
+ if (!liveDeploymentId) {
+ return "No deployments available. Deploy your project to view details.";
+ }
+ return isDetailsOpen ? "Hide deployment details" : "Show deployment details";
+ };
+
return (
{
detailsExpandableTrigger={
{