Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions apps/web/app/api/internal/notes/[taskId]/read/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export async function POST(_request: Request, { params }: RouteContext) {
},
select: {
id: true,
note: true,
summaryUpdatedAt: true,
taskUpdatedAt: true,
project: { select: { companyId: true } },
},
})
Expand All @@ -39,20 +42,32 @@ export async function POST(_request: Request, { params }: RouteContext) {
return notFound("Review reader agent not found.")
}

await prisma.taskReadMarker.upsert({
where: {
taskId_agentId_status: {
const readAt = new Date()

await prisma.$transaction(async (tx) => {
if (!task.summaryUpdatedAt) {
await tx.task.update({
where: { id: task.id },
data: { summaryUpdatedAt: task.taskUpdatedAt },
})
}

await tx.taskReadMarker.upsert({
where: {
taskId_agentId_status: {
taskId: task.id,
agentId: reviewReader.id,
status: Status.done,
},
},
create: {
taskId: task.id,
agentId: reviewReader.id,
status: Status.done,
readAt,
},
},
create: {
taskId: task.id,
agentId: reviewReader.id,
status: Status.done,
},
update: { readAt: new Date() },
update: { readAt },
})
})

return NextResponse.json({
Expand Down
7 changes: 5 additions & 2 deletions apps/web/app/api/internal/notes/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export async function GET(request: NextRequest) {
status: true,
note: true,
summaryUpdatedAt: true,
taskUpdatedAt: true,
assigned: {
select: {
id: true,
Expand All @@ -74,7 +75,9 @@ export async function GET(request: NextRequest) {
const unreadNotes = notes.filter((task) => {
const readAt = task.readMarkers[0]?.readAt

return !readAt || !task.summaryUpdatedAt || readAt < task.summaryUpdatedAt
const summaryUpdatedAt = task.summaryUpdatedAt ?? task.taskUpdatedAt

return !readAt || readAt < summaryUpdatedAt
})

return NextResponse.json({
Expand All @@ -85,7 +88,7 @@ export async function GET(request: NextRequest) {
name: task.name,
status: task.status,
note: task.note ?? "",
summaryUpdatedAt: task.summaryUpdatedAt?.toISOString() ?? null,
summaryUpdatedAt: (task.summaryUpdatedAt ?? task.taskUpdatedAt).toISOString(),
assigned: task.assigned,
project: task.project,
})),
Expand Down
7 changes: 5 additions & 2 deletions apps/web/app/dashboard/notes/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default async function NotesPage({ searchParams }: NotesPageProps) {
status: true,
note: true,
summaryUpdatedAt: true,
taskUpdatedAt: true,
assigned: {
select: {
id: true,
Expand All @@ -69,7 +70,9 @@ export default async function NotesPage({ searchParams }: NotesPageProps) {
const unreadNotes = notes.filter((task) => {
const readAt = task.readMarkers[0]?.readAt

return !readAt || !task.summaryUpdatedAt || readAt < task.summaryUpdatedAt
const summaryUpdatedAt = task.summaryUpdatedAt ?? task.taskUpdatedAt

return !readAt || readAt < summaryUpdatedAt
})

return (
Expand All @@ -96,7 +99,7 @@ export default async function NotesPage({ searchParams }: NotesPageProps) {
name: task.name,
status: task.status,
note: task.note ?? "",
summaryUpdatedAt: task.summaryUpdatedAt?.toISOString() ?? null,
summaryUpdatedAt: (task.summaryUpdatedAt ?? task.taskUpdatedAt).toISOString(),
assigned: task.assigned,
project: task.project,
}))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,5 @@ CREATE INDEX "TaskDependency_dependencyTaskId_idx" ON "TaskDependency"("dependen
ALTER TABLE "TaskDependency" ADD CONSTRAINT "TaskDependency_blockedTaskId_fkey" FOREIGN KEY ("blockedTaskId") REFERENCES "Task"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "TaskDependency" ADD CONSTRAINT "TaskDependency_dependencyTaskId_fkey" FOREIGN KEY ("dependencyTaskId") REFERENCES "Task"("id") ON DELETE CASCADE ON UPDATE CASCADE;

UPDATE "Task"
SET "summaryUpdatedAt" = CURRENT_TIMESTAMP
WHERE "note" IS NOT NULL;
-- summaryUpdatedAt is intentionally left null here. A later migration backfills
-- it from taskUpdatedAt after that deterministic freshness column exists.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Backfill deterministic summary timestamps for legacy completed task notes.
-- Some done tasks were created with note text before summaryUpdatedAt existed or was
-- consistently written. Use the task's last update timestamp instead of CURRENT_TIMESTAMP
-- so existing read markers newer than the legacy note stay read.
UPDATE "Task"
SET "summaryUpdatedAt" = "taskUpdatedAt"
WHERE "note" IS NOT NULL
AND "summaryUpdatedAt" IS NULL;
Loading