projectTree.ts 1.1 KB

123456789101112131415161718192021222324252627282930
  1. import type { ProjectListItem } from '../api/client';
  2. /**
  3. * Projects that may legally become `projectId`'s parent (#1264).
  4. *
  5. * Its own descendants are excluded as well as itself: nesting a project under
  6. * something already beneath it makes a cycle, which the API rejects anyway, so
  7. * offering it would only produce an error the user cannot act on. Walked from
  8. * the flat list rather than fetched, since every row carries its `parent_id`.
  9. */
  10. export function eligibleParents(
  11. projects: ProjectListItem[],
  12. projectId: number | undefined,
  13. ): ProjectListItem[] {
  14. if (projectId === undefined) return projects;
  15. const blocked = new Set([projectId]);
  16. // Repeat until nothing new is blocked: the list is in no particular order, so
  17. // a grandchild can appear before its parent has been blocked.
  18. let grew = true;
  19. while (grew) {
  20. grew = false;
  21. for (const candidate of projects) {
  22. if (candidate.parent_id !== null && blocked.has(candidate.parent_id) && !blocked.has(candidate.id)) {
  23. blocked.add(candidate.id);
  24. grew = true;
  25. }
  26. }
  27. }
  28. return projects.filter((p) => !blocked.has(p.id));
  29. }