Files
buzz/desktop/src/features/projects/ui/CreateProjectIssueDialog.tsx
thomaspblock 87f8ff82ae feat(desktop): refine repository-aware project workspaces (#6003)
## Summary
- Makes Projects repository-aware across browsing, branch and tag
selection, local and remote source management, work items, commits, and
contextual repository actions.
- Refines project details into consistent single-column reading surfaces
with a resizable, section-aware context pod, while keeping project
conversations available in the attached chat panel.
- Adds persistent sidebar project navigation, direct entity links,
repository discussion channels, contributor identity matching, and
consistent loading and activity presentation.
- Keeps branch-specific controls on code-oriented sections while Tasks,
Reviews, and Channels remain repository-scoped, reducing misleading
context and actions.

Replacement for #5981 with an identical final tree flattened into one
signed-off commit because the required DCO check suite remained stalled.

### Related issue
N/A

### Testing
- [x] Desktop pre-push checks, TypeScript typecheck, and unit tests
- [x] E2E production build with `pnpm build:e2e`
- [x] Focused Playwright smoke coverage for project workspace, task,
review, commit, sidebar, and contextual-panel behavior
- [ ] Attach final before/after screenshots after design review

---------

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Signed-off-by: Wintermute <165f0c871dd2586bb18b6aa109eeaf57bb2132ff4d27b10120f4368a0f627022@buzz.block.builderlab.xyz>
Co-authored-by: Wintermute <165f0c871dd2586bb18b6aa109eeaf57bb2132ff4d27b10120f4368a0f627022@buzz.block.builderlab.xyz>
2026-08-19 12:50:00 +01:00

105 lines
3.4 KiB
TypeScript

import * as React from "react";
import { toast } from "sonner";
import type { Project, Repository } from "@/features/projects/hooks";
import { useCreateProjectIssueMutation } from "@/features/projects/issueMutations";
import { selectProjectRepository } from "@/features/projects/projectModels";
import {
CreateProjectWorkItemDialog,
type CreateProjectWorkItemDialogInput,
} from "./CreateProjectWorkItemDialog";
export function CreateProjectIssueDialog({
initialProjectId,
onCreated,
onOpenChange,
open,
projects,
}: {
initialProjectId?: string;
onCreated: (
project: Project,
repository: Repository,
issueId: string,
) => void | Promise<void>;
onOpenChange: (open: boolean) => void;
open: boolean;
projects: Project[];
}) {
const repositoryOptions = React.useMemo(
() =>
projects.flatMap((project) =>
project.repositories.map((repository) => ({ project, repository })),
),
[projects],
);
const initialProject =
projects.find((project) => project.id === initialProjectId) ?? projects[0];
const [repositoryId, setRepositoryId] = React.useState(
selectProjectRepository(initialProject, null)?.id ?? "",
);
const selection =
repositoryOptions.find(
(candidate) => candidate.repository.id === repositoryId,
) ?? repositoryOptions[0];
const project = selection?.project;
const repository = selection?.repository;
const createMutation = useCreateProjectIssueMutation(repository);
React.useEffect(() => {
if (!open) return;
const nextProject =
projects.find((candidate) => candidate.id === initialProjectId) ??
projects[0];
setRepositoryId(selectProjectRepository(nextProject, null)?.id ?? "");
}, [initialProjectId, open, projects]);
async function handleCreate(input: CreateProjectWorkItemDialogInput) {
if (!project || !repository) throw new Error("Choose a repository.");
const issueId = await createMutation.mutateAsync(input);
toast.success("Task created.");
await onCreated(project, repository, issueId);
}
return (
<CreateProjectWorkItemDialog
bodyPlaceholder="Add context, expected behavior, or reproduction steps"
description={
repository
? `Create a task in ${repository.name}`
: "Choose a repository for this task."
}
isCreating={createMutation.isPending}
itemName="issue"
onCreate={handleCreate}
onOpenChange={onOpenChange}
open={open}
submitDisabled={!repository}
title="Create a task"
titlePlaceholder="Describe the task"
>
<label className="block space-y-1.5 text-sm font-medium">
<span>Repository</span>
<select
className="h-10 w-full rounded-lg border border-input bg-background px-3 text-sm font-normal outline-hidden focus:ring-1 focus:ring-ring"
data-testid="create-issue-repository"
disabled={createMutation.isPending}
onChange={(event) => setRepositoryId(event.target.value)}
value={repository?.id ?? ""}
>
{repositoryOptions.map((candidate) => (
<option
key={candidate.repository.id}
value={candidate.repository.id}
>
{candidate.project.repositories.length > 1
? `${candidate.project.name} / ${candidate.repository.name}`
: candidate.project.name}
</option>
))}
</select>
</label>
</CreateProjectWorkItemDialog>
);
}