Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { searchProjectForUser } from "../../../../../../../server/projects/search";
import { createProjectRepository } from "../../../../../../../server/projects/repository";
import type { ProjectRepository } from "../../../../../../../server/projects/types";
import { WorkspaceError } from "../../../../../../../server/workspaces/core";
import type { AppSession } from "../../../../../../../server/workspaces/types";

interface SearchRouteDependencies {
getSession: () => Promise<AppSession | null>;
projectRepository: Pick<ProjectRepository, "findWorkspaceBySlug" | "getMembership" | "getProjectByKey">;
}

async function getDefaultSession() {
const { getAppSession } = await import("../../../../../../../server/auth");
return getAppSession();
}

const defaultDependencies: SearchRouteDependencies = {
getSession: getDefaultSession,
projectRepository: createProjectRepository()
};

export async function GET(
request: Request,
context: {
params: Promise<{
slug: string;
key: string;
}>;
},
overrideDependencies?: SearchRouteDependencies
) {
const dependencies = overrideDependencies ?? defaultDependencies;
const session = await dependencies.getSession();

if (!session) {
return Response.json({ error: "authentication required." }, { status: 401 });
}

const { slug, key } = await context.params;
const query = new URL(request.url).searchParams.get("q") ?? "";

try {
const response = await searchProjectForUser(
{ projectRepository: dependencies.projectRepository },
session,
slug,
key,
query
);

return Response.json(response);
} catch (error) {
if (error instanceof WorkspaceError) {
return Response.json({ error: error.message }, { status: error.status });
}

throw error;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ export default async function ProjectOverviewPage({
stage={workspaceView.stage}
workspaceSlug={slug}
>
<OverviewView brief={project.description || "Project overview and execution alignment."} overview={workspaceView.overview} />
<OverviewView
brief={project.description || "Project overview and execution alignment."}
overview={workspaceView.overview}
workspaceSlug={workspace.slug}
projectKey={project.key}
/>
</ProjectShell>
</AppShell>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,35 @@ export async function loadProjectPageData(workspaceSlug: string, projectKey: str

try {
const membership = await requireWorkspaceMembership(workspaceRepository, session, workspace.id, "viewer");
const [workspaces, project] = await Promise.all([
const [workspaces, project, notificationRows, notificationPreferences] = await Promise.all([
listWorkspacesForUser(workspaceRepository, session),
getProjectForUser(projectRepository, session, workspaceSlug, projectKey)
getProjectForUser(projectRepository, session, workspaceSlug, projectKey),
listNotificationsForUser(notificationRepository, session, workspaceSlug, { limit: 20 }),
getNotificationPreferencesForUser(notificationRepository, session, workspaceSlug)
]);
const [workspaceView, projectStages, planItems, notificationRows, notificationPreferences] = await Promise.all([
const projectReadinessNotificationRows = await listNotificationsForUser(
notificationRepository,
session,
workspaceSlug,
{
projectId: project.id,
unreadOnly: true,
limit: null
}
);
const [workspaceView, projectStages, planItems] = await Promise.all([
getProjectWorkspaceForUser(
{
projectRepository,
workItemRepository
},
session,
workspaceSlug,
projectKey
projectKey,
{ notificationInbox: projectReadinessNotificationRows }
),
projectRepository.listProjectStages(project.id),
projectRepository.listPlanItems(project.id),
listNotificationsForUser(notificationRepository, session, workspaceSlug, { limit: 20 }),
getNotificationPreferencesForUser(notificationRepository, session, workspaceSlug)
projectRepository.listPlanItems(project.id)
]);
const notificationInbox = {
notifications: notificationRows,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,21 @@ describe("EngineeringView", () => {
);
expect(screen.getByText("OPS-1 · merged · passing · phase 2")).toBeInTheDocument();
});

it("shows setup guidance when repository signals need a GitHub connection", () => {
render(
<EngineeringView
engineering={{
...engineering,
connectionStatus: "Setup required"
}}
/>
);

expect(screen.getByText("Connect GitHub to populate engineering readiness signals.")).toBeInTheDocument();
expect(screen.getByText("Linked pull requests")).toBeInTheDocument();
expect(screen.getByText("Failing checks")).toBeInTheDocument();
expect(screen.getByText("Deployments")).toBeInTheDocument();
expect(screen.getByText("OPS-1 · merged · passing · phase 2")).toBeInTheDocument();
});
});
5 changes: 5 additions & 0 deletions apps/web/src/features/engineering/engineering-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export function EngineeringView({ engineering }: { engineering: ProjectWorkspace
<span className="rounded-full border border-white/10 px-3 py-1">{engineering.connectionStatus}</span>
<span className="rounded-full border border-white/10 px-3 py-1">{engineering.defaultBranch ?? "no branch"}</span>
</div>
{engineering.connectionStatus === "Setup required" ? (
<div className="mt-4 rounded-[1.5rem] border border-dashed border-white/15 bg-black/10 px-4 py-4 text-sm leading-6 text-planka-text-muted">
Connect GitHub to populate engineering readiness signals.
</div>
) : null}
</section>
<section className="rounded-[2rem] border border-white/10 bg-planka-card/90 p-5 shadow-[0_18px_46px_rgba(0,0,0,0.18)]">
<p className="text-xs font-semibold uppercase tracking-[0.12em] text-planka-text-muted">Pull requests</p>
Expand Down
Loading
Loading