Files
Codewalkers/apps/server/db/repositories/drizzle/review-comment.ts
Lukas May 1e723611e7 feat: Allow editing review comments
Add update method to ReviewCommentRepository, updateReviewComment tRPC
procedure, and inline edit UI in CommentThread. Edit button appears on
user-authored comments (not agent comments) when unresolved. Uses the
existing CommentForm with a new initialValue prop.
2026-03-06 11:58:08 +01:00

128 lines
3.6 KiB
TypeScript

/**
* Drizzle Review Comment Repository Adapter
*
* Implements ReviewCommentRepository interface using Drizzle ORM.
*/
import { eq, asc } from 'drizzle-orm';
import { nanoid } from 'nanoid';
import type { DrizzleDatabase } from '../../index.js';
import { reviewComments, type ReviewComment } from '../../schema.js';
import type { ReviewCommentRepository, CreateReviewCommentData } from '../review-comment-repository.js';
export class DrizzleReviewCommentRepository implements ReviewCommentRepository {
constructor(private db: DrizzleDatabase) {}
async create(data: CreateReviewCommentData): Promise<ReviewComment> {
const now = new Date();
const id = nanoid();
await this.db.insert(reviewComments).values({
id,
phaseId: data.phaseId,
filePath: data.filePath,
lineNumber: data.lineNumber,
lineType: data.lineType,
body: data.body,
author: data.author ?? 'user',
parentCommentId: data.parentCommentId ?? null,
resolved: false,
createdAt: now,
updatedAt: now,
});
const rows = await this.db
.select()
.from(reviewComments)
.where(eq(reviewComments.id, id))
.limit(1);
return rows[0]!;
}
async createReply(parentCommentId: string, body: string, author?: string): Promise<ReviewComment> {
// Fetch parent comment to copy context fields
const parentRows = await this.db
.select()
.from(reviewComments)
.where(eq(reviewComments.id, parentCommentId))
.limit(1);
const parent = parentRows[0];
if (!parent) {
throw new Error(`Parent comment not found: ${parentCommentId}`);
}
const now = new Date();
const id = nanoid();
await this.db.insert(reviewComments).values({
id,
phaseId: parent.phaseId,
filePath: parent.filePath,
lineNumber: parent.lineNumber,
lineType: parent.lineType,
body,
author: author ?? 'user',
parentCommentId,
resolved: false,
createdAt: now,
updatedAt: now,
});
const rows = await this.db
.select()
.from(reviewComments)
.where(eq(reviewComments.id, id))
.limit(1);
return rows[0]!;
}
async findByPhaseId(phaseId: string): Promise<ReviewComment[]> {
return this.db
.select()
.from(reviewComments)
.where(eq(reviewComments.phaseId, phaseId))
.orderBy(asc(reviewComments.createdAt));
}
async update(id: string, body: string): Promise<ReviewComment | null> {
await this.db
.update(reviewComments)
.set({ body, updatedAt: new Date() })
.where(eq(reviewComments.id, id));
const rows = await this.db
.select()
.from(reviewComments)
.where(eq(reviewComments.id, id))
.limit(1);
return rows[0] ?? null;
}
async resolve(id: string): Promise<ReviewComment | null> {
await this.db
.update(reviewComments)
.set({ resolved: true, updatedAt: new Date() })
.where(eq(reviewComments.id, id));
const rows = await this.db
.select()
.from(reviewComments)
.where(eq(reviewComments.id, id))
.limit(1);
return rows[0] ?? null;
}
async unresolve(id: string): Promise<ReviewComment | null> {
await this.db
.update(reviewComments)
.set({ resolved: false, updatedAt: new Date() })
.where(eq(reviewComments.id, id));
const rows = await this.db
.select()
.from(reviewComments)
.where(eq(reviewComments.id, id))
.limit(1);
return rows[0] ?? null;
}
async delete(id: string): Promise<void> {
await this.db
.delete(reviewComments)
.where(eq(reviewComments.id, id));
}
}