Files
kandev/apps/web/components/task-create-dialog-extra-repos-state.ts
T
Kandev Agent 278638ccf1 feat(web/task-create): repeatable repository rows for multi-repo task creation
Users can now actually create a multi-repo task: a "+ Add repository"
button below the primary repo + branch selectors expands the form with
additional (repository, base-branch) rows. Each extra row picks from a
filtered repository list that excludes the primary and any other already-
chosen extras, so duplicates are impossible to enter.

- ExtraRepositoryRow type + form-state hook (extraRepositories, add /
  remove / update / reset). Lives in its own file so the main state hook
  stays under the file-length lint cap.
- ExtraRepositoryRows component renders the rows and the "+ Add" button;
  per-row branches are loaded on demand via the existing
  useRepositoryBranches hook.
- buildRepositoriesPayload accepts the extras and appends them after the
  primary, dropping rows with empty repository_id silently so an
  unfinished row does not block submit. The backend's duplicate-repo
  validation (Phase 8) catches anything that slips through.
- DialogFormBody is split: CreateModeSelectors (selectors + extra rows)
  and useDialogHandlers move to dedicated files, keeping touched files
  under the lint thresholds.
- Tests cover the multi-repo payload (ordering, drop-empty, no-primary
  recovery, single-repo unchanged) and the existing
  store-multi-repo.test.ts gets named constants to satisfy the
  no-duplicate-string rule.

Verified: pnpm exec tsc --noEmit clean, vitest 608/608 green,
eslint --max-warnings 0 clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 01:20:04 +01:00

50 lines
1.5 KiB
TypeScript

"use client";
import { useCallback, useRef, useState } from "react";
import type { ExtraRepositoryRow } from "@/components/task-create-dialog-types";
/**
* Manages the extra-repository rows for multi-repo task creation. The primary
* repo lives on the form's repositoryId/branch; extras here represent the
* 2nd, 3rd, ... rows that the user adds via "+ Add repository".
*
* `nextKey` increments to give each row a stable client-side key without
* relying on array indices (which would shift on removal and break inputs).
*/
export function useExtraRepositoriesState() {
const [extraRepositories, setExtraRepositories] = useState<ExtraRepositoryRow[]>([]);
const nextKeyRef = useRef(0);
const addExtraRepository = useCallback(() => {
nextKeyRef.current += 1;
const key = `extra-${nextKeyRef.current}`;
setExtraRepositories((rows) => [...rows, { key, repositoryId: "", branch: "" }]);
}, []);
const removeExtraRepository = useCallback((key: string) => {
setExtraRepositories((rows) => rows.filter((r) => r.key !== key));
}, []);
const updateExtraRepository = useCallback(
(key: string, patch: Partial<ExtraRepositoryRow>) => {
setExtraRepositories((rows) =>
rows.map((r) => (r.key === key ? { ...r, ...patch } : r)),
);
},
[],
);
const resetExtraRepositories = useCallback(() => {
setExtraRepositories([]);
nextKeyRef.current = 0;
}, []);
return {
extraRepositories,
addExtraRepository,
removeExtraRepository,
updateExtraRepository,
resetExtraRepositories,
};
}