Merge pull request #552 from mvanhorn/osc/129-feat-filter-issues-by-project
feat(ui): add project filter to issues list
This commit is contained in:
commit
e3e7a92c77
2 changed files with 37 additions and 1 deletions
|
|
@ -40,6 +40,7 @@ export type IssueViewState = {
|
||||||
priorities: string[];
|
priorities: string[];
|
||||||
assignees: string[];
|
assignees: string[];
|
||||||
labels: string[];
|
labels: string[];
|
||||||
|
projects: string[];
|
||||||
sortField: "status" | "priority" | "title" | "created" | "updated";
|
sortField: "status" | "priority" | "title" | "created" | "updated";
|
||||||
sortDir: "asc" | "desc";
|
sortDir: "asc" | "desc";
|
||||||
groupBy: "status" | "priority" | "assignee" | "none";
|
groupBy: "status" | "priority" | "assignee" | "none";
|
||||||
|
|
@ -52,6 +53,7 @@ const defaultViewState: IssueViewState = {
|
||||||
priorities: [],
|
priorities: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
labels: [],
|
labels: [],
|
||||||
|
projects: [],
|
||||||
sortField: "updated",
|
sortField: "updated",
|
||||||
sortDir: "desc",
|
sortDir: "desc",
|
||||||
groupBy: "none",
|
groupBy: "none",
|
||||||
|
|
@ -104,6 +106,7 @@ function applyFilters(issues: Issue[], state: IssueViewState, currentUserId?: st
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (state.labels.length > 0) result = result.filter((i) => (i.labelIds ?? []).some((id) => state.labels.includes(id)));
|
if (state.labels.length > 0) result = result.filter((i) => (i.labelIds ?? []).some((id) => state.labels.includes(id)));
|
||||||
|
if (state.projects.length > 0) result = result.filter((i) => i.projectId != null && state.projects.includes(i.projectId));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,6 +138,7 @@ function countActiveFilters(state: IssueViewState): number {
|
||||||
if (state.priorities.length > 0) count++;
|
if (state.priorities.length > 0) count++;
|
||||||
if (state.assignees.length > 0) count++;
|
if (state.assignees.length > 0) count++;
|
||||||
if (state.labels.length > 0) count++;
|
if (state.labels.length > 0) count++;
|
||||||
|
if (state.projects.length > 0) count++;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,11 +149,17 @@ interface Agent {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ProjectOption {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface IssuesListProps {
|
interface IssuesListProps {
|
||||||
issues: Issue[];
|
issues: Issue[];
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
error?: Error | null;
|
error?: Error | null;
|
||||||
agents?: Agent[];
|
agents?: Agent[];
|
||||||
|
projects?: ProjectOption[];
|
||||||
liveIssueIds?: Set<string>;
|
liveIssueIds?: Set<string>;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
viewStateKey: string;
|
viewStateKey: string;
|
||||||
|
|
@ -165,6 +175,7 @@ export function IssuesList({
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
agents,
|
agents,
|
||||||
|
projects,
|
||||||
liveIssueIds,
|
liveIssueIds,
|
||||||
projectId,
|
projectId,
|
||||||
viewStateKey,
|
viewStateKey,
|
||||||
|
|
@ -362,7 +373,7 @@ export function IssuesList({
|
||||||
className="h-3 w-3 ml-1 hidden sm:block"
|
className="h-3 w-3 ml-1 hidden sm:block"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
updateView({ statuses: [], priorities: [], assignees: [], labels: [] });
|
updateView({ statuses: [], priorities: [], assignees: [], labels: [], projects: [] });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -495,6 +506,23 @@ export function IssuesList({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{projects && projects.length > 0 && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<span className="text-xs text-muted-foreground">Project</span>
|
||||||
|
<div className="space-y-0.5 max-h-32 overflow-y-auto">
|
||||||
|
{projects.map((project) => (
|
||||||
|
<label key={project.id} className="flex items-center gap-2 px-2 py-1 rounded-sm hover:bg-accent/50 cursor-pointer">
|
||||||
|
<Checkbox
|
||||||
|
checked={viewState.projects.includes(project.id)}
|
||||||
|
onCheckedChange={() => updateView({ projects: toggleInArray(viewState.projects, project.id) })}
|
||||||
|
/>
|
||||||
|
<span className="text-sm">{project.name}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useLocation, useSearchParams } from "@/lib/router";
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { issuesApi } from "../api/issues";
|
import { issuesApi } from "../api/issues";
|
||||||
import { agentsApi } from "../api/agents";
|
import { agentsApi } from "../api/agents";
|
||||||
|
import { projectsApi } from "../api/projects";
|
||||||
import { heartbeatsApi } from "../api/heartbeats";
|
import { heartbeatsApi } from "../api/heartbeats";
|
||||||
import { useCompany } from "../context/CompanyContext";
|
import { useCompany } from "../context/CompanyContext";
|
||||||
import { useBreadcrumbs } from "../context/BreadcrumbContext";
|
import { useBreadcrumbs } from "../context/BreadcrumbContext";
|
||||||
|
|
@ -50,6 +51,12 @@ export function Issues() {
|
||||||
enabled: !!selectedCompanyId,
|
enabled: !!selectedCompanyId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { data: projects } = useQuery({
|
||||||
|
queryKey: queryKeys.projects.list(selectedCompanyId!),
|
||||||
|
queryFn: () => projectsApi.list(selectedCompanyId!),
|
||||||
|
enabled: !!selectedCompanyId,
|
||||||
|
});
|
||||||
|
|
||||||
const { data: liveRuns } = useQuery({
|
const { data: liveRuns } = useQuery({
|
||||||
queryKey: queryKeys.liveRuns(selectedCompanyId!),
|
queryKey: queryKeys.liveRuns(selectedCompanyId!),
|
||||||
queryFn: () => heartbeatsApi.liveRunsForCompany(selectedCompanyId!),
|
queryFn: () => heartbeatsApi.liveRunsForCompany(selectedCompanyId!),
|
||||||
|
|
@ -102,6 +109,7 @@ export function Issues() {
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
error={error as Error | null}
|
error={error as Error | null}
|
||||||
agents={agents}
|
agents={agents}
|
||||||
|
projects={projects}
|
||||||
liveIssueIds={liveIssueIds}
|
liveIssueIds={liveIssueIds}
|
||||||
viewStateKey="paperclip:issues-view"
|
viewStateKey="paperclip:issues-view"
|
||||||
issueLinkState={issueLinkState}
|
issueLinkState={issueLinkState}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue