mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-05 12:29:37 +01:00
UX spec PAP-10520 §6. Inline IssueDocumentsSection plan editing/revision is unchanged; it gains an "Open in Documents" (ExternalLink) button routing to /documents/<id>?from=issue:<key> and a 💬N · ✎M feedback summary per doc. New issue "Documents" tab (next to Chat/Activity/Related work) surfaces every company document linked to the issue, reusing the library DocumentRow, pinning the plan to the top, with scoped search and an Add document control (link existing via picker / create new in Chat). Tab label shows the linked-doc count plus an open-feedback dot. Cross-issue document mention chip renders internal /<co>/documents/<id> links in markdown using the chip-match-document-* tokens, with a hover summary of the document title, status, and feedback counts. Feedback counts come from one issue-scoped company-documents query shared by the inline card and the tab (useIssueLinkedDocuments). Storybook stories added for UX sign-off. Co-Authored-By: Paperclip <noreply@paperclip.ing>
43 lines
1.4 KiB
TypeScript
43 lines
1.4 KiB
TypeScript
import { useMemo } from "react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import type { CompanyDocumentSummary } from "@paperclipai/shared";
|
|
import { documentsApi, type CompanyDocumentListFilters } from "../api/documents";
|
|
import { queryKeys } from "../lib/queryKeys";
|
|
|
|
/**
|
|
* Company documents linked to an issue (its inline issue documents plus any
|
|
* cross-linked company docs). Backs both the inline issue document card feedback
|
|
* counts and the issue "Documents" tab, so they stay in sync from one query.
|
|
*/
|
|
export function useIssueLinkedDocuments(
|
|
companyId: string | null | undefined,
|
|
issueId: string,
|
|
options: { search?: string; enabled?: boolean } = {},
|
|
) {
|
|
const filters: CompanyDocumentListFilters = useMemo(
|
|
() => ({
|
|
targetType: "issue",
|
|
targetId: issueId,
|
|
q: options.search?.trim() || undefined,
|
|
includeArchived: true,
|
|
limit: 200,
|
|
}),
|
|
[issueId, options.search],
|
|
);
|
|
|
|
const query = useQuery({
|
|
queryKey: queryKeys.documents.list(companyId ?? "", filters as Record<string, unknown>),
|
|
queryFn: () => documentsApi.list(companyId!, filters),
|
|
enabled: Boolean(companyId) && (options.enabled ?? true),
|
|
placeholderData: (previous) => previous,
|
|
});
|
|
|
|
const byId = useMemo(() => {
|
|
const map = new Map<string, CompanyDocumentSummary>();
|
|
for (const doc of query.data ?? []) map.set(doc.id, doc);
|
|
return map;
|
|
}, [query.data]);
|
|
|
|
return { ...query, documents: query.data ?? [], byId };
|
|
}
|