Skip to content

Commit 0065882

Browse files
committed
lunted
1 parent c33384a commit 0065882

File tree

4 files changed

+119
-98
lines changed

4 files changed

+119
-98
lines changed

apps/api/src/query/utils.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ export function deduplicateGeoRows(
117117
let totalVisitors = 0;
118118
for (const row of rows) {
119119
const code = row.country_code || row.name;
120-
if (!code) continue;
120+
if (!code) {
121+
continue;
122+
}
121123
if (map.has(code)) {
122124
const existing = map.get(code);
123125
if (existing) {
@@ -150,7 +152,9 @@ function applyReferrerParsing(
150152
): Record<string, any>[] {
151153
return data.map((row) => {
152154
const referrerUrl = row.name || row.referrer;
153-
if (!referrerUrl) return row;
155+
if (!referrerUrl) {
156+
return row;
157+
}
154158

155159
const parsed = parseReferrer(referrerUrl, websiteDomain);
156160

apps/dashboard/app/(main)/websites/[id]/assistant/components/message-bubble.tsx

Lines changed: 75 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
'use client';
22

33
import {
4-
Brain,
5-
CaretDown,
6-
ChartBar,
7-
ChartLine,
8-
ChartPie,
9-
Clock,
10-
Hash,
11-
Robot,
12-
User,
4+
BrainIcon,
5+
CaretDownIcon,
6+
ChartBarIcon,
7+
ChartLineIcon,
8+
ChartPieIcon,
9+
ClockIcon,
10+
HashIcon,
11+
RobotIcon,
12+
UserIcon,
1313
} from '@phosphor-icons/react';
14-
import React, { useEffect, useState } from 'react';
14+
import { useEffect, useState } from 'react';
1515
import {
1616
Accordion,
1717
AccordionContent,
@@ -28,13 +28,13 @@ interface MessageBubbleProps {
2828
const getChartIcon = (chartType: string) => {
2929
switch (chartType) {
3030
case 'bar':
31-
return <ChartBar className="h-4 w-4" />;
31+
return <ChartBarIcon className="h-4 w-4" />;
3232
case 'line':
33-
return <ChartLine className="h-4 w-4" />;
33+
return <ChartLineIcon className="h-4 w-4" />;
3434
case 'pie':
35-
return <ChartPie className="h-4 w-4" />;
35+
return <ChartPieIcon className="h-4 w-4" />;
3636
default:
37-
return <ChartBar className="h-4 w-4" />;
37+
return <ChartBarIcon className="h-4 w-4" />;
3838
}
3939
};
4040

@@ -44,7 +44,9 @@ function ThinkingStepsPreview({ steps }: { steps: string[] }) {
4444
const maxPreviewSteps = 3;
4545

4646
useEffect(() => {
47-
if (steps.length === 0) return;
47+
if (steps.length === 0) {
48+
return;
49+
}
4850

4951
// Show the latest steps in the preview (sliding window)
5052
const latestSteps = steps.slice(-maxPreviewSteps);
@@ -59,7 +61,9 @@ function ThinkingStepsPreview({ steps }: { steps: string[] }) {
5961
}
6062
}, [steps]);
6163

62-
if (visibleSteps.length === 0) return null;
64+
if (visibleSteps.length === 0) {
65+
return null;
66+
}
6367

6468
return (
6569
<div className="mt-2 max-h-20 space-y-1 overflow-hidden">
@@ -76,14 +80,14 @@ function ThinkingStepsPreview({ steps }: { steps: string[] }) {
7680
)}
7781
key={`preview-${index}-${step.slice(0, 20)}`}
7882
>
79-
<Clock className="mt-0.5 h-3 w-3 flex-shrink-0" />
83+
<ClockIcon className="mt-0.5 h-3 w-3 flex-shrink-0" />
8084
<span className="break-words leading-relaxed">{step}</span>
8185
</div>
8286
);
8387
})}
8488
{steps.length > maxPreviewSteps && (
8589
<div className="flex items-center gap-2 py-1 pl-1 text-muted-foreground text-xs opacity-60">
86-
<CaretDown className="h-3 w-3" />
90+
<CaretDownIcon className="h-3 w-3" />
8791
<span>+{steps.length - maxPreviewSteps} more steps...</span>
8892
</div>
8993
)}
@@ -92,14 +96,16 @@ function ThinkingStepsPreview({ steps }: { steps: string[] }) {
9296
}
9397

9498
function ThinkingStepsAccordion({ steps }: { steps: string[] }) {
95-
if (steps.length === 0) return null;
99+
if (steps.length === 0) {
100+
return null;
101+
}
96102

97103
return (
98104
<Accordion className="w-full" collapsible type="single">
99105
<AccordionItem className="border-border/30" value="thinking-steps">
100106
<AccordionTrigger className="py-2 text-xs hover:no-underline">
101107
<div className="flex items-center gap-2">
102-
<Brain className="h-3 w-3" />
108+
<BrainIcon className="h-3 w-3" />
103109
<span>Thinking Process ({steps.length} steps)</span>
104110
</div>
105111
</AccordionTrigger>
@@ -125,37 +131,44 @@ function ThinkingStepsAccordion({ steps }: { steps: string[] }) {
125131
);
126132
}
127133

128-
export function MessageBubble({ message }: MessageBubbleProps) {
129-
const isUser = message.type === 'user';
130-
const isInProgress = message.type === 'assistant' && !message.content;
134+
function InProgressMessage({ message }: { message: Message }) {
131135
const hasThinkingSteps =
132136
message.thinkingSteps && message.thinkingSteps.length > 0;
133137

134-
if (isInProgress) {
135-
return (
136-
<div className="flex w-full max-w-[85%] gap-3">
137-
<div className="mt-1 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full border bg-muted shadow-sm">
138-
<Robot className="h-4 w-4" />
139-
</div>
140-
<div className="min-w-0 flex-1 rounded-lg border bg-muted px-4 py-3 shadow-sm">
141-
<div className="flex items-center gap-2 text-sm">
142-
<div className="flex space-x-1">
143-
<div className="h-2 w-2 animate-bounce rounded-full bg-foreground [animation-delay:-0.3s]" />
144-
<div className="h-2 w-2 animate-bounce rounded-full bg-foreground [animation-delay:-0.15s]" />
145-
<div className="h-2 w-2 animate-bounce rounded-full bg-foreground" />
146-
</div>
147-
<span className="text-muted-foreground">Nova is analyzing...</span>
138+
return (
139+
<div className="flex w-full max-w-[85%] gap-3">
140+
<div className="mt-1 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full border bg-muted shadow-sm">
141+
<RobotIcon className="h-4 w-4" />
142+
</div>
143+
<div className="min-w-0 flex-1 rounded-lg border bg-muted px-4 py-3 shadow-sm">
144+
<div className="flex items-center gap-2 text-sm">
145+
<div className="flex space-x-1">
146+
<div className="h-2 w-2 animate-bounce rounded-full bg-foreground [animation-delay:-0.3s]" />
147+
<div className="h-2 w-2 animate-bounce rounded-full bg-foreground [animation-delay:-0.15s]" />
148+
<div className="h-2 w-2 animate-bounce rounded-full bg-foreground" />
148149
</div>
149-
150-
{hasThinkingSteps && (
151-
<div className="mt-3 border-border/30 border-t pt-3">
152-
<ThinkingStepsPreview steps={message.thinkingSteps || []} />
153-
</div>
154-
)}
150+
<span className="text-muted-foreground">Nova is analyzing...</span>
155151
</div>
152+
153+
{hasThinkingSteps && (
154+
<div className="mt-3 border-border/30 border-t pt-3">
155+
<ThinkingStepsPreview steps={message.thinkingSteps || []} />
156+
</div>
157+
)}
156158
</div>
157-
);
158-
}
159+
</div>
160+
);
161+
}
162+
163+
function CompletedMessage({
164+
message,
165+
isUser,
166+
}: {
167+
message: Message;
168+
isUser: boolean;
169+
}) {
170+
const hasThinkingSteps =
171+
message.thinkingSteps && message.thinkingSteps.length > 0;
159172

160173
return (
161174
<div
@@ -164,7 +177,6 @@ export function MessageBubble({ message }: MessageBubbleProps) {
164177
isUser ? 'justify-end' : 'justify-start'
165178
)}
166179
>
167-
{/* Avatar */}
168180
<div
169181
className={cn(
170182
'mt-1 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full shadow-sm',
@@ -173,10 +185,13 @@ export function MessageBubble({ message }: MessageBubbleProps) {
173185
: 'order-1 border bg-muted'
174186
)}
175187
>
176-
{isUser ? <User className="h-4 w-4" /> : <Robot className="h-4 w-4" />}
188+
{isUser ? (
189+
<UserIcon className="h-4 w-4" />
190+
) : (
191+
<RobotIcon className="h-4 w-4" />
192+
)}
177193
</div>
178194

179-
{/* Message Content */}
180195
<div
181196
className={cn(
182197
'relative min-w-0 max-w-[85%] rounded-lg px-4 py-3 shadow-sm',
@@ -185,26 +200,23 @@ export function MessageBubble({ message }: MessageBubbleProps) {
185200
: 'order-2 border bg-muted'
186201
)}
187202
>
188-
{/* Main message text */}
189203
<div className="overflow-wrap-anywhere whitespace-pre-wrap break-words text-sm leading-relaxed">
190204
{message.content}
191205
</div>
192206

193-
{/* Thinking Steps Accordion (for completed messages) */}
194207
{hasThinkingSteps && !isUser && message.content && (
195208
<div className="mt-3">
196209
<ThinkingStepsAccordion steps={message.thinkingSteps || []} />
197210
</div>
198211
)}
199212

200-
{/* Metric Display */}
201213
{message.responseType === 'metric' &&
202214
message.metricValue !== undefined &&
203215
!isUser && (
204216
<div className="mt-4 rounded-lg border border-primary/20 bg-primary/5 p-4">
205217
<div className="flex min-w-0 items-center gap-3">
206218
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-primary/10">
207-
<Hash className="h-5 w-5 text-primary" />
219+
<HashIcon className="h-5 w-5 text-primary" />
208220
</div>
209221
<div className="min-w-0 flex-1">
210222
<div className="truncate font-medium text-muted-foreground text-xs uppercase tracking-wide">
@@ -220,7 +232,6 @@ export function MessageBubble({ message }: MessageBubbleProps) {
220232
</div>
221233
)}
222234

223-
{/* Visualization Indicator */}
224235
{message.hasVisualization && !isUser && (
225236
<div className="mt-3 border-border/30 border-t pt-3">
226237
<div className="flex items-center gap-2 text-muted-foreground text-xs">
@@ -230,7 +241,6 @@ export function MessageBubble({ message }: MessageBubbleProps) {
230241
</div>
231242
)}
232243

233-
{/* Timestamp (hover) */}
234244
<div className="-bottom-5 absolute right-0 opacity-0 transition-opacity group-hover:opacity-60">
235245
<div className="mt-1 font-mono text-xs">
236246
{message.timestamp.toLocaleTimeString([], {
@@ -243,3 +253,14 @@ export function MessageBubble({ message }: MessageBubbleProps) {
243253
</div>
244254
);
245255
}
256+
257+
export function MessageBubble({ message }: MessageBubbleProps) {
258+
const isUser = message.type === 'user';
259+
const isInProgress = message.type === 'assistant' && !message.content;
260+
261+
if (isInProgress) {
262+
return <InProgressMessage message={message} />;
263+
}
264+
265+
return <CompletedMessage isUser={isUser} message={message} />;
266+
}

apps/dashboard/app/(main)/websites/[id]/assistant/components/model-selector.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
'use client';
22

3-
import { CaretDown, Check, Flask, Star } from '@phosphor-icons/react';
3+
import {
4+
CaretDownIcon,
5+
CheckIcon,
6+
FlaskIcon,
7+
StarIcon,
8+
} from '@phosphor-icons/react';
49
import type React from 'react';
510
import { Button } from '@/components/ui/button';
611
import {
@@ -14,9 +19,11 @@ import type { AssistantModel } from '../types/model';
1419
import { MODEL_CONFIGS } from '../types/model';
1520

1621
const modelIcons: Record<string, React.ReactNode> = {
17-
chat: <Star className="h-4 w-4 text-yellow-400" weight="duotone" />, // Use Star for default
18-
agent: <Flask className="h-4 w-4 text-blue-400" weight="duotone" />, // Flask for experimental/agent
19-
'agent-max': <Flask className="h-4 w-4 text-purple-400" weight="duotone" />, // Flask for max
22+
chat: <StarIcon className="h-4 w-4 text-yellow-400" weight="duotone" />, // Use Star for default
23+
agent: <FlaskIcon className="h-4 w-4 text-blue-400" weight="duotone" />, // Flask for experimental/agent
24+
'agent-max': (
25+
<FlaskIcon className="h-4 w-4 text-purple-400" weight="duotone" />
26+
), // Flask for max
2027
};
2128

2229
interface ModelSelectorProps {
@@ -47,7 +54,7 @@ export function ModelSelector({
4754
>
4855
{modelIcons[selectedModel]}
4956
<span className="mr-1 ml-2">{currentConfig.name}</span>
50-
<CaretDown className="h-3 w-3 opacity-50" weight="duotone" />
57+
<CaretDownIcon className="h-3 w-3 opacity-50" weight="duotone" />
5158
</Button>
5259
</DropdownMenuTrigger>
5360
<DropdownMenuContent
@@ -77,7 +84,10 @@ export function ModelSelector({
7784
</div>
7885
</div>
7986
{selectedModel === config.id && (
80-
<Check className="ml-2 h-4 w-4 text-primary" weight="duotone" />
87+
<CheckIcon
88+
className="ml-2 h-4 w-4 text-primary"
89+
weight="duotone"
90+
/>
8191
)}
8292
</DropdownMenuItem>
8393
))}

0 commit comments

Comments
 (0)