Skip to content

Commit 37e1cf1

Browse files
authored
Merge pull request #615 from trycompai/main
[comp] Production Deploy
2 parents 06e8bc6 + aba9bc3 commit 37e1cf1

File tree

21 files changed

+335
-383
lines changed

21 files changed

+335
-383
lines changed

apps/app/src/actions/files/upload-file.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { auth } from "@/utils/auth";
88
import { headers } from "next/headers";
99
import { PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
1010
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
11+
import { revalidatePath } from "next/cache";
1112

1213
function mapFileTypeToAttachmentType(fileType: string): AttachmentType {
1314
const type = fileType.split("/")[0];
@@ -31,12 +32,20 @@ const uploadAttachmentSchema = z.object({
3132
fileData: z.string(),
3233
entityId: z.string(),
3334
entityType: z.nativeEnum(AttachmentEntityType),
35+
pathToRevalidate: z.string().optional(),
3436
});
3537

3638
export const uploadFile = async (
3739
input: z.infer<typeof uploadAttachmentSchema>,
3840
) => {
39-
const { fileName, fileType, fileData, entityId, entityType } = input;
41+
const {
42+
fileName,
43+
fileType,
44+
fileData,
45+
entityId,
46+
entityType,
47+
pathToRevalidate,
48+
} = input;
4049
const session = await auth.api.getSession({ headers: await headers() });
4150
const organizationId = session?.session.activeOrganizationId;
4251

@@ -74,6 +83,16 @@ export const uploadFile = async (
7483

7584
await s3Client.send(putCommand);
7685

86+
console.log("Creating attachment...");
87+
console.log({
88+
name: fileName,
89+
url: key,
90+
type: mapFileTypeToAttachmentType(fileType),
91+
entityId: entityId,
92+
entityType: entityType,
93+
organizationId: organizationId,
94+
});
95+
7796
const attachment = await db.attachment.create({
7897
data: {
7998
name: fileName,
@@ -94,6 +113,10 @@ export const uploadFile = async (
94113
expiresIn: 900,
95114
});
96115

116+
if (pathToRevalidate) {
117+
revalidatePath(pathToRevalidate);
118+
}
119+
97120
return {
98121
success: true,
99122
data: {

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tasks/actions/createComment.ts renamed to apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/actions/createComment.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import { z } from "zod";
1111
const createCommentSchema = z
1212
.object({
1313
content: z.string(),
14-
taskId: z.string(),
14+
entityId: z.string(),
15+
entityType: z.nativeEnum(CommentEntityType),
1516
attachmentIds: z.array(z.string()).optional(),
17+
pathToRevalidate: z.string().optional(),
1618
})
1719
.refine(
1820
(data) =>
@@ -28,7 +30,8 @@ const createCommentSchema = z
2830
export const createComment = async (
2931
input: z.infer<typeof createCommentSchema>,
3032
) => {
31-
const { content, taskId, attachmentIds } = input;
33+
const { content, entityId, entityType, attachmentIds, pathToRevalidate } =
34+
input;
3235
const session = await auth.api.getSession({
3336
headers: await headers(),
3437
});
@@ -42,11 +45,11 @@ export const createComment = async (
4245
};
4346
}
4447

45-
if (!taskId) {
46-
console.error("Task ID missing after validation in createComment");
48+
if (!entityId) {
49+
console.error("Entity ID missing after validation in createComment");
4750
return {
4851
success: false,
49-
error: "Internal error: Task ID missing.",
52+
error: "Internal error: Entity ID missing.",
5053
data: null,
5154
};
5255
}
@@ -72,24 +75,32 @@ export const createComment = async (
7275
// Wrap create and update in a transaction
7376
const result = await db.$transaction(async (tx) => {
7477
// 1. Create the comment within the transaction
78+
console.log("Creating comment:", {
79+
content,
80+
entityId,
81+
entityType,
82+
memberId: member.id,
83+
organizationId: orgId,
84+
});
7585
const comment = await tx.comment.create({
7686
data: {
7787
content: content ?? "",
78-
entityId: taskId, // Use validated const
79-
entityType: CommentEntityType.task,
88+
entityId,
89+
entityType,
8090
authorId: member.id,
8191
organizationId: orgId,
8292
},
8393
});
8494

8595
// 2. Link attachments if provided (using updateMany)
8696
if (attachmentIds && attachmentIds.length > 0) {
97+
console.log("Linking attachments to comment:", attachmentIds);
8798
await tx.attachment.updateMany({
8899
where: {
89100
id: { in: attachmentIds },
90101
organizationId: orgId,
91-
entityType: AttachmentEntityType.comment,
92-
entityId: taskId,
102+
entityId,
103+
entityType: entityType as AttachmentEntityType,
93104
},
94105
data: {
95106
entityId: comment.id,
@@ -99,10 +110,11 @@ export const createComment = async (
99110
}
100111

101112
return comment;
102-
}); // End of transaction
113+
});
103114

104-
// Revalidate outside the transaction
105-
revalidatePath(`/${orgId}/tasks/${taskId}`);
115+
if (pathToRevalidate) {
116+
revalidatePath(pathToRevalidate);
117+
}
106118

107119
return {
108120
success: true,

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tasks/actions/deleteComment.ts renamed to apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/actions/deleteComment.ts

File renamed without changes.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tasks/actions/deleteCommentAttachment.ts renamed to apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/actions/deleteCommentAttachment.ts

File renamed without changes.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tasks/actions/getCommentAttachmentUrl.ts renamed to apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/actions/getCommentAttachmentUrl.ts

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { GetObjectCommand } from "@aws-sdk/client-s3";
44
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
55
import { db } from "@comp/db";
6-
import { AttachmentEntityType, CommentEntityType } from "@comp/db/types";
6+
import { AttachmentEntityType } from "@comp/db/types";
77
import { z } from "zod";
88
import { s3Client, BUCKET_NAME, extractS3KeyFromUrl } from "@/app/s3";
99
import { auth } from "@/utils/auth";
@@ -35,14 +35,13 @@ export const getCommentAttachmentUrl = async (
3535
where: {
3636
id: attachmentId,
3737
organizationId: organizationId,
38-
// No include needed here anymore
3938
},
4039
});
4140

4241
if (!attachment) {
4342
return {
4443
success: false,
45-
error: "Attachment not found or access denied (1)",
44+
error: "Attachment not found or access denied",
4645
} as const;
4746
}
4847

@@ -64,10 +63,6 @@ export const getCommentAttachmentUrl = async (
6463
id: attachment.entityId, // Use entityId from attachment
6564
organizationId: organizationId, // Ensure comment is in the same org
6665
},
67-
select: {
68-
entityId: true, // Parent (Task) ID
69-
entityType: true, // Parent (Task) Type
70-
},
7166
});
7267

7368
if (!comment) {
@@ -82,39 +77,7 @@ export const getCommentAttachmentUrl = async (
8277
} as const;
8378
}
8479

85-
// 3. Verify parent entity is a task
86-
if (comment.entityType !== CommentEntityType.task) {
87-
console.error(
88-
"Comment's parent entity is not a task",
89-
attachmentId,
90-
comment.entityType,
91-
);
92-
return {
93-
success: false,
94-
error: "Invalid attachment parent link",
95-
} as const;
96-
}
97-
98-
// 4. Verify user has access to the parent Task
99-
const parentTask = await db.task.findFirst({
100-
where: {
101-
id: comment.entityId, // Use entityId from comment
102-
organizationId: organizationId,
103-
},
104-
select: { id: true },
105-
});
106-
if (!parentTask) {
107-
console.error(
108-
"Parent task not found or access denied",
109-
comment.entityId,
110-
);
111-
return {
112-
success: false,
113-
error: "Access denied to parent task",
114-
} as const;
115-
}
116-
117-
// 5. Extract S3 key
80+
// 3. Extract S3 key
11881
let key: string;
11982
try {
12083
key = extractS3KeyFromUrl(attachment.url);
@@ -130,7 +93,7 @@ export const getCommentAttachmentUrl = async (
13093
} as const;
13194
}
13295

133-
// 6. Generate Signed URL using shared client and bucket name
96+
// 4. Generate Signed URL using shared client and bucket name
13497
try {
13598
const command = new GetObjectCommand({
13699
Bucket: BUCKET_NAME!,

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tasks/actions/updateComment.ts renamed to apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/actions/updateComment.ts

File renamed without changes.

apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tasks/[taskId]/components/TaskCommentForm.tsx renamed to apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/components/comments/CommentForm.tsx

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
"use client";
22

3-
import { Button } from "@comp/ui/button";
4-
import { Textarea } from "@comp/ui/textarea";
5-
import { ArrowUp, Loader2, Paperclip } from "lucide-react";
63
import { uploadFile } from "@/actions/files/upload-file";
74
import { authClient } from "@/utils/auth-client";
8-
import { AttachmentEntityType } from "@comp/db/types";
5+
import { AttachmentEntityType, CommentEntityType } from "@comp/db/types";
6+
import { Button } from "@comp/ui/button";
97
import { Label } from "@comp/ui/label";
8+
import { Textarea } from "@comp/ui/textarea";
109
import clsx from "clsx";
10+
import { ArrowUp, Loader2, Paperclip } from "lucide-react";
1111
import React, { useCallback, useRef, useState } from "react";
1212
import { toast } from "sonner";
13+
import { useParams, useRouter } from "next/navigation";
1314
import { createComment } from "../../actions/createComment";
14-
import { AttachmentItem } from "./AttachmentItem";
15+
import { AttachmentItem } from "../../tasks/[taskId]/components/AttachmentItem";
1516

16-
interface TaskCommentFormProps {
17-
taskId: string;
17+
interface CommentFormProps {
18+
entityId: string;
19+
entityType: CommentEntityType;
1820
}
1921

2022
interface PendingAttachment {
@@ -24,31 +26,28 @@ interface PendingAttachment {
2426
signedUrl: string | null;
2527
}
2628

27-
function mapFileTypeToAttachmentType(fileType: string): string {
28-
const type = fileType.split("/")[0];
29-
switch (type) {
30-
case "image":
31-
return "image";
32-
case "video":
33-
return "video";
34-
case "audio":
35-
return "audio";
36-
case "application":
37-
if (fileType === "application/pdf") return "document"; // Specific PDF check
38-
return "document";
39-
default:
40-
return "other";
41-
}
42-
}
43-
44-
export function TaskCommentForm({ taskId }: TaskCommentFormProps) {
29+
export function CommentForm({ entityId, entityType }: CommentFormProps) {
4530
const session = authClient.useSession();
4631
const [newComment, setNewComment] = useState("");
4732
const [pendingAttachments, setPendingAttachments] = useState<
4833
PendingAttachment[]
4934
>([]);
5035
const [isUploading, setIsUploading] = useState(false);
5136
const fileInputRef = useRef<HTMLInputElement>(null);
37+
const { orgId } = useParams<{ orgId: string }>();
38+
39+
let pathToRevalidate = "";
40+
switch (entityType) {
41+
case "task":
42+
pathToRevalidate = `/${orgId}/tasks/${entityId}`;
43+
break;
44+
case "vendor":
45+
pathToRevalidate = `/${orgId}/vendors/${entityId}`;
46+
break;
47+
case "risk":
48+
pathToRevalidate = `/${orgId}/risks/${entityId}`;
49+
break;
50+
}
5251

5352
const triggerFileInput = () => {
5453
fileInputRef.current?.click();
@@ -93,8 +92,9 @@ export function TaskCommentForm({ taskId }: TaskCommentFormProps) {
9392
fileName: file.name,
9493
fileType: file.type,
9594
fileData: base64Data,
96-
entityId: taskId,
97-
entityType: AttachmentEntityType.comment,
95+
entityId,
96+
entityType: entityType as AttachmentEntityType,
97+
pathToRevalidate,
9898
});
9999
if (error) {
100100
console.error(
@@ -145,7 +145,7 @@ export function TaskCommentForm({ taskId }: TaskCommentFormProps) {
145145
if (fileInputRef.current) fileInputRef.current.value = "";
146146
})();
147147
},
148-
[taskId, pendingAttachments.length],
148+
[entityId, entityType, pendingAttachments.length],
149149
);
150150

151151
const handleRemovePendingAttachment = (attachmentIdToRemove: string) => {
@@ -194,8 +194,10 @@ export function TaskCommentForm({ taskId }: TaskCommentFormProps) {
194194

195195
const { success, data, error } = await createComment({
196196
content: newComment,
197-
taskId: taskId,
197+
entityId,
198+
entityType,
198199
attachmentIds: pendingAttachments.map((att) => att.id),
200+
pathToRevalidate,
199201
});
200202

201203
if (success && data) {

0 commit comments

Comments
 (0)