fix(issue-documents): address greptile review

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-03-14 09:17:46 -05:00
parent a7a64f11be
commit bc12f08c66
3 changed files with 65 additions and 41 deletions

View File

@@ -443,8 +443,7 @@ export function issueRoutes(db: Db, storage: StorageService) {
} }
const actor = getActorInfo(req); const actor = getActorInfo(req);
const before = await documentsSvc.getIssueDocumentByKey(issue.id, keyParsed.data); const result = await documentsSvc.upsertIssueDocument({
const doc = await documentsSvc.upsertIssueDocument({
issueId: issue.id, issueId: issue.id,
key: keyParsed.data, key: keyParsed.data,
title: req.body.title ?? null, title: req.body.title ?? null,
@@ -455,6 +454,7 @@ export function issueRoutes(db: Db, storage: StorageService) {
createdByAgentId: actor.agentId ?? null, createdByAgentId: actor.agentId ?? null,
createdByUserId: actor.actorType === "user" ? actor.actorId : null, createdByUserId: actor.actorType === "user" ? actor.actorId : null,
}); });
const doc = result.document;
await logActivity(db, { await logActivity(db, {
companyId: issue.companyId, companyId: issue.companyId,
@@ -462,7 +462,7 @@ export function issueRoutes(db: Db, storage: StorageService) {
actorId: actor.actorId, actorId: actor.actorId,
agentId: actor.agentId, agentId: actor.agentId,
runId: actor.runId, runId: actor.runId,
action: before ? "issue.document_updated" : "issue.document_created", action: result.created ? "issue.document_created" : "issue.document_updated",
entityType: "issue", entityType: "issue",
entityId: issue.id, entityId: issue.id,
details: { details: {
@@ -474,7 +474,7 @@ export function issueRoutes(db: Db, storage: StorageService) {
}, },
}); });
res.status(before ? 200 : 201).json(doc); res.status(result.created ? 201 : 200).json(doc);
}); });
router.get("/issues/:id/documents/:key/revisions", async (req, res) => { router.get("/issues/:id/documents/:key/revisions", async (req, res) => {

View File

@@ -297,15 +297,18 @@ export function documentService(db: Db) {
.where(eq(issueDocuments.documentId, existing.id)); .where(eq(issueDocuments.documentId, existing.id));
return { return {
...existing, created: false as const,
title: input.title ?? null, document: {
format: input.format, ...existing,
body: input.body, title: input.title ?? null,
latestRevisionId: revision.id, format: input.format,
latestRevisionNumber: nextRevisionNumber, body: input.body,
updatedByAgentId: input.createdByAgentId ?? null, latestRevisionId: revision.id,
updatedByUserId: input.createdByUserId ?? null, latestRevisionNumber: nextRevisionNumber,
updatedAt: now, updatedByAgentId: input.createdByAgentId ?? null,
updatedByUserId: input.createdByUserId ?? null,
updatedAt: now,
},
}; };
} }
@@ -360,21 +363,24 @@ export function documentService(db: Db) {
}); });
return { return {
id: document.id, created: true as const,
companyId: issue.companyId, document: {
issueId: issue.id, id: document.id,
key, companyId: issue.companyId,
title: document.title, issueId: issue.id,
format: document.format, key,
body: document.latestBody, title: document.title,
latestRevisionId: revision.id, format: document.format,
latestRevisionNumber: 1, body: document.latestBody,
createdByAgentId: document.createdByAgentId, latestRevisionId: revision.id,
createdByUserId: document.createdByUserId, latestRevisionNumber: 1,
updatedByAgentId: document.updatedByAgentId, createdByAgentId: document.createdByAgentId,
updatedByUserId: document.updatedByUserId, createdByUserId: document.createdByUserId,
createdAt: document.createdAt, updatedByAgentId: document.updatedByAgentId,
updatedAt: document.updatedAt, updatedByUserId: document.updatedByUserId,
createdAt: document.createdAt,
updatedAt: document.updatedAt,
},
}; };
}); });
} catch (error) { } catch (error) {

View File

@@ -31,6 +31,7 @@ type DraftState = {
type DocumentConflictState = { type DocumentConflictState = {
key: string; key: string;
serverDocument: IssueDocument; serverDocument: IssueDocument;
localDraft: DraftState;
showRemote: boolean; showRemote: boolean;
}; };
@@ -189,14 +190,15 @@ export function IssueDocumentsSection({
const beginEdit = (key: string) => { const beginEdit = (key: string) => {
const doc = sortedDocuments.find((entry) => entry.key === key); const doc = sortedDocuments.find((entry) => entry.key === key);
if (!doc) return; if (!doc) return;
const conflictedDraft = documentConflict?.key === key ? documentConflict.localDraft : null;
setFoldedDocumentKeys((current) => current.filter((entry) => entry !== key)); setFoldedDocumentKeys((current) => current.filter((entry) => entry !== key));
resetAutosaveState(); resetAutosaveState();
setDocumentConflict((current) => current?.key === key ? current : null); setDocumentConflict((current) => current?.key === key ? current : null);
setDraft({ setDraft({
key: doc.key, key: conflictedDraft?.key ?? doc.key,
title: doc.title ?? "", title: conflictedDraft?.title ?? doc.title ?? "",
body: doc.body, body: conflictedDraft?.body ?? doc.body,
baseRevisionId: doc.latestRevisionId, baseRevisionId: conflictedDraft?.baseRevisionId ?? doc.latestRevisionId,
isNew: false, isNew: false,
}); });
setError(null); setError(null);
@@ -306,6 +308,13 @@ export function IssueDocumentsSection({
setDocumentConflict({ setDocumentConflict({
key: normalizedKey, key: normalizedKey,
serverDocument: latestDocument, serverDocument: latestDocument,
localDraft: {
key: normalizedKey,
title: isPlanKey(normalizedKey) ? "" : normalizedTitle,
body: currentDraft.body,
baseRevisionId: currentDraft.baseRevisionId,
isNew: false,
},
showRemote: true, showRemote: true,
}); });
setFoldedDocumentKeys((current) => current.filter((key) => key !== normalizedKey)); setFoldedDocumentKeys((current) => current.filter((key) => key !== normalizedKey));
@@ -338,10 +347,14 @@ export function IssueDocumentsSection({
}, [documentConflict, resetAutosaveState]); }, [documentConflict, resetAutosaveState]);
const overwriteDocumentFromDraft = useCallback(async (key: string) => { const overwriteDocumentFromDraft = useCallback(async (key: string) => {
if (documentConflict?.key !== key || !draft || draft.key !== key || draft.isNew) return; if (documentConflict?.key !== key) return;
const sourceDraft =
draft && draft.key === key && !draft.isNew
? draft
: documentConflict.localDraft;
await commitDraft( await commitDraft(
{ {
...draft, ...sourceDraft,
baseRevisionId: documentConflict.serverDocument.latestRevisionId, baseRevisionId: documentConflict.serverDocument.latestRevisionId,
}, },
{ {
@@ -352,6 +365,17 @@ export function IssueDocumentsSection({
); );
}, [commitDraft, documentConflict, draft]); }, [commitDraft, documentConflict, draft]);
const keepConflictedDraft = useCallback((key: string) => {
if (documentConflict?.key !== key) return;
setDraft(documentConflict.localDraft);
setDocumentConflict((current) =>
current?.key === key
? { ...current, showRemote: false }
: current,
);
setError(null);
}, [documentConflict]);
const copyDocumentBody = useCallback(async (key: string, body: string) => { const copyDocumentBody = useCallback(async (key: string, body: string) => {
try { try {
await navigator.clipboard.writeText(body); await navigator.clipboard.writeText(body);
@@ -727,13 +751,7 @@ export function IssueDocumentsSection({
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => onClick={() => keepConflictedDraft(doc.key)}
setDocumentConflict((current) =>
current?.key === doc.key
? { ...current, showRemote: false }
: current,
)
}
> >
Keep my draft Keep my draft
</Button> </Button>