diff --git a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx
index a5dc9e23e..89d89f386 100644
--- a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx
+++ b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx
@@ -24,6 +24,8 @@ import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
import { Stepper } from '@documenso/ui/primitives/stepper';
import { useToast } from '@documenso/ui/primitives/use-toast';
+import { Comments } from '~/components/forms/comments';
+
export type EditDocumentFormProps = {
className?: string;
user: User;
@@ -179,60 +181,70 @@ export const EditDocumentForm = ({
const currentDocumentFlow = documentFlow[step];
return (
-
-
-
-
+
+
+
+
+
+
+
+
+
+
e.preventDefault()}
+ >
+ setStep(EditDocumentSteps[step - 1])}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ Comments
+
+
+
-
-
-
e.preventDefault()}
- >
- setStep(EditDocumentSteps[step - 1])}
- >
-
-
-
-
-
-
-
-
);
};
diff --git a/apps/web/src/components/comments/comment-card.tsx b/apps/web/src/components/comments/comment-card.tsx
new file mode 100644
index 000000000..91485d251
--- /dev/null
+++ b/apps/web/src/components/comments/comment-card.tsx
@@ -0,0 +1,29 @@
+import { cn } from '@documenso/ui/lib/utils';
+import { Button } from '@documenso/ui/primitives/button';
+
+import { LocaleDate } from '~/components/formatter/locale-date';
+
+export type CommentCardProps = {
+ comment: any;
+ className?: string;
+};
+
+export const CommentCard = ({ comment, className }: CommentCardProps) => {
+ return (
+
+
{comment.User.name}
+
+
+
+
{comment.comment}
+
+
+ );
+};
diff --git a/apps/web/src/components/forms/comments.tsx b/apps/web/src/components/forms/comments.tsx
new file mode 100644
index 000000000..02807b5c7
--- /dev/null
+++ b/apps/web/src/components/forms/comments.tsx
@@ -0,0 +1,32 @@
+'use client';
+
+import { CornerDownRight } from 'lucide-react';
+
+import { trpc } from '@documenso/trpc/react';
+
+import { CommentCard } from '~/components/comments/comment-card';
+
+export const Comments = () => {
+ const { data: comments } = trpc.comment.getComments.useQuery();
+
+ console.log(comments);
+ return (
+
+ {comments?.map((comment) => (
+
+
+ {comment.replies && comment.replies.length > 0 ? (
+
+ {comment.replies.map((reply) => (
+
+
+
+
+ ))}
+
+ ) : null}
+
+ ))}
+
+ );
+};
diff --git a/apps/web/src/components/forms/profile.tsx b/apps/web/src/components/forms/profile.tsx
index 0ce5c7f3d..793919c34 100644
--- a/apps/web/src/components/forms/profile.tsx
+++ b/apps/web/src/components/forms/profile.tsx
@@ -124,6 +124,7 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
className="h-44 w-full"
containerClassName="rounded-lg border bg-background"
defaultValue={user.signature ?? undefined}
+
onChange={(v) => onChange(v ?? '')}
/>
diff --git a/package.json b/package.json
index 30076100f..ce02986e0 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"name": "@documenso/root",
"workspaces": [
"apps/*",
+
"packages/*"
],
"dependencies": {},
diff --git a/packages/lib/server-only/auth/api-auth-middleware.ts b/packages/lib/server-only/auth/api-auth-middleware.ts
new file mode 100644
index 000000000..f452f2b28
--- /dev/null
+++ b/packages/lib/server-only/auth/api-auth-middleware.ts
@@ -0,0 +1,33 @@
+import { getUserByApiToken } from '@documenso/lib/server-only/public-api/get-user-by-token';
+
+export type Headers = {
+ headers: {
+ authorization: string;
+ };
+};
+
+export const authenticatedMiddleware = (fn: (args: T) => Promise) => {
+ return async (args: T) => {
+ if (!args.headers.authorization) {
+ return {
+ status: 401,
+ body: {
+ message: 'Unauthorized access',
+ },
+ };
+ }
+
+ try {
+ await getUserByApiToken({ token: args.headers.authorization });
+ } catch (err) {
+ return {
+ status: 401,
+ body: {
+ message: 'Unauthorized access',
+ },
+ };
+ }
+
+ return fn(args);
+ };
+};
diff --git a/packages/lib/server-only/comment/find-comments.ts b/packages/lib/server-only/comment/find-comments.ts
new file mode 100644
index 000000000..2d0aa6ea3
--- /dev/null
+++ b/packages/lib/server-only/comment/find-comments.ts
@@ -0,0 +1,25 @@
+import { prisma } from '@documenso/prisma';
+
+export const findComments = async () => {
+ return await prisma.documentComment.findMany({
+ where: {
+ parentId: null,
+ },
+ include: {
+ User: {
+ select: {
+ name: true,
+ },
+ },
+ replies: {
+ include: {
+ User: {
+ select: {
+ name: true,
+ },
+ },
+ },
+ },
+ },
+ });
+};
diff --git a/packages/prisma/migrations/20240109110727_document_comments/migration.sql b/packages/prisma/migrations/20240109110727_document_comments/migration.sql
new file mode 100644
index 000000000..c182ffb46
--- /dev/null
+++ b/packages/prisma/migrations/20240109110727_document_comments/migration.sql
@@ -0,0 +1,17 @@
+-- CreateTable
+CREATE TABLE "DocumentComment" (
+ "id" SERIAL NOT NULL,
+ "documentId" INTEGER NOT NULL,
+ "userId" INTEGER NOT NULL,
+ "comment" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT "DocumentComment_pkey" PRIMARY KEY ("id")
+);
+
+-- AddForeignKey
+ALTER TABLE "DocumentComment" ADD CONSTRAINT "DocumentComment_documentId_fkey" FOREIGN KEY ("documentId") REFERENCES "Document"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "DocumentComment" ADD CONSTRAINT "DocumentComment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/packages/prisma/migrations/20240110080456_add_comment_replies/migration.sql b/packages/prisma/migrations/20240110080456_add_comment_replies/migration.sql
new file mode 100644
index 000000000..670fe4c76
--- /dev/null
+++ b/packages/prisma/migrations/20240110080456_add_comment_replies/migration.sql
@@ -0,0 +1,5 @@
+-- AlterTable
+ALTER TABLE "DocumentComment" ADD COLUMN "parentId" INTEGER;
+
+-- AddForeignKey
+ALTER TABLE "DocumentComment" ADD CONSTRAINT "DocumentComment_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "DocumentComment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma
index f0bfc6fda..7a85c4885 100644
--- a/packages/prisma/schema.prisma
+++ b/packages/prisma/schema.prisma
@@ -41,7 +41,8 @@ model User {
twoFactorEnabled Boolean @default(false)
twoFactorBackupCodes String?
VerificationToken VerificationToken[]
- Template Template[]
+ Template Template[]
+ DocumentComment DocumentComment[]
@@index([email])
}
@@ -121,27 +122,43 @@ enum DocumentStatus {
}
model Document {
- id Int @id @default(autoincrement())
- userId Int
- User User @relation(fields: [userId], references: [id], onDelete: Cascade)
- title String
- status DocumentStatus @default(DRAFT)
- Recipient Recipient[]
- Field Field[]
- ShareLink DocumentShareLink[]
- documentDataId String
- documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade)
- documentMeta DocumentMeta?
- createdAt DateTime @default(now())
- updatedAt DateTime @default(now()) @updatedAt
- completedAt DateTime?
- deletedAt DateTime?
+ id Int @id @default(autoincrement())
+ userId Int
+ User User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ title String
+ status DocumentStatus @default(DRAFT)
+ Recipient Recipient[]
+ Field Field[]
+ ShareLink DocumentShareLink[]
+ documentDataId String
+ documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade)
+ documentMeta DocumentMeta?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @default(now()) @updatedAt
+ completedAt DateTime?
+ deletedAt DateTime?
+ DocumentComments DocumentComment[]
@@unique([documentDataId])
@@index([userId])
@@index([status])
}
+model DocumentComment {
+ id Int @id @default(autoincrement())
+ documentId Int
+ userId Int
+ comment String
+ createdAt DateTime @default(now())
+ updatedAt DateTime @default(now()) @updatedAt
+ parentId Int?
+ parent DocumentComment? @relation("CommentReplies", fields: [parentId], references: [id], onDelete: Cascade)
+ replies DocumentComment[] @relation("CommentReplies")
+
+ Document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
+ User User @relation(fields: [userId], references: [id], onDelete: Cascade)
+}
+
enum DocumentDataType {
S3_PATH
BYTES
@@ -161,8 +178,8 @@ model DocumentMeta {
id String @id @default(cuid())
subject String?
message String?
- timezone String? @db.Text @default("Etc/UTC")
- dateFormat String? @db.Text @default("yyyy-MM-dd hh:mm a")
+ timezone String? @default("Etc/UTC") @db.Text
+ dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text
documentId Int @unique
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
}
@@ -183,19 +200,19 @@ enum SigningStatus {
}
model Recipient {
- id Int @id @default(autoincrement())
+ id Int @id @default(autoincrement())
documentId Int?
templateId Int?
- email String @db.VarChar(255)
- name String @default("") @db.VarChar(255)
+ email String @db.VarChar(255)
+ name String @default("") @db.VarChar(255)
token String
expired DateTime?
signedAt DateTime?
readStatus ReadStatus @default(NOT_OPENED)
signingStatus SigningStatus @default(NOT_SIGNED)
sendStatus SendStatus @default(NOT_SENT)
- Document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade)
- Template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade)
+ Document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade)
+ Template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade)
Field Field[]
Signature Signature[]
@@ -279,10 +296,10 @@ model Template {
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
- templateDocumentData DocumentData @relation(fields: [templateDocumentDataId], references: [id], onDelete: Cascade)
- User User @relation(fields: [userId], references: [id], onDelete: Cascade)
- Recipient Recipient[]
- Field Field[]
+ templateDocumentData DocumentData @relation(fields: [templateDocumentDataId], references: [id], onDelete: Cascade)
+ User User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ Recipient Recipient[]
+ Field Field[]
@@unique([templateDocumentDataId])
}
diff --git a/packages/trpc/server/comment-router/router.ts b/packages/trpc/server/comment-router/router.ts
new file mode 100644
index 000000000..54aaa7825
--- /dev/null
+++ b/packages/trpc/server/comment-router/router.ts
@@ -0,0 +1,9 @@
+import { findComments } from '@documenso/lib/server-only/comment/find-comments';
+
+import { procedure, router } from '../trpc';
+
+export const commentRouter = router({
+ getComments: procedure.query(async () => {
+ return await findComments();
+ }),
+});
diff --git a/packages/trpc/server/comment-router/schema.ts b/packages/trpc/server/comment-router/schema.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/trpc/server/router.ts b/packages/trpc/server/router.ts
index 77d18e06d..e8531f8f6 100644
--- a/packages/trpc/server/router.ts
+++ b/packages/trpc/server/router.ts
@@ -1,5 +1,6 @@
import { adminRouter } from './admin-router/router';
import { authRouter } from './auth-router/router';
+import { commentRouter } from './comment-router/router';
import { documentRouter } from './document-router/router';
import { fieldRouter } from './field-router/router';
import { profileRouter } from './profile-router/router';
@@ -21,6 +22,7 @@ export const appRouter = router({
singleplayer: singleplayerRouter,
twoFactorAuthentication: twoFactorAuthenticationRouter,
template: templateRouter,
+ comment: commentRouter,
});
export type AppRouter = typeof appRouter;