Compare commits

...

1 Commits

Author SHA1 Message Date
13d23b6111 feat: doc comments 2024-01-11 11:53:52 +02:00
13 changed files with 262 additions and 79 deletions

View File

@ -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,6 +181,7 @@ export const EditDocumentForm = ({
const currentDocumentFlow = documentFlow[step];
return (
<div>
<div className={cn('grid w-full grid-cols-12 gap-8', className)}>
<Card
className="relative col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7"
@ -234,5 +237,14 @@ export const EditDocumentForm = ({
</DocumentFlowFormContainer>
</div>
</div>
<Card className="my-8" gradient={true} degrees={200}>
<CardContent className="mt-8 flex flex-col">
<h2 className="text-foreground text-2xl font-semibold">Comments</h2>
<hr className="border-border mb-4 mt-4" />
<Comments />
<hr className="border-border -mt-4 mb-4" />
</CardContent>
</Card>
</div>
);
};

View File

@ -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 (
<div className={cn('mb-8', className)} key={comment.id}>
<p className="font-semibold">{comment.User.name}</p>
<p className="text-sm">
<LocaleDate
date={comment.createdAt}
format={{
month: 'long',
day: 'numeric',
year: 'numeric',
}}
/>
</p>
<p className="mb-2 mt-2 text-base">{comment.comment}</p>
<Button>Reply</Button>
</div>
);
};

View File

@ -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 (
<div>
{comments?.map((comment) => (
<div key={comment.id}>
<CommentCard comment={comment} className="mb-8" />
{comment.replies && comment.replies.length > 0 ? (
<div>
{comment.replies.map((reply) => (
<div className="ml-6 flex" key={reply.id}>
<CornerDownRight className="flex shrink-0" />
<CommentCard comment={reply} className="ml-6" />
</div>
))}
</div>
) : null}
</div>
))}
</div>
);
};

View File

@ -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 ?? '')}
/>
</FormControl>

View File

@ -45,6 +45,7 @@
"name": "@documenso/root",
"workspaces": [
"apps/*",
"packages/*"
],
"dependencies": {},

View File

@ -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 = <T extends Headers>(fn: (args: T) => Promise<any>) => {
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);
};
};

View File

@ -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,
},
},
},
},
},
});
};

View File

@ -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;

View File

@ -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;

View File

@ -42,6 +42,7 @@ model User {
twoFactorBackupCodes String?
VerificationToken VerificationToken[]
Template Template[]
DocumentComment DocumentComment[]
@@index([email])
}
@ -136,12 +137,28 @@ model Document {
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)
}

View File

@ -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();
}),
});

View File

@ -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;