+
+ setSearch(e.target.value)}
+ />
+
+
+ {loading ? (
+
+ {Array.from({ length: 3 }).map((_, i) => (
+
+ ))}
+
+ ) : sortedProjects.length === 0 ? (
+
+ {search ? "No projects match your search." : "No projects connected yet. Run `stack dev` to connect a project."}
+
+ ) : (
+
+ {sortedProjects.map((project) => {
+ const configPath = projectConfigPaths.get(project.id);
+ const onboardingStatus = projectStatuses.get(project.id);
+ const projectHref = onboardingStatus === "completed"
+ ? urlString`/projects/${project.id}`
+ : urlString`/new-project?project_id=${project.id}`;
+
+ return (
+
+
+
+
+
+
+ {configPath ?? project.id}
+
+ {onboardingStatus != null && onboardingStatus !== "completed" && (
+
+ Setup incomplete
+
+ )}
+
+
+ );
+ })}
+
+ )}
+
+ );
+}
+
function ProjectsListPage() {
const app = useStackApp();
const appInternals = useMemo(() => getStackAppInternals(app), [app]);
diff --git a/apps/dashboard/src/app/api/development-environment/projects/route.ts b/apps/dashboard/src/app/api/development-environment/projects/route.ts
new file mode 100644
index 000000000..086f3c097
--- /dev/null
+++ b/apps/dashboard/src/app/api/development-environment/projects/route.ts
@@ -0,0 +1,23 @@
+import { getPublicEnvVar } from "@/lib/env";
+import { assertRemoteDevelopmentEnvironmentBrowserRequest } from "@/lib/remote-development-environment/security";
+import { NextRequest, NextResponse } from "next/server";
+
+export const runtime = "nodejs";
+
+export async function GET(req: NextRequest) {
+ const isRemoteDevelopmentEnvironment = getPublicEnvVar("NEXT_PUBLIC_STACK_IS_REMOTE_DEVELOPMENT_ENVIRONMENT") === "true";
+ if (!isRemoteDevelopmentEnvironment) {
+ return NextResponse.json({ error: "This endpoint is only available in remote development environments." }, { status: 404 });
+ }
+
+ const securityResponse = assertRemoteDevelopmentEnvironmentBrowserRequest(req);
+ if (securityResponse != null) return securityResponse;
+
+ const { getRemoteDevelopmentEnvironmentProjectConfigPaths } = await import("@/lib/remote-development-environment/manager");
+ const configPaths = getRemoteDevelopmentEnvironmentProjectConfigPaths();
+ const projectConfigPaths: Record