mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 02:33:01 +00:00
table rows as links
This commit is contained in:
@@ -3,7 +3,7 @@ import Avatar from "@/components/avatar";
|
|||||||
import { useSelection } from "@/components/selection-provider";
|
import { useSelection } from "@/components/selection-provider";
|
||||||
import StatusTag from "@/components/status-tag";
|
import StatusTag from "@/components/status-tag";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
import { useIssues, useSelectedOrganisation } from "@/lib/query/hooks";
|
import { useIssues, useSelectedOrganisation, useSelectedProject } from "@/lib/query/hooks";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export function IssuesTable({
|
export function IssuesTable({
|
||||||
@@ -16,10 +16,28 @@ export function IssuesTable({
|
|||||||
const { selectedProjectId, selectedIssueId, selectIssue } = useSelection();
|
const { selectedProjectId, selectedIssueId, selectIssue } = useSelection();
|
||||||
const { data: issuesData = [] } = useIssues(selectedProjectId);
|
const { data: issuesData = [] } = useIssues(selectedProjectId);
|
||||||
const selectedOrganisation = useSelectedOrganisation();
|
const selectedOrganisation = useSelectedOrganisation();
|
||||||
|
const selectedProject = useSelectedProject();
|
||||||
const statuses = selectedOrganisation?.Organisation.statuses ?? {};
|
const statuses = selectedOrganisation?.Organisation.statuses ?? {};
|
||||||
|
|
||||||
const issues = useMemo(() => [...issuesData].reverse(), [issuesData]);
|
const issues = useMemo(() => [...issuesData].reverse(), [issuesData]);
|
||||||
|
|
||||||
|
const getIssueUrl = (issueNumber: number) => {
|
||||||
|
if (!selectedOrganisation || !selectedProject) return "#";
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("o", selectedOrganisation.Organisation.slug.toLowerCase());
|
||||||
|
params.set("p", selectedProject.Project.key.toLowerCase());
|
||||||
|
params.set("i", issueNumber.toString());
|
||||||
|
return `/app?${params.toString()}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLinkClick = (e: React.MouseEvent) => {
|
||||||
|
if (e.metaKey || e.ctrlKey || e.shiftKey) {
|
||||||
|
e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table className={cn("table-fixed", className)}>
|
<Table className={cn("table-fixed", className)}>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
@@ -51,13 +69,23 @@ export function IssuesTable({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(columns.id == null || columns.id === true) && (
|
{(columns.id == null || columns.id === true) && (
|
||||||
<TableCell className="font-medium border-r text-right">
|
<TableCell className="font-medium border-r text-right p-0">
|
||||||
{issueData.Issue.number.toString().padStart(3, "0")}
|
<a
|
||||||
|
href={getIssueUrl(issueData.Issue.number)}
|
||||||
|
onClick={handleLinkClick}
|
||||||
|
className="block w-full h-full px-2 py-1 text-inherit hover:underline decoration-transparent"
|
||||||
|
>
|
||||||
|
{issueData.Issue.number.toString().padStart(3, "0")}
|
||||||
|
</a>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
{(columns.title == null || columns.title === true) && (
|
{(columns.title == null || columns.title === true) && (
|
||||||
<TableCell className="min-w-0">
|
<TableCell className="min-w-0 p-0">
|
||||||
<div className="flex items-center gap-2 min-w-0">
|
<a
|
||||||
|
href={getIssueUrl(issueData.Issue.number)}
|
||||||
|
onClick={handleLinkClick}
|
||||||
|
className="flex items-center gap-2 min-w-0 w-full h-full px-2 py-1 text-inherit hover:underline decoration-transparent"
|
||||||
|
>
|
||||||
{(columns.status == null || columns.status === true) && (
|
{(columns.status == null || columns.status === true) && (
|
||||||
<StatusTag
|
<StatusTag
|
||||||
status={issueData.Issue.status}
|
status={issueData.Issue.status}
|
||||||
@@ -65,33 +93,47 @@ export function IssuesTable({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span className="truncate">{issueData.Issue.title}</span>
|
<span className="truncate">{issueData.Issue.title}</span>
|
||||||
</div>
|
</a>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
{(columns.description == null || columns.description === true) && (
|
{(columns.description == null || columns.description === true) && (
|
||||||
<TableCell className="overflow-hidden">{issueData.Issue.description}</TableCell>
|
<TableCell className="overflow-hidden p-0">
|
||||||
|
<a
|
||||||
|
href={getIssueUrl(issueData.Issue.number)}
|
||||||
|
onClick={handleLinkClick}
|
||||||
|
className="block w-full h-full px-2 py-1 text-inherit hover:underline decoration-transparent"
|
||||||
|
>
|
||||||
|
{issueData.Issue.description}
|
||||||
|
</a>
|
||||||
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
{(columns.assignee == null || columns.assignee === true) && (
|
{(columns.assignee == null || columns.assignee === true) && (
|
||||||
<TableCell className={"flex items-center justify-end py-0 h-[32px]"}>
|
<TableCell className="h-[32px] p-0">
|
||||||
{issueData.Assignees && issueData.Assignees.length > 0 && (
|
<a
|
||||||
<div className="flex items-center -space-x-2 pr-1.5">
|
href={getIssueUrl(issueData.Issue.number)}
|
||||||
{issueData.Assignees.slice(0, 3).map((assignee) => (
|
onClick={handleLinkClick}
|
||||||
<Avatar
|
className="flex items-center justify-end w-full h-full px-2"
|
||||||
key={assignee.id}
|
>
|
||||||
name={assignee.name}
|
{issueData.Assignees && issueData.Assignees.length > 0 && (
|
||||||
username={assignee.username}
|
<div className="flex items-center -space-x-2 pr-1.5">
|
||||||
avatarURL={assignee.avatarURL}
|
{issueData.Assignees.slice(0, 3).map((assignee) => (
|
||||||
textClass="text-xs"
|
<Avatar
|
||||||
className="ring-1 ring-background"
|
key={assignee.id}
|
||||||
/>
|
name={assignee.name}
|
||||||
))}
|
username={assignee.username}
|
||||||
{issueData.Assignees.length > 3 && (
|
avatarURL={assignee.avatarURL}
|
||||||
<span className="flex items-center justify-center w-6 h-6 text-[10px] font-medium bg-muted text-muted-foreground rounded-full ring-1 ring-background">
|
textClass="text-xs"
|
||||||
+{issueData.Assignees.length - 3}
|
className="ring-1 ring-background"
|
||||||
</span>
|
/>
|
||||||
)}
|
))}
|
||||||
</div>
|
{issueData.Assignees.length > 3 && (
|
||||||
)}
|
<span className="flex items-center justify-center w-6 h-6 text-[10px] font-medium bg-muted text-muted-foreground rounded-full ring-1 ring-background">
|
||||||
|
+{issueData.Assignees.length - 3}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
Reference in New Issue
Block a user