From e41db56f16bcd97af667100b71254ec2f0af7ec5 Mon Sep 17 00:00:00 2001 From: The Doom Lab Date: Wed, 20 Aug 2025 15:50:27 -0500 Subject: [PATCH 1/8] fix PMs should see notes --- src/notes/schemas.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/notes/schemas.ts b/src/notes/schemas.ts index 59dab6cc..f4552120 100644 --- a/src/notes/schemas.ts +++ b/src/notes/schemas.ts @@ -1,13 +1,14 @@ import { z } from "zod" +import { NoteVisibility } from "@prisma/client" -export const NoteVisibilityEnum = z.enum(["PRIVATE", "SHARED"]) +export const NoteVisibilityEnum = z.nativeEnum(NoteVisibility) export const CreateNoteInput = z.object({ projectId: z.number().int().positive(), title: z.string().max(200).optional(), contentMarkdown: z.string().optional(), contentJSON: z.any().optional(), - visibility: NoteVisibilityEnum.default("PRIVATE"), + visibility: NoteVisibilityEnum.default(NoteVisibility.PRIVATE), pinned: z.boolean().optional(), }) From 2c473e56017ea4d53834e8277a0dd7e5403992b6 Mon Sep 17 00:00:00 2001 From: The Doom Lab Date: Wed, 20 Aug 2025 16:26:50 -0500 Subject: [PATCH 2/8] update and fix note sharing --- src/notes/mutations/createNote.ts | 7 ++++++- src/notes/mutations/updateNote.ts | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/notes/mutations/createNote.ts b/src/notes/mutations/createNote.ts index 128229a0..2c9d9a35 100644 --- a/src/notes/mutations/createNote.ts +++ b/src/notes/mutations/createNote.ts @@ -2,9 +2,14 @@ import db from "db" import { resolver } from "@blitzjs/rpc" import { CreateNoteInput } from "src/notes/schemas" import { NoteVisibility, MemberPrivileges } from "@prisma/client" +import { z } from "zod" export default resolver.pipe( - resolver.zod(CreateNoteInput), + resolver.zod( + CreateNoteInput.extend({ + visibility: z.nativeEnum(NoteVisibility).optional(), + }) + ), resolver.authorize(), async ({ projectId, ...data }, ctx) => { const userId = ctx.session.userId! diff --git a/src/notes/mutations/updateNote.ts b/src/notes/mutations/updateNote.ts index 044ac2fc..32f0b9d8 100644 --- a/src/notes/mutations/updateNote.ts +++ b/src/notes/mutations/updateNote.ts @@ -2,9 +2,14 @@ import db from "db" import { resolver } from "@blitzjs/rpc" import { UpdateNoteInput } from "src/notes/schemas" import { NoteVisibility, MemberPrivileges } from "@prisma/client" +import { z } from "zod" export default resolver.pipe( - resolver.zod(UpdateNoteInput), + resolver.zod( + UpdateNoteInput.extend({ + visibility: z.nativeEnum(NoteVisibility).optional(), + }) + ), resolver.authorize(), async ({ id, ...data }, ctx) => { const userId = ctx.session.userId! From 9a62769925fcece482c297c2b723513497a966a8 Mon Sep 17 00:00:00 2001 From: The Doom Lab Date: Wed, 20 Aug 2025 16:38:59 -0500 Subject: [PATCH 3/8] fix type errors --- src/notes/mutations/createNote.ts | 2 +- src/notes/mutations/updateNote.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/notes/mutations/createNote.ts b/src/notes/mutations/createNote.ts index 2c9d9a35..19df70ab 100644 --- a/src/notes/mutations/createNote.ts +++ b/src/notes/mutations/createNote.ts @@ -7,7 +7,7 @@ import { z } from "zod" export default resolver.pipe( resolver.zod( CreateNoteInput.extend({ - visibility: z.nativeEnum(NoteVisibility).optional(), + visibility: z.union([z.nativeEnum(NoteVisibility), z.literal("SHARED")]).optional(), }) ), resolver.authorize(), diff --git a/src/notes/mutations/updateNote.ts b/src/notes/mutations/updateNote.ts index 32f0b9d8..59db8eab 100644 --- a/src/notes/mutations/updateNote.ts +++ b/src/notes/mutations/updateNote.ts @@ -7,7 +7,7 @@ import { z } from "zod" export default resolver.pipe( resolver.zod( UpdateNoteInput.extend({ - visibility: z.nativeEnum(NoteVisibility).optional(), + visibility: z.union([z.nativeEnum(NoteVisibility), z.literal("SHARED")]).optional(), }) ), resolver.authorize(), From 5e5e1cb53a92550c304bf5d6afc9cdc4f89a1b72 Mon Sep 17 00:00:00 2001 From: The Doom Lab Date: Thu, 21 Aug 2025 13:08:45 -0500 Subject: [PATCH 4/8] update main dashboard comment issues --- src/projects/queries/getDashboardProjects.ts | 148 ++++++++++-------- src/widgets/components/ColumnHelpers.tsx | 22 ++- .../components/widgets/MainNotification.tsx | 4 +- .../components/widgets/MainTotalTask.tsx | 2 +- 4 files changed, 105 insertions(+), 71 deletions(-) diff --git a/src/projects/queries/getDashboardProjects.ts b/src/projects/queries/getDashboardProjects.ts index 8762964f..1df8aadd 100644 --- a/src/projects/queries/getDashboardProjects.ts +++ b/src/projects/queries/getDashboardProjects.ts @@ -1,75 +1,91 @@ -import { ProjectWithNewCommentsCount } from "src/core/types" import { Ctx } from "blitz" import { resolver } from "@blitzjs/rpc" -import getProjects from "./getProjects" +import db from "db" -export default resolver.pipe(resolver.authorize(), async (undefined, ctx: Ctx) => { - const { projects } = await getProjects( - { - where: { - projectMembers: { - some: { - users: { - some: { id: ctx.session.userId as number }, - }, - deleted: false, - }, - }, - }, - orderBy: { updatedAt: "asc" }, - take: 3, - include: { - tasks: { - where: { - taskLogs: { - some: { - assignedTo: { - id: ctx.session.userId as number, - }, - }, - }, - }, - include: { - taskLogs: { - include: { - comments: { - include: { - commentReadStatus: { - where: { - read: false, - }, - }, - }, - }, - }, - }, - }, - }, +// Return shape for the dashboard widget +type ProjectDashboardItem = { + id: number + name: string + updatedAt: Date | null + newCommentsCount: number +} + +export default resolver.pipe(resolver.authorize(), async (_: unknown, ctx: Ctx) => { + const userId = ctx.session.userId as number + + // 1) Projects for user + their projectMemberId per project (minimal select) + const projects = await db.project.findMany({ + where: { + projectMembers: { some: { users: { some: { id: userId } }, deleted: false } }, + }, + select: { + id: true, + name: true, + createdAt: true, + projectMembers: { + where: { users: { some: { id: userId } } }, + select: { id: true }, + take: 1, }, }, - ctx - ) + }) + + if (projects.length === 0) { + return { projects: [] as ProjectDashboardItem[] } + } - return { - projects: projects.map((project: ProjectWithNewCommentsCount) => { - const newCommentsCount = project.tasks.reduce((acc, task) => { - return ( - acc + - task.taskLogs.reduce((logAcc, log) => { - return ( - logAcc + - log.comments.reduce((commentAcc, comment) => { - return commentAcc + (comment.commentReadStatus?.length || 0) - }, 0) - ) - }, 0) - ) - }, 0) + const projectIds = projects.map((p) => p.id) - return { - ...project, - newCommentsCount, - } - }), + // 2) Fetch recent TaskLogs for these projects and reduce to latest per project + // (order and cap to avoid scanning excessive rows in huge workspaces) + const recentLogs = await db.taskLog.findMany({ + where: { task: { projectId: { in: projectIds } } }, + select: { createdAt: true, task: { select: { projectId: true } } }, + orderBy: { createdAt: "desc" }, + take: 2000, + }) + + const latestByProject = new Map() + for (const l of recentLogs) { + const pid = l.task.projectId + if (!latestByProject.has(pid)) latestByProject.set(pid, l.createdAt) + } + + // If a project has no TaskLogs in the recentLogs window, there would be no latestByProject entry. + // Fallback to the project's own createdAt so lastUpdate is never null. + const decorated = projects.map((p: any) => ({ + id: p.id, + name: p.name, + myProjectMemberId: p.projectMembers[0]?.id as number | undefined, + lastUpdate: (latestByProject.get(p.id) ?? p.createdAt) as Date, + })) + + const top3 = decorated + .sort((a, b) => (b.lastUpdate?.getTime() ?? 0) - (a.lastUpdate?.getTime() ?? 0)) + .slice(0, 3) + + // 4) For each project, compute unread = total comments in project - read:false for this pmId + const results: ProjectDashboardItem[] = [] + for (const p of top3) { + const pmId = p.myProjectMemberId + let unread = 0 + if (pmId) { + unread = await db.commentReadStatus.count({ + where: { + projectMemberId: pmId, + read: false, + comment: { taskLog: { task: { projectId: p.id } } }, + }, + }) + } + + results.push({ + id: p.id, + name: p.name, + updatedAt: p.lastUpdate, + newCommentsCount: unread, + }) } + + return { projects: results } }) diff --git a/src/widgets/components/ColumnHelpers.tsx b/src/widgets/components/ColumnHelpers.tsx index 5b233f30..2864fb9e 100644 --- a/src/widgets/components/ColumnHelpers.tsx +++ b/src/widgets/components/ColumnHelpers.tsx @@ -1,11 +1,16 @@ import { Routes } from "@blitzjs/next" -import { ChatBubbleOvalLeftEllipsisIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline" +import { + ChatBubbleOvalLeftEllipsisIcon, + MagnifyingGlassIcon, + InformationCircleIcon, +} from "@heroicons/react/24/outline" import { Prisma, Notification, Task, TaskLog } from "@prisma/client" import { ColumnDef, createColumnHelper } from "@tanstack/react-table" import NotificationMessage from "src/notifications/components/NotificationMessage" import Link from "next/link" import DateFormat from "src/core/components/DateFormat" import { ProjectWithNewCommentsCount } from "src/core/types" +import { Tooltip } from "react-tooltip" // Tasks table type TaskWithProject = Prisma.TaskGetPayload<{ @@ -78,7 +83,20 @@ export const projectColumns: ColumnDef[] = [ }), projectColumnHelper.accessor("newCommentsCount", { id: "newComments", - header: "Comments", + header: () => ( +
+ Comments + + +
+ ), enableColumnFilter: false, enableSorting: false, cell: (info) => ( diff --git a/src/widgets/components/widgets/MainNotification.tsx b/src/widgets/components/widgets/MainNotification.tsx index f55a62fb..fab608a4 100644 --- a/src/widgets/components/widgets/MainNotification.tsx +++ b/src/widgets/components/widgets/MainNotification.tsx @@ -19,7 +19,7 @@ const MainNotification: React.FC<{ size: "SMALL" | "MEDIUM" | "LARGE" }> = ({ si +
{["Task", "Comment", "Project", "Other"].map((type) => (
@@ -46,7 +46,7 @@ const MainNotification: React.FC<{ size: "SMALL" | "MEDIUM" | "LARGE" }> = ({ si /> } tooltipId="tool-notifications" - tooltipContent="Number of notifications all projects" + tooltipContent="Number of notifications all projects, clear them by marking them read." size={size} /> ) diff --git a/src/widgets/components/widgets/MainTotalTask.tsx b/src/widgets/components/widgets/MainTotalTask.tsx index f32838ad..24206ea6 100644 --- a/src/widgets/components/widgets/MainTotalTask.tsx +++ b/src/widgets/components/widgets/MainTotalTask.tsx @@ -86,7 +86,7 @@ const AllTaskTotal: React.FC<{ size: "SMALL" | "MEDIUM" | "LARGE" }> = ({ size } /> } tooltipId="tool-tasks" - tooltipContent="Percent of tasks completed and new comments on tasks" + tooltipContent="Percent of tasks completed and new comments on only tasks assigned to you" size={size} newCommentsCount={newCommentsCount} /> From 004e79955de30246b83918883b5c8f5ff3200b13 Mon Sep 17 00:00:00 2001 From: The Doom Lab Date: Thu, 21 Aug 2025 14:06:37 -0500 Subject: [PATCH 5/8] fix task comments notifications --- src/tasks/components/TaskItems.tsx | 2 +- src/tasks/hooks/useTaskBoardData.ts | 23 +++++++++++++++++-- src/tasks/queries/getTaskStats.ts | 3 +++ .../widgets/ProjectNotification.tsx | 4 ++-- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/tasks/components/TaskItems.tsx b/src/tasks/components/TaskItems.tsx index 94ac2c31..13c6b7d4 100644 --- a/src/tasks/components/TaskItems.tsx +++ b/src/tasks/components/TaskItems.tsx @@ -58,7 +58,7 @@ const TaskItems = ({