Skip to content

Commit 24da001

Browse files
authored
Merge pull request #324 from QueueLab/feat/move-search-button-to-header
Move Map Search Button to Header
2 parents bbf5e91 + c24fbda commit 24da001

File tree

11 files changed

+3033
-216
lines changed

11 files changed

+3033
-216
lines changed

app/layout.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { MapToggleProvider } from '@/components/map-toggle-context'
1313
import { ProfileToggleProvider } from '@/components/profile-toggle-context'
1414
import { MapLoadingProvider } from '@/components/map-loading-context';
1515
import ConditionalLottie from '@/components/conditional-lottie';
16+
import { MapProvider } from '@/components/map/map-context'
1617

1718
const fontSans = FontSans({
1819
subsets: ['latin'],
@@ -63,14 +64,16 @@ export default function RootLayout({
6364
disableTransitionOnChange
6465
themes={['light', 'dark', 'earth']}
6566
>
66-
<MapLoadingProvider>
67-
<Header />
68-
<ConditionalLottie />
69-
{children}
70-
<Sidebar />
71-
<Footer />
72-
<Toaster />
73-
</MapLoadingProvider>
67+
<MapProvider>
68+
<MapLoadingProvider>
69+
<Header />
70+
<ConditionalLottie />
71+
{children}
72+
<Sidebar />
73+
<Footer />
74+
<Toaster />
75+
</MapLoadingProvider>
76+
</MapProvider>
7477
</ThemeProvider>
7578
</ProfileToggleProvider>
7679
</MapToggleProvider>

bun.lock

Lines changed: 2867 additions & 0 deletions
Large diffs are not rendered by default.

bun.lockb

-587 KB
Binary file not shown.

components/analysis-tool.tsx

Lines changed: 0 additions & 77 deletions
This file was deleted.

components/chat.tsx

Lines changed: 43 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,9 @@ import MobileIconsBar from './mobile-icons-bar'
1111
import { useProfileToggle, ProfileToggleEnum } from "@/components/profile-toggle-context";
1212
import SettingsView from "@/components/settings/settings-view";
1313
import { MapDataProvider, useMapData } from './map/map-data-context'; // Add this and useMapData
14-
import { MapProvider } from './map/map-context'
1514
import { updateDrawingContext } from '@/lib/actions/chat'; // Import the server action
1615
import dynamic from 'next/dynamic'
17-
18-
const AnalysisTool = dynamic(() => import('./analysis-tool').then(mod => mod.AnalysisTool), {
19-
ssr: false,
20-
})
16+
import { HeaderSearchButton } from './header-search-button'
2117

2218
type ChatProps = {
2319
id?: string // This is the chatId
@@ -45,7 +41,7 @@ export function Chat({ id }: ChatProps) {
4541
useEffect(() => {
4642
// Check if device is mobile
4743
const checkMobile = () => {
48-
setIsMobile(window.innerWidth <= 1024)
44+
setIsMobile(window.innerWidth < 768)
4945
}
5046

5147
// Initial check
@@ -86,42 +82,18 @@ export function Chat({ id }: ChatProps) {
8682
if (isMobile) {
8783
return (
8884
<MapDataProvider> {/* Add Provider */}
89-
<MapProvider>
90-
<div className="mobile-layout-container">
91-
<div className="mobile-map-section">
92-
{activeView ? <SettingsView /> : <Mapbox />}
93-
</div>
94-
<div className="mobile-icons-bar">
95-
<MobileIconsBar onAttachmentClick={handleAttachment} />
96-
</div>
97-
<div className="mobile-chat-input-area">
98-
<ChatPanel ref={chatPanelRef} messages={messages} input={input} setInput={setInput} />
99-
</div>
100-
<div className="mobile-chat-messages-area">
101-
{showEmptyScreen ? (
102-
<EmptyScreen
103-
submitMessage={message => {
104-
setInput(message)
105-
}}
106-
/>
107-
) : (
108-
<ChatMessages messages={messages} />
109-
)}
110-
</div>
111-
</div>
112-
</MapProvider>
113-
</MapDataProvider>
114-
);
115-
}
116-
117-
// Desktop layout
118-
return (
119-
<MapDataProvider> {/* Add Provider */}
120-
<MapProvider>
121-
<div className="flex justify-start items-start">
122-
{/* This is the new div for scrolling */}
123-
<div className="w-1/2 flex flex-col space-y-3 md:space-y-4 px-8 sm:px-12 pt-12 md:pt-14 pb-4 h-[calc(100vh-0.5in)] overflow-y-auto">
124-
<ChatPanel messages={messages} input={input} setInput={setInput} />
85+
<HeaderSearchButton />
86+
<div className="mobile-layout-container">
87+
<div className="mobile-map-section">
88+
{activeView ? <SettingsView /> : <Mapbox />}
89+
</div>
90+
<div className="mobile-icons-bar">
91+
<MobileIconsBar onAttachmentClick={handleAttachment} />
92+
</div>
93+
<div className="mobile-chat-input-area">
94+
<ChatPanel ref={chatPanelRef} messages={messages} input={input} setInput={setInput} />
95+
</div>
96+
<div className="mobile-chat-messages-area">
12597
{showEmptyScreen ? (
12698
<EmptyScreen
12799
submitMessage={message => {
@@ -131,16 +103,37 @@ export function Chat({ id }: ChatProps) {
131103
) : (
132104
<ChatMessages messages={messages} />
133105
)}
134-
</div>
135-
<div
136-
className="w-1/2 p-4 fixed h-[calc(100vh-0.5in)] top-0 right-0 mt-[0.5in]"
137-
style={{ zIndex: 10 }} // Added z-index
138-
>
139-
{activeView ? <SettingsView /> : <Mapbox />}
140-
<AnalysisTool />
141106
</div>
142107
</div>
143-
</MapProvider>
108+
</MapDataProvider>
109+
);
110+
}
111+
112+
// Desktop layout
113+
return (
114+
<MapDataProvider> {/* Add Provider */}
115+
<HeaderSearchButton />
116+
<div className="flex justify-start items-start">
117+
{/* This is the new div for scrolling */}
118+
<div className="w-1/2 flex flex-col space-y-3 md:space-y-4 px-8 sm:px-12 pt-12 md:pt-14 pb-4 h-[calc(100vh-0.5in)] overflow-y-auto">
119+
<ChatPanel messages={messages} input={input} setInput={setInput} />
120+
{showEmptyScreen ? (
121+
<EmptyScreen
122+
submitMessage={message => {
123+
setInput(message)
124+
}}
125+
/>
126+
) : (
127+
<ChatMessages messages={messages} />
128+
)}
129+
</div>
130+
<div
131+
className="w-1/2 p-4 fixed h-[calc(100vh-0.5in)] top-0 right-0 mt-[0.5in]"
132+
style={{ zIndex: 10 }} // Added z-index
133+
>
134+
{activeView ? <SettingsView /> : <Mapbox />}
135+
</div>
136+
</div>
144137
</MapDataProvider>
145138
);
146139
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
'use client'
2+
3+
import React, { useState, useEffect } from 'react'
4+
import { createPortal } from 'react-dom'
5+
import { Button } from '@/components/ui/button'
6+
import { Search } from 'lucide-react'
7+
import { useMap } from './map/map-context'
8+
import { useActions, useUIState } from 'ai/rsc'
9+
import { AI } from '@/app/actions'
10+
import { nanoid } from 'nanoid'
11+
import { UserMessage } from './user-message'
12+
import { toast } from 'react-toastify'
13+
14+
// Define an interface for the actions to help TypeScript during build
15+
interface HeaderActions {
16+
submit: (formData: FormData) => Promise<any>;
17+
}
18+
19+
export function HeaderSearchButton() {
20+
const { map } = useMap()
21+
// Cast the actions to our defined interface to avoid build errors
22+
const actions = useActions<typeof AI>() as unknown as HeaderActions
23+
const [, setMessages] = useUIState<typeof AI>()
24+
const [isAnalyzing, setIsAnalyzing] = useState(false)
25+
const [desktopPortal, setDesktopPortal] = useState<HTMLElement | null>(null)
26+
const [mobilePortal, setMobilePortal] = useState<HTMLElement | null>(null)
27+
28+
useEffect(() => {
29+
// Portals can only be used on the client-side after the DOM has mounted
30+
setDesktopPortal(document.getElementById('header-search-portal'))
31+
setMobilePortal(document.getElementById('mobile-header-search-portal'))
32+
}, [])
33+
34+
const handleResolutionSearch = async () => {
35+
if (!map) {
36+
toast.error('Map is not available yet. Please wait for it to load.')
37+
return
38+
}
39+
if (!actions) {
40+
// This should theoretically not happen if the component is used correctly
41+
toast.error('Search actions are not available.')
42+
return
43+
}
44+
45+
setIsAnalyzing(true)
46+
47+
try {
48+
setMessages(currentMessages => [
49+
...currentMessages,
50+
{
51+
id: nanoid(),
52+
component: <UserMessage content={[{ type: 'text', text: 'Analyze this map view.' }]} />
53+
}
54+
])
55+
56+
const canvas = map.getCanvas()
57+
const blob = await new Promise<Blob | null>(resolve => {
58+
canvas.toBlob(resolve, 'image/png')
59+
})
60+
61+
if (!blob) {
62+
throw new Error('Failed to capture map image.')
63+
}
64+
65+
const formData = new FormData()
66+
formData.append('file', blob, 'map_capture.png')
67+
formData.append('action', 'resolution_search')
68+
69+
const responseMessage = await actions.submit(formData)
70+
setMessages(currentMessages => [...currentMessages, responseMessage as any])
71+
} catch (error) {
72+
console.error('Failed to perform resolution search:', error)
73+
toast.error('An error occurred while analyzing the map.')
74+
} finally {
75+
setIsAnalyzing(false)
76+
}
77+
}
78+
79+
const desktopButton = (
80+
<Button
81+
variant="ghost"
82+
size="icon"
83+
onClick={handleResolutionSearch}
84+
disabled={isAnalyzing || !map || !actions}
85+
title="Analyze current map view"
86+
>
87+
{isAnalyzing ? (
88+
<div className="h-5 w-5 animate-spin rounded-full border-b-2 border-current"></div>
89+
) : (
90+
<Search className="h-[1.2rem] w-[1.2rem]" />
91+
)}
92+
</Button>
93+
)
94+
95+
const mobileButton = (
96+
<Button variant="ghost" size="sm" onClick={handleResolutionSearch} disabled={isAnalyzing || !map || !actions}>
97+
<Search className="h-4 w-4 mr-2" />
98+
Search
99+
</Button>
100+
)
101+
102+
return (
103+
<>
104+
{desktopPortal && createPortal(desktopButton, desktopPortal)}
105+
{mobilePortal && createPortal(mobileButton, mobilePortal)}
106+
</>
107+
)
108+
}

components/header.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ export const Header = () => {
3939
<CalendarDays className="h-[1.2rem] w-[1.2rem]" />
4040
</Button>
4141

42-
<Button variant="ghost" size="icon">
43-
<Search className="h-[1.2rem] w-[1.2rem]" />
44-
</Button>
42+
<div id="header-search-portal" />
4543

4644
<Button variant="ghost" size="icon">
4745
<TentTree className="h-[1.2rem] w-[1.2rem]" />
@@ -54,10 +52,7 @@ export const Header = () => {
5452

5553
{/* Mobile menu buttons */}
5654
<div className="flex md:hidden gap-2">
57-
<Button variant="ghost" size="sm">
58-
<Search className="h-4 w-4 mr-2" />
59-
Search
60-
</Button>
55+
<div id="mobile-header-search-portal" />
6156

6257
<ProfileToggle/>
6358
</div>

dev_server.log

Lines changed: 0 additions & 7 deletions
This file was deleted.
-34.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)