mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-16 19:00:38 +09:00
Add feedback voting and thumbs capture flow
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
3db6bdfc3c
commit
c0d0d03bce
66 changed files with 18988 additions and 78 deletions
|
|
@ -1,6 +1,13 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { DocumentRevision, Issue, IssueDocument } from "@paperclipai/shared";
|
||||
import type {
|
||||
DocumentRevision,
|
||||
FeedbackDataSharingPreference,
|
||||
FeedbackVote,
|
||||
FeedbackVoteValue,
|
||||
Issue,
|
||||
IssueDocument,
|
||||
} from "@paperclipai/shared";
|
||||
import { useLocation } from "@/lib/router";
|
||||
import { ApiError } from "../api/client";
|
||||
import { issuesApi } from "../api/issues";
|
||||
|
|
@ -9,6 +16,7 @@ import { queryKeys } from "../lib/queryKeys";
|
|||
import { cn, relativeTime } from "../lib/utils";
|
||||
import { MarkdownBody } from "./MarkdownBody";
|
||||
import { MarkdownEditor, type MentionOption } from "./MarkdownEditor";
|
||||
import { OutputFeedbackButtons } from "./OutputFeedbackButtons";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
|
|
@ -101,14 +109,26 @@ function documentHasUnsavedChanges(doc: IssueDocument, draft: DraftState | null)
|
|||
export function IssueDocumentsSection({
|
||||
issue,
|
||||
canDeleteDocuments,
|
||||
feedbackVotes = [],
|
||||
feedbackDataSharingPreference = "prompt",
|
||||
feedbackTermsUrl = null,
|
||||
mentions,
|
||||
imageUploadHandler,
|
||||
onVote,
|
||||
extraActions,
|
||||
}: {
|
||||
issue: Issue;
|
||||
canDeleteDocuments: boolean;
|
||||
feedbackVotes?: FeedbackVote[];
|
||||
feedbackDataSharingPreference?: FeedbackDataSharingPreference;
|
||||
feedbackTermsUrl?: string | null;
|
||||
mentions?: MentionOption[];
|
||||
imageUploadHandler?: (file: File) => Promise<string>;
|
||||
onVote?: (
|
||||
revisionId: string,
|
||||
vote: FeedbackVoteValue,
|
||||
options?: { allowSharing?: boolean; reason?: string },
|
||||
) => Promise<void>;
|
||||
extraActions?: ReactNode;
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
|
|
@ -207,6 +227,15 @@ export function IssueDocumentsSection({
|
|||
});
|
||||
}, [documents]);
|
||||
|
||||
const feedbackVoteByTargetId = useMemo(() => {
|
||||
const map = new Map<string, FeedbackVoteValue>();
|
||||
for (const feedbackVote of feedbackVotes) {
|
||||
if (feedbackVote.targetType !== "issue_document_revision") continue;
|
||||
map.set(feedbackVote.targetId, feedbackVote.vote);
|
||||
}
|
||||
return map;
|
||||
}, [feedbackVotes]);
|
||||
|
||||
const hasRealPlan = sortedDocuments.some((doc) => doc.key === "plan");
|
||||
const isEmpty = sortedDocuments.length === 0 && !issue.legacyPlanDocument;
|
||||
const newDocumentKeyError =
|
||||
|
|
@ -718,6 +747,7 @@ export function IssueDocumentsSection({
|
|||
const displayedRevisionNumber = selectedHistoricalRevision?.revisionNumber ?? doc.latestRevisionNumber;
|
||||
const displayedUpdatedAt = selectedHistoricalRevision?.createdAt ?? doc.updatedAt;
|
||||
const showTitle = !isPlanKey(doc.key) && !!displayedTitle.trim() && !titlesMatchKey(displayedTitle, doc.key);
|
||||
const canVoteOnDocument = Boolean(doc.latestRevisionId && doc.updatedByAgentId && !doc.updatedByUserId && onVote);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -1053,6 +1083,16 @@ export function IssueDocumentsSection({
|
|||
: ""}
|
||||
</span>
|
||||
</div>
|
||||
{canVoteOnDocument && doc.latestRevisionId ? (
|
||||
<OutputFeedbackButtons
|
||||
activeVote={feedbackVoteByTargetId.get(doc.latestRevisionId) ?? null}
|
||||
sharingPreference={feedbackDataSharingPreference}
|
||||
termsUrl={feedbackTermsUrl}
|
||||
onVote={(vote: FeedbackVoteValue, options?: { allowSharing?: boolean; reason?: string }) =>
|
||||
onVote?.(doc.latestRevisionId!, vote, options) ?? Promise.resolve()
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue