Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #20: Intuitive Repository Grouping within the Task Panel #58

Merged
merged 5 commits into from
Dec 18, 2024

Conversation

birdup000
Copy link
Owner

Resolves #20

The following modifications were applied:

```xml
<modification>
  <file>app/types/workspace.ts</file>
  <operation>replace</operation>
  <target>export interface Workspace {
  id: string;
  name: string;
  description?: string;
  repositories: Repository[];
  settings: WorkspaceSettings;
  createdAt: Date;
  updatedAt: Date;
  isDefault?: boolean;
}</target>
  <content>export interface Workspace {
  id: string;
  name: string;
  description?: string;
  repositories: Repository[];
  groups: Group[];
  settings: WorkspaceSettings;
  createdAt: Date;
  updatedAt: Date;
  isDefault?: boolean;
}

export interface Group {
  id: string;
  name: string;
  repositoryIds: string[]; // Array to store IDs of repositories in the group
}
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/types/workspace.ts</file>
  <operation>replace</operation>
  <target>export interface Repository {
  id: string;
  name: string;
  url: string;
  description?: string;
  defaultBranch: string;
  settings?: RepositorySettings;
  addedAt: Date;
}</target>
  <content>export interface Repository {
  id: string;
  name: string;
  url: string;
  description?: string;
  defaultBranch: string;
  settings?: RepositorySettings;
  addedAt: Date;
  groupId?: string; // Optional group ID for the repository
}
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/hooks/useWorkspaces.ts</file>
  <operation>replace</operation>
  <target>const [workspaces, setWorkspaces] = useState&lt;Workspace[]&gt;([]);</target>
  <content>  const [workspaces, setWorkspaces] = useState&lt;Workspace[]&gt;([]);
  const [groups, setGroups] = useState&lt;Group[]&gt;([]);</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/hooks/useWorkspaces.ts</file>
  <operation>insert</operation>
  <target>useEffect(() => {
    // Save workspaces to localStorage when they change
    localStorage.setItem(STORAGE_KEY, JSON.stringify(workspaces));
  }, [workspaces]);</target>
  <content>  const getWorkspace = (workspaceId: string): Workspace | undefined => {
    return workspaces.find((w) => w.id === workspaceId);
  };

  const getGroup = (workspaceId: string, groupId: string): Group | undefined => {
    const workspace = getWorkspace(workspaceId);
    if (!workspace) return undefined;
    return workspace.groups.find((g) => g.id === groupId);
  };

  const createGroup = (workspaceId: string, groupName: string): Group => {
    const newGroup: Group = {
      id: crypto.randomUUID(),
      name: groupName,
      repositoryIds: [],
    };

    setWorkspaces(
      workspaces.map((workspace) =>
        workspace.id === workspaceId
          ? { ...workspace, groups: [...workspace.groups, newGroup] }
          : workspace
      )
    );

    return newGroup;
  };

  const updateGroup = (
    workspaceId: string,
    groupId: string,
    updates: Partial&lt;Group&gt;
  ) => {
    setWorkspaces(
      workspaces.map((workspace) =>
        workspace.id === workspaceId
          ? {
              ...workspace,
              groups: workspace.groups.map((group) =>
                group.id === groupId ? { ...group, ...updates } : group
              ),
            }
          : workspace
      )
    );
  };

  const deleteGroup = (workspaceId: string, groupId: string) => {
    setWorkspaces(
      workspaces.map((workspace) =>
        workspace.id === workspaceId
          ? {
              ...workspace,
              groups: workspace.groups.filter((group) => group.id !== groupId),
            }
          : workspace
      )
    );
  };
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/hooks/useWorkspaces.ts</file>
  <operation>replace</operation>
  <target>const createWorkspace = (workspace: Omit&lt;Workspace, 'id' | 'createdAt' | 'updatedAt'&gt;) =>{
    const newWorkspace: Workspace = {
      ...workspace,
      id: crypto.randomUUID(),
      createdAt: new Date(),
      updatedAt: new Date(),
      settings: {
        ...workspace.settings,
        lastAccessed: new Date(),
        visibility: workspace.settings?.visibility || 'private'
      },
    };

    if (workspaces.length === 0) {
      newWorkspace.isDefault = true;
    }

    setWorkspaces([...workspaces, newWorkspace]);
    setCurrentWorkspace(newWorkspace.id);
    return newWorkspace;
  };</target>
  <content>  const createWorkspace = (
    workspace: Omit&lt;Workspace, "id" | "createdAt" | "updatedAt" | "groups"&gt;
  ) => {
    const newWorkspace: Workspace = {
      ...workspace,
      id: crypto.randomUUID(),
      createdAt: new Date(),
      updatedAt: new Date(),
      groups: [],
      settings: {
        ...workspace.settings,
        lastAccessed: new Date(),
        visibility: workspace.settings?.visibility || "private",
      },
    };

    const defaultGroup: Group = {
      id: crypto.randomUUID(),
      name: "Default Group",
      repositoryIds: [],
    };

    newWorkspace.groups.push(defaultGroup);

    if (workspaces.length === 0) {
      newWorkspace.isDefault = true;
    }

    setWorkspaces([...workspaces, newWorkspace]);
    setCurrentWorkspace(newWorkspace.id);
    return newWorkspace;
  };
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/hooks/useWorkspaces.ts</file>
  <operation>replace</operation>
  <target>const addRepository = (workspaceId: string, repository: Omit&lt;Repository, 'id' | 'addedAt'&gt;) => {
    const newRepository: Repository = {
      ...repository,
      id: crypto.randomUUID(),
      addedAt: new Date(),
    };

    updateWorkspace(workspaceId, {
      repositories: [
        ...(workspaces.find(w => w.id === workspaceId)?.repositories || []),
        newRepository
      ]
    });

    return newRepository;
  };</target>
  <content>  const addRepository = (
    workspaceId: string,
    repository: Omit&lt;Repository, "id" | "addedAt"&gt;,
    groupId?: string
  ) => {
    const workspace = getWorkspace(workspaceId);
    if (!workspace) {
      throw new Error(`Workspace not found: ${workspaceId}`);
    }

    const newRepository: Repository = {
      ...repository,
      id: crypto.randomUUID(),
      addedAt: new Date(),
      groupId: groupId || workspace.groups[0]?.id, // Add to the specified group or the first group by default
    };

    updateWorkspace(workspaceId, {
      repositories: [...workspace.repositories, newRepository],
    });

    // Add the repository ID to the group's repositoryIds array
    if (groupId) {
      updateGroup(workspaceId, groupId, {
        repositoryIds: [
          ...(workspace.groups.find((g) => g.id === groupId)?.repositoryIds ||
            []),
          newRepository.id,
        ],
      });
    }

    return newRepository;
  };</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/hooks/useWorkspaces.ts</file>
  <operation>replace</operation>
  <target>const removeRepository = (workspaceId: string, repositoryId: string) => {
    const workspace = workspaces.find(w => w.id === workspaceId);
    if (workspace) {
      updateWorkspace(workspaceId, {
        repositories: workspace.repositories.filter(r => r.id !== repositoryId)
      });
    }
  };</target>
  <content>  const removeRepository = (workspaceId: string, repositoryId: string) => {
    const workspace = getWorkspace(workspaceId);
    if (!workspace) return;

    const repository = workspace.repositories.find((r) => r.id === repositoryId);
    if (!repository) return;

    // Remove the repository from its group
    if (repository.groupId) {
      const group = getGroup(workspaceId, repository.groupId);
      if (group) {
        updateGroup(workspaceId, repository.groupId, {
          repositoryIds: group.repositoryIds.filter((id) => id !== repositoryId),
        });
      }
    }

    // Remove the repository from the workspace
    updateWorkspace(workspaceId, {
      repositories: workspace.repositories.filter((r) => r.id !== repositoryId),
    });
  };
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/hooks/useWorkspaces.ts</file>
  <operation>replace</operation>
  <target>return {
    workspaces,
    currentWorkspace,
    setCurrentWorkspace,
    createWorkspace,
    updateWorkspace,
    deleteWorkspace,
    addRepository,
    removeRepository,
    updateRepositorySettings,
    updateWorkspaceSettings,
  };
};</target>
  <content>  return {
    workspaces,
    currentWorkspace,
    setCurrentWorkspace,
    createWorkspace,
    updateWorkspace,
    deleteWorkspace,
    addRepository,
    removeRepository,
    updateRepositorySettings,
    updateWorkspaceSettings,
    createGroup,
    updateGroup,
    deleteGroup,
    getGroup,
  };
};
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/components/WorkspacePanel.tsx</file>
  <operation>replace</operation>
  <target>const {
    workspaces,
    currentWorkspace,
    setCurrentWorkspace,
    createWorkspace,
    updateWorkspace,
    deleteWorkspace,
    addRepository,
    removeRepository,
    updateRepositorySettings,
  } = useWorkspaces();</target>
  <content>  const {
    workspaces,
    currentWorkspace,
    setCurrentWorkspace,
    createWorkspace,
    updateWorkspace,
    deleteWorkspace,
    addRepository,
    removeRepository,
    updateRepositorySettings,
    createGroup,
    updateGroup,
    deleteGroup,
    getGroup,
  } = useWorkspaces();

  const [showNewGroupForm, setShowNewGroupForm] = useState(false);
  const [newGroupName, setNewGroupName] = useState("");
  const [selectedGroup, setSelectedGroup] = useState&lt;string | null&gt;(null);
  const [isRenamingGroup, setIsRenamingGroup] = useState(false);
  const [renamingGroupId, setRenamingGroupId] = useState&lt;string | null&gt;(null);
  const [editedGroupName, setEditedGroupName] = useState("");
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/components/WorkspacePanel.tsx</file>
  <operation>replace</operation>
  <target>const currentWorkspaceData = workspaces.find(w => w.id === currentWorkspace);</target>
  <content>  const currentWorkspaceData = workspaces.find((w) => w.id === currentWorkspace);

  const handleCreateGroup = () => {
    if (newGroupName &amp;&amp; currentWorkspace) {
      createGroup(currentWorkspace, newGroupName);
      setNewGroupName("");
      setShowNewGroupForm(false);
    }
  };

  const handleRenameGroup = (group: Group) => {
    setRenamingGroupId(group.id);
    setEditedGroupName(group.name);
    setIsRenamingGroup(true);
  };

  const handleSaveGroupName = (groupId: string) => {
    if (currentWorkspace &amp;&amp; editedGroupName) {
      updateGroup(currentWorkspace, groupId, { name: editedGroupName });
    }
    setIsRenamingGroup(false);
    setRenamingGroupId(null);
    setEditedGroupName("");
  };

  const handleDeleteGroup = (groupId: string) => {
    if (
      currentWorkspace &amp;&amp;
      confirm("Are you sure you want to delete this group?")
    ) {
      deleteGroup(currentWorkspace, groupId);
      setSelectedGroup(null);
    }
  };

  const handleSelectGroup = (groupId: string) => {
    setSelectedGroup(groupId);
  };

  const filteredRepositories = React.useMemo(() => {
    if (!currentWorkspaceData) return [];
    if (!selectedGroup) return currentWorkspaceData.repositories;

    const group = getGroup(currentWorkspaceData.id, selectedGroup);
    if (!group) return [];

    return currentWorkspaceData.repositories.filter((repo) =>
      group.repositoryIds.includes(repo.id)
    );
  }, [currentWorkspaceData, selectedGroup]);
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>
  <file>app/components/WorkspacePanel.tsx</file>
  <operation>replace</operation>
  <target>          &lt;div className="space-y-4"&gt;
            {workspaces.map(workspace =&gt; (
              &lt;div
                key={workspace.id}
                className={`p-4 rounded-lg ${
                  workspace.id === currentWorkspace
                    ? 'bg-indigo-600/20 border border-indigo-500/30'
                    : 'bg-[#333333] hover:bg-[#3A3A3A]'
                } transition-colors cursor-pointer`}
                onClick={() =&gt; setCurrentWorkspace(workspace.id)}
              &gt;
                &lt;div className="flex justify-between items-start mb-2"&gt;
                  &lt;div&gt;
                    &lt;div className="flex items-center gap-2"&gt;
                      &lt;h3 className="font-medium"&gt;{workspace.name}&lt;/h3&gt;
                      &lt;span className={`text-xs px-2 py-0.5 rounded-full ${
                        workspace.settings?.visibility === 'public' 
                          ? 'bg-green-600/20 text-green-300'
                          : workspace.settings?.visibility === 'team'
                          ? 'bg-blue-600/20 text-blue-300'
                          : 'bg-gray-600/20 text-gray-300'
                      }`}&gt;
                        {workspace.settings?.visibility || 'private'}
                      &lt;/span&gt;
                    &lt;/div&gt;
                    {workspace.description &amp;&amp; (
                      &lt;p className="text-sm text-gray-400"&gt;{workspace.description}&lt;/p&gt;
                    )}
                    {workspace.settings?.collaborators?.length &gt; 0 &amp;&amp; (
                      &lt;div className="text-xs text-gray-400 mt-1"&gt;
                        {workspace.settings.collaborators.length} collaborator(s)
                      &lt;/div&gt;
                    )}
                  &lt;/div&gt;
                  &lt;div className="flex items-center gap-2"&gt;
                    &lt;button
                      onClick={(e) =&gt; {
                        e.stopPropagation();
                        updateWorkspace(workspace.id, {
                          name: prompt('New workspace name:', workspace.name) || workspace.name
                        });
                      }}
                      className="p-1 rounded hover:bg-[#444444] transition-colors"
                    &gt;
                      &lt;PencilIcon className="h-4 w-4" /&gt;
                    &lt;/button&gt;
                    &lt;button
                      onClick={(e) =&gt; {
                        e.stopPropagation();
                        if (confirm('Are you sure you want to delete this workspace?')) {
                          deleteWorkspace(workspace.id);
                        }
                      }}
                      className="p-1 rounded hover:bg-[#444444] transition-colors"
                    &gt;
                      &lt;TrashIcon className="h-4 w-4" /&gt;
                    &lt;/button&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
                &lt;div className="flex items-center gap-2 text-sm text-gray-400"&gt;
                    &lt;span&gt;{workspace.repositories.length} repositories&lt;/span&gt;
                    &lt;button
                      onClick={(e) =&gt; {
                        e.stopPropagation();
                        setCurrentWorkspace(workspace.id);
                        setShowCollaborators(true);
                      }}
                      className="px-2 py-1 rounded bg-[#444444] hover:bg-[#555555] transition-colors"
                    &gt;
                      Manage Collaborators
                    &lt;/button&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            ))}
          &lt;/div&gt;</target>
  <content>          &lt;div className="space-y-4"&gt;
            {workspaces.map((workspace) =&gt; (
              &lt;div key={workspace.id}&gt;
                &lt;div
                  className={`p-4 rounded-lg ${
                    workspace.id === currentWorkspace
                      ? "bg-indigo-600/20 border border-indigo-500/30"
                      : "bg-[#333333] hover:bg-[#3A3A3A]"
                  } transition-colors cursor-pointer`}
                  onClick={() =&gt; {
                    setCurrentWorkspace(workspace.id);
                    setSelectedGroup(null); // Reset selected group when switching workspaces
                  }}
                &gt;
                  &lt;div className="flex justify-between items-start mb-2"&gt;
                    &lt;div&gt;
                      &lt;div className="flex items-center gap-2"&gt;
                        &lt;h3 className="font-medium"&gt;{workspace.name}&lt;/h3&gt;
                        &lt;span
                          className={`text-xs px-2 py-0.5 rounded-full ${
                            workspace.settings?.visibility === "public"
                              ? "bg-green-600/20 text-green-300"
                              : workspace.settings?.visibility === "team"
                              ? "bg-blue-600/20 text-blue-300"
                              : "bg-gray-600/20 text-gray-300"
                          }`}
                        &gt;
                          {workspace.settings?.visibility || "private"}
                        &lt;/span&gt;
                      &lt;/div&gt;
                      {workspace.description &amp;&amp; (
                        &lt;p className="text-sm text-gray-400"&gt;
                          {workspace.description}
                        &lt;/p&gt;
                      )}
                      {workspace.settings?.collaborators?.length &gt; 0 &amp;&amp; (
                        &lt;div className="text-xs text-gray-400 mt-1"&gt;
                          {workspace.settings.collaborators.length} collaborator(s)
                        &lt;/div&gt;
                      )}
                    &lt;/div&gt;
                    &lt;div className="flex items-center gap-2"&gt;
                      &lt;button
                        onClick={(e) =&gt; {
                          e.stopPropagation();
                          updateWorkspace(workspace.id, {
                            name:
                              prompt("New workspace name:", workspace.name) ||
                              workspace.name,
                          });
                        }}
                        className="p-1 rounded hover:bg-[#444444] transition-colors"
                      &gt;
                        &lt;PencilIcon className="h-4 w-4" /&gt;
                      &lt;/button&gt;
                      &lt;button
                        onClick={(e) =&gt; {
                          e.stopPropagation();
                          if (
                            confirm("Are you sure you want to delete this workspace?")
                          ) {
                            deleteWorkspace(workspace.id);
                          }
                        }}
                        className="p-1 rounded hover:bg-[#444444] transition-colors"
                      &gt;
                        &lt;TrashIcon className="h-4 w-4" /&gt;
                      &lt;/button&gt;
                    &lt;/div&gt;
                  &lt;/div&gt;
                  &lt;div className="flex items-center gap-2 text-sm text-gray-400"&gt;
                    &lt;span&gt;{workspace.repositories.length} repositories&lt;/span&gt;
                    &lt;button
                      onClick={(e) =&gt; {
                        e.stopPropagation();
                        setCurrentWorkspace(workspace.id);
                        setShowCollaborators(true);
                      }}
                      className="px-2 py-1 rounded bg-[#444444] hover:bg-[#555555] transition-colors"
                    &gt;
                      Manage Collaborators
                    &lt;/button&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
                {workspace.id === currentWorkspace &amp;&amp; (
                  &lt;div className="mt-4"&gt;
                    &lt;div className="flex justify-between items-center mb-2"&gt;
                      &lt;h4 className="text-lg font-medium"&gt;Groups&lt;/h4&gt;
                      &lt;button
                        onClick={() =&gt; setShowNewGroupForm(true)}
                        className="px-3 py-1.5 rounded text-sm bg-indigo-600 hover:bg-indigo-700 transition-colors flex items-center gap-2"
                      &gt;
                        &lt;PlusIcon className="h-4 w-4" /&gt;
                        New Group
                      &lt;/button&gt;
                    &lt;/div&gt;
                    &lt;div className="space-y-2"&gt;
                      {workspace.groups.map((group) =&gt; (
                        &lt;div
                          key={group.id}
                          className={`p-2 rounded-lg ${
                            selectedGroup === group.id
                              ? "bg-indigo-600/20 border border-indigo-500/30"
                              : "bg-[#444444] hover:bg-[#4A4A4A]"
                          } transition-colors cursor-pointer`}
                          onClick={() =&gt; handleSelectGroup(group.id)}
                        &gt;
                          &lt;div className="flex justify-between items-center"&gt;
                            &lt;span className="font-medium"&gt;
                              {isRenamingGroup &amp;&amp; renamingGroupId === group.id ? (
                                &lt;input
                                  type="text"
                                  value={editedGroupName}
                                  onChange={(e) =&gt;
                                    setEditedGroupName(e.target.value)
                                  }
                                  onBlur={() =&gt; handleSaveGroupName(group.id)}
                                  onKeyDown={(e) =&gt; {
                                    if (e.key === "Enter") {
                                      handleSaveGroupName(group.id);
                                    }
                                  }}
                                  className="px-2 py-1 rounded bg-[#333333] text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
                                  autoFocus
                                /&gt;
                              ) : (
                                group.name
                              )}
                            &lt;/span&gt;
                            &lt;div className="flex items-center gap-2"&gt;
                              &lt;button
                                onClick={(e) =&gt; {
                                  e.stopPropagation();
                                  handleRenameGroup(group);
                                }}
                                className="p-1 rounded hover:bg-[#555555] transition-colors"
                              &gt;
                                &lt;PencilIcon className="h-4 w-4" /&gt;
                              &lt;/button&gt;
                              &lt;button
                                onClick={(e) =&gt; {
                                  e.stopPropagation();
                                  handleDeleteGroup(group.id);
                                }}
                                className="p-1 rounded hover:bg-[#555555] transition-colors"
                              &gt;
                                &lt;TrashIcon className="h-4 w-4" /&gt;
                              &lt;/button&gt;
                            &lt;/div&gt;
                          &lt;/div&gt;
                        &lt;/div&gt;
                      ))}
                    &lt;/div&gt;
                  &lt;/div&gt;
                )}
                {/* New Group Form */}
                {showNewGroupForm &amp;&amp; (
                  &lt;div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"&gt;
                    &lt;div className="bg-[#212121] p-6 rounded-lg w-full max-w-md mx-4"&gt;
                      &lt;h2 className="text-xl font-semibold mb-4"&gt;
                        Create New Group
                      &lt;/h2&gt;
                      &lt;input
                        type="text"
                        value={newGroupName}
                        onChange={(e) =&gt; setNewGroupName(e.target.value)}
                        placeholder="Group name"
                        className="w-full p-2 rounded bg-[#333333] text-white mb-4"
                      /&gt;
                      &lt;div className="flex justify-end gap-4"&gt;
                        &lt;button
                          onClick={() =&gt; setShowNewGroupForm(false)}
                          className="px-4 py-2 rounded bg-[#444444] hover:bg-[#555555] transition-colors"
                        &gt;
                          Cancel
                        &lt;/button&gt;
                        &lt;button
                          onClick={handleCreateGroup}
                          className="px-4 py-2 rounded bg-indigo-600 hover:bg-indigo-700 transition-colors"
                        &gt;
                          Create
                        &lt;/button&gt;
                      &lt;/div&gt;
                    &lt;/div&gt;
                  &lt;/div&gt;
                )}
              &lt;/div&gt;
            ))}
          &lt;/div&gt;
</content>
  <fuzzy_match>false</fuzzy_match>
</modification>
<modification>

@birdup000
Copy link
Owner Author

This is a good start towards implementing repository grouping. The core functionality seems to be in place, and the code is generally well-structured and follows project patterns.

Here's a breakdown of the feedback:

1. Critical Issues or Bugs:

  • The use of confirm and prompt is not ideal for a modern web application. They are blocking calls and can disrupt the user experience.
  • The addRepository function could handle missing or invalid groupId values more gracefully.
  • Error handling could be more robust throughout the application.

2. Resolution of the Issue:

The changes do implement the basic functionality of repository grouping as described in issue #20. Users can create, rename, delete, and select groups. Repositories can be added to groups, and the UI filters repositories based on the selected group. However, the user experience could be improved, and error handling needs to be more robust.

3. Feedback without Modifications:

The code is generally well-written and follows React best practices. The use of hooks is appropriate, and the component structure is logical. The implementation of filtering repositories based on the selected group using React.useMemo is efficient. However, the reliance on browser-native confirm and prompt functions detracts from the user experience. The error handling is minimal, and there's no feedback to the user when an operation fails. Additionally, the code could benefit from more comments explaining the logic behind certain decisions.

4. No XML Modification Blocks

The user specifically instructed that no XML modification blocks are to be provided in this instance.

Overall:

The changes are a good foundation for repository grouping. However, addressing the identified issues and considering the suggested improvements would significantly enhance the user experience and the robustness of the application.

@birdup000 birdup000 merged commit 325b792 into main Dec 18, 2024
0 of 2 checks passed
@birdup000 birdup000 deleted the issue-20 branch December 19, 2024 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Intuitive Repository Grouping within the Task Panel
1 participant