2025-06-14 15:36:38 +00:00
|
|
|
import type { User } from 'oidc-client-ts';
|
|
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
|
import './App.css';
|
2025-06-15 21:06:10 +00:00
|
|
|
import { AppSidebar } from './AppSidebar';
|
2025-06-15 13:49:34 +00:00
|
|
|
import { getUser, handleCallback, logout } from './auth/authService';
|
|
|
|
|
import LoginModal from './components/LoginModal';
|
2025-06-14 15:36:38 +00:00
|
|
|
import { Map } from './components/Map';
|
2025-06-18 18:56:02 +00:00
|
|
|
import { Parameters, type ParameterValues } from './components/Parameters';
|
2025-06-15 21:06:10 +00:00
|
|
|
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from './components/ui/breadcrumb';
|
|
|
|
|
import { Button } from './components/ui/button';
|
|
|
|
|
import { Separator } from './components/ui/separator';
|
|
|
|
|
import { SidebarInset, SidebarProvider, SidebarTrigger } from './components/ui/sidebar';
|
2025-06-14 15:36:38 +00:00
|
|
|
|
|
|
|
|
function App() {
|
2025-06-15 12:48:52 +00:00
|
|
|
const [listingData, setListingData] = useState({});
|
2025-06-14 15:36:38 +00:00
|
|
|
const [user, setUser] = useState<User | null>(null);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// Check if this is a callback from Authentik (after login)
|
|
|
|
|
if (window.location.pathname === '/callback') {
|
|
|
|
|
handleCallback().then(() => {
|
|
|
|
|
window.location.href = '/'; // Redirect to home after login
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load user data
|
|
|
|
|
getUser().then(setUser);
|
|
|
|
|
}, []);
|
|
|
|
|
|
2025-06-15 21:06:10 +00:00
|
|
|
const [isParametersModalOpen, setIsParametersModalOpen] = useState(true)
|
|
|
|
|
const [error, setError] = useState('')
|
2025-06-18 18:56:02 +00:00
|
|
|
const [queryParameters, setQueryParameters] = useState<ParameterValues | null>(null)
|
2025-06-18 20:38:50 +00:00
|
|
|
const fetchData = async (parameters: ParameterValues) => {
|
|
|
|
|
const accessToken = user?.access_token;
|
|
|
|
|
const queryString = new URLSearchParams();
|
|
|
|
|
queryString.append('listing_type', parameters.listing_type)
|
|
|
|
|
if (parameters.max_price) {
|
|
|
|
|
queryString.append("max_price", parameters.max_price.toString());
|
|
|
|
|
}
|
2025-06-15 21:06:10 +00:00
|
|
|
try {
|
2025-06-18 20:38:50 +00:00
|
|
|
const response = await fetch("/api/listing_geojson?" + queryString,
|
2025-06-15 21:06:10 +00:00
|
|
|
{
|
|
|
|
|
method: 'GET',
|
|
|
|
|
headers: {
|
|
|
|
|
'Authorization': `Bearer ${accessToken}`, // Pass the token
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
if (!response.ok) throw new Error('Error: ' + response.json());
|
|
|
|
|
const data: Response = await response.json();
|
|
|
|
|
return data;
|
|
|
|
|
} catch (err) {
|
|
|
|
|
setError('Failed to fetch data: ' + err);
|
2025-06-18 20:38:50 +00:00
|
|
|
alert(JSON.stringify(err))
|
2025-06-15 21:06:10 +00:00
|
|
|
} finally {
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-06-18 18:56:02 +00:00
|
|
|
const onSubmit = async (parameters: ParameterValues) => {
|
2025-06-15 21:06:10 +00:00
|
|
|
// Fetch listing data
|
2025-06-18 18:56:02 +00:00
|
|
|
setQueryParameters(parameters)
|
2025-06-18 20:38:50 +00:00
|
|
|
const data = await fetchData(parameters);
|
|
|
|
|
console.log(data)
|
2025-06-15 21:06:10 +00:00
|
|
|
if (data) {
|
|
|
|
|
setListingData(data);
|
|
|
|
|
}
|
|
|
|
|
setIsParametersModalOpen(false)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-15 13:49:34 +00:00
|
|
|
if (!user) {
|
|
|
|
|
return <LoginModal isOpen={user === null} />
|
|
|
|
|
}
|
2025-06-14 15:36:38 +00:00
|
|
|
return (
|
|
|
|
|
<>
|
2025-06-15 21:06:10 +00:00
|
|
|
<SidebarProvider defaultOpen={false}>
|
|
|
|
|
<AppSidebar />
|
|
|
|
|
<SidebarInset>
|
|
|
|
|
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
|
|
|
|
<SidebarTrigger className="-ml-1" />
|
|
|
|
|
<Separator
|
|
|
|
|
orientation="vertical"
|
|
|
|
|
className="mr-2 data-[orientation=vertical]:h-4"
|
|
|
|
|
/>
|
|
|
|
|
<Breadcrumb>
|
|
|
|
|
<BreadcrumbList>
|
|
|
|
|
<BreadcrumbItem className="hidden md:block">
|
|
|
|
|
<BreadcrumbLink href="#">
|
|
|
|
|
Building Your Application
|
|
|
|
|
</BreadcrumbLink>
|
|
|
|
|
</BreadcrumbItem>
|
|
|
|
|
<BreadcrumbSeparator className="hidden md:block" />
|
|
|
|
|
<BreadcrumbItem>
|
|
|
|
|
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
|
|
|
|
|
</BreadcrumbItem>
|
|
|
|
|
</BreadcrumbList>
|
|
|
|
|
</Breadcrumb>
|
|
|
|
|
</header>
|
|
|
|
|
<div className="flex flex-col h-screen w-full">
|
|
|
|
|
<div className="flex gap-2 p-2 bg-gray-100">
|
|
|
|
|
<h1>Welcome, {user.profile.name}!</h1>
|
|
|
|
|
<Button onClick={logout}>Logout</Button>
|
|
|
|
|
<Parameters onSubmit={onSubmit} isOpen={isParametersModalOpen} />
|
|
|
|
|
</div>
|
|
|
|
|
{Object.keys(listingData).length > 0 &&
|
|
|
|
|
<div className="flex-1 w-full relative" style={{ minHeight: 0, marginBottom: '8rem' }}>
|
2025-06-18 18:56:02 +00:00
|
|
|
<Map listingData={listingData} queryParameters={queryParameters} />
|
2025-06-15 21:06:10 +00:00
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-06-15 12:48:52 +00:00
|
|
|
|
|
|
|
|
|
2025-06-15 21:06:10 +00:00
|
|
|
</SidebarInset>
|
|
|
|
|
</SidebarProvider>
|
2025-06-14 15:36:38 +00:00
|
|
|
</>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default App
|