Skip to content

Commit b6c964b

Browse files
chore: added user hover tooltip for preview page
1 parent 8e80d26 commit b6c964b

File tree

30 files changed

+1356
-71
lines changed

30 files changed

+1356
-71
lines changed

actions/chat/get-chat-conversations.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const getChatConversations = async ({ sharedConversationId }: GetChatConv
5252
isCreated: Boolean(conversation.shared),
5353
isOnlyAuth: conversation.shared?.isOnlyAuth ?? false,
5454
isShared: conversation.shared?.isActive ?? false,
55+
pictureUrl: conversation.user?.pictureUrl,
5556
username: conversation.user?.name,
5657
},
5758
},
@@ -95,11 +96,12 @@ export const getChatConversations = async ({ sharedConversationId }: GetChatConv
9596
position: newChatConversation.position,
9697
title: newChatConversation.title,
9798
shared: {
98-
id: null,
9999
expiredAt: null,
100+
id: null,
100101
isCreated: false,
101102
isOnlyAuth: false,
102103
isShared: false,
104+
pictureUrl: null,
103105
username: '',
104106
},
105107
},
@@ -114,6 +116,7 @@ export const getChatConversations = async ({ sharedConversationId }: GetChatConv
114116
isCreated: Boolean(conversation.shared),
115117
isOnlyAuth: conversation.shared?.isOnlyAuth ?? false,
116118
isShared: conversation.shared?.isActive ?? false,
119+
pictureUrl: conversation.user?.pictureUrl,
117120
username: conversation.user?.name,
118121
},
119122
}));

actions/csm/create-csm-issue.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'use server';
2+
3+
import { getTranslations } from 'next-intl/server';
4+
5+
import { db } from '@/lib/db';
6+
import { createWebSocketNotification } from '@/lib/notifications';
7+
8+
type CreateCsmIssue = {
9+
categoryId: string;
10+
description: string;
11+
email?: string | null;
12+
files?: { url: string; name: string }[];
13+
userId?: string;
14+
};
15+
16+
export const createCsmIssue = async ({
17+
categoryId,
18+
description,
19+
email = '',
20+
files = [],
21+
userId,
22+
}: CreateCsmIssue) => {
23+
const t = await getTranslations('csm-modal.notifications');
24+
25+
const lastIssueInDb = await db.csmIssue.findMany({ orderBy: { createdAt: 'desc' }, take: 1 });
26+
const issueNumber = `CSM-${lastIssueInDb.length ? Number(lastIssueInDb[0].name.split('-')[1]) + 1 : 1}`;
27+
28+
const issue = await db.csmIssue.create({
29+
data: {
30+
categoryId,
31+
description,
32+
email,
33+
name: issueNumber,
34+
userId,
35+
},
36+
});
37+
38+
await db.csmAttachment.createMany({
39+
data: files.map((file: { url: string; name: string }) => ({
40+
csmIssueId: issue.id,
41+
name: file.name,
42+
url: file.url,
43+
})),
44+
});
45+
46+
if (userId) {
47+
await createWebSocketNotification({
48+
channel: `notification_channel_${userId}`,
49+
data: {
50+
body: t('success'),
51+
title: `${issue.name}`.toUpperCase(),
52+
userId,
53+
},
54+
event: `private_event_${userId}`,
55+
});
56+
}
57+
58+
const ownerId = process.env.NEXT_PUBLIC_OWNER_ID as string;
59+
60+
await createWebSocketNotification({
61+
channel: `notification_channel_${ownerId}`,
62+
data: {
63+
body: t('ownerSuccess', { user: email }),
64+
title: `${issue.name}`.toUpperCase(),
65+
userId: ownerId,
66+
},
67+
event: `private_event_${ownerId}`,
68+
});
69+
70+
return { issueNumber: issue.name };
71+
};

actions/docs/get-app-docs.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
'use server';
22

33
import { promises as fs } from 'fs';
4+
import { getLocale } from 'next-intl/server';
45

56
import { getGithubContents } from '../github/get-contents';
67

78
export const getAppDocs = async (document: string) => {
9+
const locale = await getLocale();
10+
811
try {
912
const content =
1013
process.env.NODE_ENV === 'development'
11-
? await fs.readFile(`${process.cwd()}/docs/${document}.md`, 'utf8')
12-
: await getGithubContents({ path: `docs/${document}.md` });
14+
? await fs.readFile(`${process.cwd()}/docs/${locale}/${document}.md`, 'utf8')
15+
: await getGithubContents({ path: `docs/${locale}/${document}.md` });
1316

1417
return content;
1518
} catch (error) {

actions/github/get-releases.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export const getGithubReleases = async ({
4545
},
4646
TEN_MINUTE_SEC,
4747
);
48+
4849
return githubReleases;
4950
} catch (error) {
5051
console.error('[GET_GITHUB_RELEASES]', error);

app/(chat)/(routes)/chat/[[...slug]]/_components/chat-main/chat-body.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,14 @@ type ChatBodyProps = {
5959
introMessages: string[];
6060
isShared?: boolean;
6161
isSubmitting?: boolean;
62-
sharedName?: string | null;
6362
onSubmit: (
6463
event: SyntheticEvent,
6564
options?: {
6665
userMessage?: string;
6766
},
6867
) => void;
68+
sharedName?: string | null;
69+
sharedPicture?: string | null;
6970
};
7071

7172
const ChatBodyComponent = ({
@@ -74,8 +75,9 @@ const ChatBodyComponent = ({
7475
introMessages,
7576
isShared,
7677
isSubmitting,
77-
sharedName,
7878
onSubmit,
79+
sharedName,
80+
sharedPicture,
7981
}: ChatBodyProps) => {
8082
const t = useTranslations('chat.body');
8183

@@ -114,14 +116,17 @@ const ChatBodyComponent = ({
114116
{messages.map((message) => {
115117
const isAssistant = message.role === ChatCompletionRole.ASSISTANT;
116118

117-
const name = (() => {
119+
const [name, picture] = (() => {
118120
if (isShared && !isAssistant) {
119-
return sharedName ?? 'Current User';
121+
return [sharedName ?? 'Current User', sharedPicture];
122+
}
123+
124+
if (isAssistant) {
125+
return ['Nova Copilot', null];
120126
}
121127

122-
return isAssistant ? 'Nova Copilot' : user?.name || 'Current User';
128+
return [user?.name || 'Current User', user?.image];
123129
})();
124-
const picture = isAssistant ? null : user?.image;
125130

126131
return (
127132
<div

app/(chat)/(routes)/chat/[[...slug]]/_components/chat-main/chat-bubble.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const ChatBubble = ({
5252
<div className="pb-4 pt-2">
5353
<div className="flex gap-x-4">
5454
<Avatar className="border dark:border-muted-foreground">
55-
{!isAssistant && !isShared && <AvatarImage src={picture ?? ''} />}
55+
{!isAssistant && <AvatarImage src={picture ?? ''} />}
5656
{isAssistant && (
5757
<AvatarImage className="bg-white p-2 rounded-full" src="/assets/copilot.svg" />
5858
)}

app/(chat)/(routes)/chat/[[...slug]]/_components/chat-main/chat.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ export const Chat = ({ conversations = [], initialData, isEmbed, isShared }: Cha
307307
isSubmitting={isSubmitting}
308308
onSubmit={handleSubmit}
309309
sharedName={conversations?.[0]?.shared?.username}
310+
sharedPicture={conversations?.[0]?.shared?.pictureUrl}
310311
/>
311312
{!isShared && (
312313
<ChatInput

app/(dashboard)/(routes)/preview-course/[courseId]/_components/preview-description.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@ import { GenerateTextResponseAi } from '@/components/ai/generate-text-response-a
1010
import { IconBadge } from '@/components/common/icon-badge';
1111
import { Price } from '@/components/common/price';
1212
import { TextBadge } from '@/components/common/text-badge';
13+
import { UserHoverCard } from '@/components/common/user-hover-card';
14+
import { Button } from '@/components/ui';
1315
import { ChatCompletionRole, USER_TRANSLATE_PROMPT } from '@/constants/ai';
1416
import { TIMESTAMP_PREVIEW_TEMPLATE } from '@/constants/common';
1517
import { useCurrentUser } from '@/hooks/use-current-user';
1618

1719
type PreviewDescriptionProps = {
1820
author?: string | null;
21+
authorUserId?: string | null;
1922
categories: string[];
2023
chaptersLength: number;
2124
customRates: string | null;
@@ -32,6 +35,7 @@ type PreviewDescriptionProps = {
3235

3336
export const PreviewDescription = ({
3437
author,
38+
authorUserId,
3539
categories,
3640
chaptersLength,
3741
customRates,
@@ -89,11 +93,16 @@ export const PreviewDescription = ({
8993
</div>
9094
)}
9195
<div className="mt-4 gap-y-1">
92-
{author && (
93-
<div className="flex items-center gap-x-1 text-neutral-500 mb-1">
94-
<BookA className="h-4 w-4" />
95-
<span className="text-xs">{t('author', { author })}</span>
96-
</div>
96+
{author && authorUserId && (
97+
<UserHoverCard userId={authorUserId}>
98+
<Button
99+
className="flex items-center gap-x-1 text-neutral-500 p-0 font-normal"
100+
variant="link"
101+
>
102+
<BookA className="h-4 w-4" />
103+
<span className="text-xs">{t('author', { author })}</span>
104+
</Button>
105+
</UserHoverCard>
97106
)}
98107
<div className="flex items-center gap-x-1 text-neutral-500 mb-1">
99108
<CalendarDays className="h-4 w-4" />

app/(dashboard)/(routes)/preview-course/[courseId]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ const PreviewCourseIdPage = async (props: PreviewCourseIdPageProps) => {
8383
)}
8484
<PreviewDescription
8585
author={course?.user?.name}
86+
authorUserId={course?.userId}
8687
categories={[course.category!.name]}
8788
chaptersLength={course._count.chapters}
8889
customRates={course.customRates}

app/api/csm/create/route.ts

Lines changed: 8 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,24 @@
11
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
22
import { NextRequest, NextResponse } from 'next/server';
3-
import { getTranslations } from 'next-intl/server';
43

54
import { getCurrentUser } from '@/actions/auth/get-current-user';
6-
import { db } from '@/lib/db';
7-
import { createWebSocketNotification } from '@/lib/notifications';
5+
import { createCsmIssue } from '@/actions/csm/create-csm-issue';
86

97
export const POST = async (req: NextRequest) => {
108
try {
11-
const t = await getTranslations('csm-modal.notifications');
12-
139
const user = await getCurrentUser();
1410

1511
const { email, categoryId, description, files } = await req.json();
1612

17-
const lastIssueInDb = await db.csmIssue.findMany({ orderBy: { createdAt: 'desc' }, take: 1 });
18-
const issueNumber = `CSM-${lastIssueInDb.length ? Number(lastIssueInDb[0].name.split('-')[1]) + 1 : 1}`;
19-
20-
const issue = await db.csmIssue.create({
21-
data: {
22-
categoryId,
23-
description,
24-
email,
25-
name: issueNumber,
26-
userId: user?.userId,
27-
},
28-
});
29-
30-
await db.csmAttachment.createMany({
31-
data: files.map((file: { url: string; name: string }) => ({
32-
csmIssueId: issue.id,
33-
name: file.name,
34-
url: file.url,
35-
})),
36-
});
37-
38-
if (user) {
39-
const userId = user.userId;
40-
41-
await createWebSocketNotification({
42-
channel: `notification_channel_${userId}`,
43-
data: {
44-
body: t('success'),
45-
title: `${issue.name}`.toUpperCase(),
46-
userId,
47-
},
48-
event: `private_event_${userId}`,
49-
});
50-
}
51-
52-
const ownerId = process.env.NEXT_PUBLIC_OWNER_ID as string;
53-
54-
await createWebSocketNotification({
55-
channel: `notification_channel_${ownerId}`,
56-
data: {
57-
body: t('ownerSuccess', { user: user?.name || email }),
58-
title: `${issue.name}`.toUpperCase(),
59-
userId: ownerId,
60-
},
61-
event: `private_event_${ownerId}`,
13+
const csmIssue = await createCsmIssue({
14+
categoryId,
15+
description,
16+
email,
17+
files,
18+
userId: user?.userId,
6219
});
6320

64-
return NextResponse.json({ issueNumber: issue.name });
21+
return NextResponse.json({ issueNumber: csmIssue.issueNumber });
6522
} catch (error) {
6623
console.error('[CSM_CREATE]', error);
6724

0 commit comments

Comments
 (0)