Skip to content

Commit 0bb3919

Browse files
committed
Improve control center query button
1 parent f741698 commit 0bb3919

6 files changed

Lines changed: 289 additions & 122 deletions

File tree

apps/backend/src/app/health/email/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const waitForVerificationEmail = async (testEmail: string, useInbucket: boolean)
120120
throw new StackAssertionError(`Couldn't find verification email in time limit`, { recipient_email: testEmail, max_poll_attempts: MAX_POLL_ATTEMPTS, poll_interval_ms: POLL_INTERVAL_MS });
121121
};
122122

123-
export const GET = createSmartRouteHandler({
123+
export const POST = createSmartRouteHandler({
124124
metadata: {
125125
hidden: true,
126126
summary: "Email Health Monitor",

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/analytics/tables/page-client.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Dialog,
66
DialogBody,
77
DialogContent,
8+
DialogFooter,
89
DialogHeader,
910
DialogTitle,
1011
} from "@/components/ui/dialog";
@@ -619,6 +620,7 @@ function TableContent({ tableId }: { tableId: TableId }) {
619620

620621
export default function PageClient() {
621622
const [selectedTable, setSelectedTable] = useState<TableId | null>("events");
623+
const [queryDialogOpen, setQueryDialogOpen] = useState(false);
622624

623625
return (
624626
<AppEnabledGuard appId="analytics">
@@ -647,7 +649,7 @@ export default function PageClient() {
647649
</div>
648650
<div className="py-4 px-4">
649651
<button
650-
onClick={() => window.dispatchEvent(new CustomEvent("spotlight-toggle"))}
652+
onClick={() => setQueryDialogOpen(true)}
651653
className="flex items-center gap-2 px-3 py-2 rounded-md text-sm text-muted-foreground hover:bg-muted hover:text-foreground transition-colors hover:transition-none w-full"
652654
>
653655
<SparkleIcon className="h-4 w-4" />
@@ -667,6 +669,23 @@ export default function PageClient() {
667669
)}
668670
</div>
669671
</div>
672+
673+
{/* Query moved dialog */}
674+
<Dialog open={queryDialogOpen} onOpenChange={setQueryDialogOpen}>
675+
<DialogContent>
676+
<DialogHeader>
677+
<DialogTitle>Analytics Queries have moved to the Control Center</DialogTitle>
678+
</DialogHeader>
679+
<DialogBody>
680+
<Typography variant="secondary">
681+
You can now do analytics queries directly from the Control Center. To open the Control Center, press <kbd className="px-1.5 py-0.5 rounded bg-muted font-mono text-xs"></kbd> + <kbd className="px-1.5 py-0.5 rounded bg-muted font-mono text-xs">K</kbd>
682+
</Typography>
683+
</DialogBody>
684+
<DialogFooter>
685+
<Button onClick={() => setQueryDialogOpen(false)}>OK</Button>
686+
</DialogFooter>
687+
</DialogContent>
688+
</Dialog>
670689
</PageLayout>
671690
</AppEnabledGuard>
672691
);

apps/dashboard/src/components/cmdk-search.tsx

Lines changed: 4 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -230,102 +230,6 @@ const CyclingPlaceholder = memo(function CyclingPlaceholder({
230230
);
231231
});
232232

233-
// Analytics query mode placeholder component
234-
const AnalyticsQueryPlaceholder = memo(function AnalyticsQueryPlaceholder({
235-
onSelectQuery,
236-
}: {
237-
onSelectQuery?: (query: string) => void,
238-
}) {
239-
const tables = [
240-
{ id: "events", name: "Events", description: "User events and actions", query: "SELECT * FROM events ORDER BY event_at DESC LIMIT 100" },
241-
];
242-
243-
const exampleQueries = [
244-
"Show me all events from the last hour",
245-
"Count events by type",
246-
"SELECT event_type, COUNT(*) FROM events GROUP BY event_type",
247-
];
248-
249-
return (
250-
<div className="h-full flex flex-col items-center select-none px-6">
251-
{/* Top spacer */}
252-
<div className="flex-1" />
253-
254-
<div className="relative w-fit max-w-md">
255-
{/* Header */}
256-
<div className="relative text-center mb-6">
257-
<h2 className="relative text-base font-semibold text-foreground mb-1 inline-block">
258-
Analytics Query
259-
</h2>
260-
<p className="text-[11px] text-muted-foreground/50">
261-
Query your data using English or ClickHouse SQL
262-
</p>
263-
</div>
264-
265-
{/* Tables section */}
266-
<div className="mb-6">
267-
<p className="text-[9px] text-muted-foreground/40 uppercase tracking-wider mb-3 text-center">Available Tables</p>
268-
<div className="space-y-2">
269-
{tables.map((table) => (
270-
<button
271-
key={table.id}
272-
type="button"
273-
onClick={() => onSelectQuery?.(table.query)}
274-
className="w-full flex items-center gap-3 rounded-lg px-4 py-3 transition-colors hover:transition-none hover:bg-foreground/[0.04] border border-foreground/[0.06]"
275-
>
276-
<div className="w-8 h-8 rounded-lg bg-amber-500/10 flex items-center justify-center">
277-
<PlayIcon className="h-4 w-4 text-amber-500" />
278-
</div>
279-
<div className="flex-1 min-w-0 text-left">
280-
<h3 className="text-[12px] font-medium text-foreground font-mono">
281-
{table.name}
282-
</h3>
283-
<p className="text-[10px] text-muted-foreground/50">
284-
{table.description}
285-
</p>
286-
</div>
287-
</button>
288-
))}
289-
</div>
290-
</div>
291-
292-
{/* Example queries */}
293-
<div>
294-
<p className="text-[9px] text-muted-foreground/40 uppercase tracking-wider mb-3 text-center">Try something like</p>
295-
<div className="space-y-1.5">
296-
{exampleQueries.map((q, index) => (
297-
<button
298-
key={index}
299-
type="button"
300-
onClick={() => onSelectQuery?.(q)}
301-
className="w-full text-left text-[11px] text-muted-foreground/60 hover:text-foreground px-3 py-2 rounded-md hover:bg-foreground/[0.04] transition-colors hover:transition-none font-mono truncate"
302-
>
303-
{q}
304-
</button>
305-
))}
306-
</div>
307-
</div>
308-
</div>
309-
310-
{/* Bottom spacer */}
311-
<div className="flex-1" />
312-
313-
{/* Footer */}
314-
<div className="w-full shrink-0 -mx-6 px-6">
315-
<div className="py-3 border-t border-foreground/[0.06] w-full flex items-center justify-center gap-5 text-[10px] text-muted-foreground/40">
316-
<div className="flex items-center gap-1.5">
317-
<kbd className="px-1.5 py-0.5 rounded bg-foreground/[0.06] font-mono"></kbd>
318-
<span>run query</span>
319-
</div>
320-
<div className="flex items-center gap-1.5">
321-
<kbd className="px-1.5 py-0.5 rounded bg-foreground/[0.06] font-mono">esc</kbd>
322-
<span>close</span>
323-
</div>
324-
</div>
325-
</div>
326-
</div>
327-
);
328-
});
329233

330234
// Reusable Results List Component
331235
export const CmdKResultsList = memo(function CmdKResultsList({
@@ -337,7 +241,6 @@ export const CmdKResultsList = memo(function CmdKResultsList({
337241
showCyclingPlaceholder = false,
338242
onSelectExampleQuery,
339243
isParentColumn = false,
340-
analyticsQueryMode = false,
341244
}: {
342245
commands: CmdKCommand[],
343246
selectedIndex: number,
@@ -351,8 +254,6 @@ export const CmdKResultsList = memo(function CmdKResultsList({
351254
onSelectExampleQuery?: (query: string) => void,
352255
/** When true, selection shows as outline only (for parent columns) */
353256
isParentColumn?: boolean,
354-
/** When true, show analytics query placeholder instead of cycling placeholder */
355-
analyticsQueryMode?: boolean,
356257
}) {
357258
const itemRefs = useRef<Map<number, HTMLButtonElement>>(new Map());
358259
const hasResults = commands.length > 0;
@@ -366,9 +267,6 @@ export const CmdKResultsList = memo(function CmdKResultsList({
366267
}, [selectedIndex]);
367268

368269
if (!hasResults) {
369-
if (analyticsQueryMode) {
370-
return <AnalyticsQueryPlaceholder onSelectQuery={onSelectExampleQuery} />;
371-
}
372270
if (showCyclingPlaceholder) {
373271
return <CyclingPlaceholder onSelectQuery={onSelectExampleQuery} />;
374272
}
@@ -505,8 +403,6 @@ export function CmdKSearch({
505403
const [activeDepth, setActiveDepth] = useState(0); // Which column is active (0 = main list)
506404
const [selectedIndices, setSelectedIndices] = useState<number[]>([0]); // Selected index in each column
507405
const [nestedBlurHandlers, setNestedBlurHandlers] = useState<(() => void)[]>([]); // onBlur handlers for each depth
508-
// Analytics query mode state
509-
const [analyticsQueryMode, setAnalyticsQueryMode] = useState(false);
510406
const router = useRouter();
511407
const pathname = usePathname();
512408
const inputRef = useRef<HTMLInputElement>(null);
@@ -528,23 +424,15 @@ export function CmdKSearch({
528424
setOpen((prev) => !prev);
529425
};
530426

531-
const handleAnalyticsQuery = () => {
532-
setAnalyticsQueryMode(true);
533-
setQuery("");
534-
setOpen(true);
535-
};
536-
537427
document.addEventListener("keydown", down);
538428
window.addEventListener("spotlight-toggle", handleToggle);
539-
window.addEventListener("spotlight-analytics-query", handleAnalyticsQuery);
540429
return () => {
541430
document.removeEventListener("keydown", down);
542431
window.removeEventListener("spotlight-toggle", handleToggle);
543-
window.removeEventListener("spotlight-analytics-query", handleAnalyticsQuery);
544432
};
545433
}, []);
546434

547-
// Focus and select input when opening, reset analytics mode when closing
435+
// Focus and select input when opening
548436
useEffect(() => {
549437
if (open) {
550438
setSelectedIndex(0);
@@ -553,16 +441,13 @@ export function CmdKSearch({
553441
inputRef.current?.focus();
554442
inputRef.current?.select();
555443
});
556-
} else {
557-
// Reset analytics mode when closing
558-
setAnalyticsQueryMode(false);
559444
}
560445
}, [open]);
561446

562447
// Get commands from the hook
563448
const commands = useCmdKCommands({ projectId, enabledApps, query, onEnableApp });
564449

565-
// Filter commands based on query
450+
// Filter and sort commands based on query
566451
const filteredCommands = useMemo(() => {
567452
if (!query.trim()) return [];
568453

@@ -872,7 +757,7 @@ export function CmdKSearch({
872757
value={query}
873758
onChange={(e) => setQuery(e.target.value)}
874759
onKeyDown={handleKeyDown}
875-
placeholder={analyticsQueryMode ? "Type your query... (English or ClickHouse SQL)" : "Search or ask AI..."}
760+
placeholder="Search or ask AI..."
876761
className="flex-1 bg-transparent text-base outline-none placeholder:text-muted-foreground/50"
877762
autoComplete="off"
878763
autoCorrect="off"
@@ -934,7 +819,6 @@ export function CmdKSearch({
934819
showCyclingPlaceholder={true}
935820
onSelectExampleQuery={setQuery}
936821
isParentColumn={activeDepth > 0}
937-
analyticsQueryMode={analyticsQueryMode}
938822
/>
939823
</div>
940824

@@ -1097,7 +981,7 @@ export function CmdKTrigger() {
1097981
"rounded-[12px]",
1098982
"ring-2 ring-inset ring-foreground/[0.06]",
1099983
"transition-all duration-300 hover:transition-none",
1100-
"hover:ring-blue-500/15 hover:shadow-[0_0_16px_rgba(59,130,246,0.08),inset_0_1px_0_rgba(255,255,255,0.03)]"
984+
"hover:ring-[#9196F4]/20 hover:shadow-[0_0_20px_rgba(145,150,244,0.12),inset_0_1px_0_rgba(255,255,255,0.03)]"
1101985
)}
1102986
>
1103987
<div

0 commit comments

Comments
 (0)