feat: Implement initial authentication pages and a MapLibre GL map component with TanStack Router setup.

This commit is contained in:
2026-03-09 19:04:28 +01:00
parent c04f805936
commit bd8bb70b88
9 changed files with 5146 additions and 3131 deletions

1476
src/components/maps/map.tsx Normal file

File diff suppressed because it is too large Load Diff

6
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View File

@@ -2,7 +2,7 @@ import * as z from "zod"
export const loginFormSchema = z.object({
email: z.string().email("Invalid email address"),
password: z.string().min(1, "Password must be at least 1 character long"),
password: z.string().min(1, "Password must be at least 1 character long")
})
export const signupFormSchema = z.object({
@@ -10,16 +10,16 @@ export const signupFormSchema = z.object({
password: z.string().min(6, "Password must be at least 6 characters long"),
name: z.string().min(1, "The field is required"),
location: z.string().min(1, "The field is required"),
redirectUrl: z.string().optional(),
redirectUrl: z.string().optional()
})
export const profileFormSchema = z.object({
id: z.uuid(),
firstName: z.string().min(1, "First name is required"),
lastName: z.string().min(1, "Last name is required"),
lastName: z.string().min(1, "Last name is required")
})
export const userListParamsSchema = z.object({
page: z.number().min(1).default(1),
limit: z.number().min(1).max(100).default(10),
limit: z.number().min(1).max(100).default(10)
})

View File

@@ -13,6 +13,7 @@ import { Route as LoginRouteRouteImport } from './routes/login/route'
import { Route as IndexRouteImport } from './routes/index'
import { Route as DemoTanstackQueryRouteImport } from './routes/demo/tanstack-query'
import { Route as DemoI18nRouteImport } from './routes/demo.i18n'
import { Route as AuthDashboardRouteImport } from './routes/auth/dashboard'
import { Route as DemoSentryTestingRouteImport } from './routes/demo/sentry.testing'
const LoginRouteRoute = LoginRouteRouteImport.update({
@@ -35,6 +36,11 @@ const DemoI18nRoute = DemoI18nRouteImport.update({
path: '/demo/i18n',
getParentRoute: () => rootRouteImport,
} as any)
const AuthDashboardRoute = AuthDashboardRouteImport.update({
id: '/auth/dashboard',
path: '/auth/dashboard',
getParentRoute: () => rootRouteImport,
} as any)
const DemoSentryTestingRoute = DemoSentryTestingRouteImport.update({
id: '/demo/sentry/testing',
path: '/demo/sentry/testing',
@@ -44,6 +50,7 @@ const DemoSentryTestingRoute = DemoSentryTestingRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/login': typeof LoginRouteRoute
'/auth/dashboard': typeof AuthDashboardRoute
'/demo/i18n': typeof DemoI18nRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
'/demo/sentry/testing': typeof DemoSentryTestingRoute
@@ -51,6 +58,7 @@ export interface FileRoutesByFullPath {
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/login': typeof LoginRouteRoute
'/auth/dashboard': typeof AuthDashboardRoute
'/demo/i18n': typeof DemoI18nRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
'/demo/sentry/testing': typeof DemoSentryTestingRoute
@@ -59,6 +67,7 @@ export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/login': typeof LoginRouteRoute
'/auth/dashboard': typeof AuthDashboardRoute
'/demo/i18n': typeof DemoI18nRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
'/demo/sentry/testing': typeof DemoSentryTestingRoute
@@ -68,6 +77,7 @@ export interface FileRouteTypes {
fullPaths:
| '/'
| '/login'
| '/auth/dashboard'
| '/demo/i18n'
| '/demo/tanstack-query'
| '/demo/sentry/testing'
@@ -75,6 +85,7 @@ export interface FileRouteTypes {
to:
| '/'
| '/login'
| '/auth/dashboard'
| '/demo/i18n'
| '/demo/tanstack-query'
| '/demo/sentry/testing'
@@ -82,6 +93,7 @@ export interface FileRouteTypes {
| '__root__'
| '/'
| '/login'
| '/auth/dashboard'
| '/demo/i18n'
| '/demo/tanstack-query'
| '/demo/sentry/testing'
@@ -90,6 +102,7 @@ export interface FileRouteTypes {
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
LoginRouteRoute: typeof LoginRouteRoute
AuthDashboardRoute: typeof AuthDashboardRoute
DemoI18nRoute: typeof DemoI18nRoute
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
DemoSentryTestingRoute: typeof DemoSentryTestingRoute
@@ -125,6 +138,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof DemoI18nRouteImport
parentRoute: typeof rootRouteImport
}
'/auth/dashboard': {
id: '/auth/dashboard'
path: '/auth/dashboard'
fullPath: '/auth/dashboard'
preLoaderRoute: typeof AuthDashboardRouteImport
parentRoute: typeof rootRouteImport
}
'/demo/sentry/testing': {
id: '/demo/sentry/testing'
path: '/demo/sentry/testing'
@@ -138,6 +158,7 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
LoginRouteRoute: LoginRouteRoute,
AuthDashboardRoute: AuthDashboardRoute,
DemoI18nRoute: DemoI18nRoute,
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
DemoSentryTestingRoute: DemoSentryTestingRoute,

View File

@@ -5,7 +5,7 @@ import {
Scripts
} from "@tanstack/react-router"
import { Devtools } from "@/integrations/devtools"
import { getLocale } from "@/integrations/paraglide/runtime"
import { getLocale } from "@/paraglide/runtime"
import appCss from "@/styles/globals.css?url"
import Header from "../components/Header"

View File

@@ -0,0 +1,79 @@
import { Card } from "@heroui/react"
import { createFileRoute } from "@tanstack/react-router"
import { Drone } from "lucide-react"
import { useState } from "react"
import {
Map as MapComponent,
MapMarker,
MapViewport,
MarkerContent,
MarkerPopup,
MarkerTooltip
} from "@/components/maps/map"
export const Route = createFileRoute("/auth/dashboard")({
component: RouteComponent
})
function RouteComponent() {
const locations = [
{
id: 1,
name: "Location 1",
lat: 40.76,
lng: -73.98
},
{
id: 2,
name: "Location 2",
lat: 40.77,
lng: -73.99
},
{
id: 3,
name: "Location 3",
lat: 40.5874827,
lng: -1.7925343
}
]
const [viewport, setViewport] = useState<MapViewport>({
center: [40.5874827, -1.7925343],
zoom: 8,
bearing: 0,
pitch: 0
})
return (
<div>
<Card className="h-[800px] p-0 overflow-hidden">
<MapComponent
center={[40.5874827, -1.7925343]}
zoom={10}
viewport={viewport}
onViewportChange={setViewport}
>
{locations.map((location) => (
<MapMarker
key={location.id}
longitude={location.lng}
latitude={location.lat}
>
<MarkerContent>
<Drone size={24} color="green" className="text-green-200" />
{/* <div className="size-5 rounded-full bg-rose-500 border-2 border-white shadow-lg cursor-pointer hover:scale-110 transition-transform" /> */}
</MarkerContent>
<MarkerTooltip>{location.name}</MarkerTooltip>
<MarkerPopup>
<div className="space-y-1">
<p className="font-medium text-foreground">{location.name}</p>
<p className="text-xs text-muted-foreground">
{location.lat.toFixed(4)}, {location.lng.toFixed(4)}
</p>
</div>
</MarkerPopup>
</MapMarker>
))}
</MapComponent>
</Card>
</div>
)
}

View File

@@ -4,13 +4,13 @@ import { LogIn } from "lucide-react"
import { useState } from "react"
export const Route = createFileRoute("/login")({
component: RouteComponent,
component: RouteComponent
})
function RouteComponent() {
const [values, setValues] = useState({
email: "",
password: "",
password: ""
})
return (
<div className="grid-cols-2 grid min-h-screen">
@@ -88,7 +88,7 @@ function RouteComponent() {
height="16"
viewBox="0 0 16 16"
>
<g fill="none" fill-rule="evenodd" clip-rule="evenodd">
<g fill="none" fillRule="evenodd" clipRule="evenodd">
<path
fill="#f44336"
d="M7.209 1.061c.725-.081 1.154-.081 1.933 0a6.57 6.57 0 0 1 3.65 1.82a100 100 0 0 0-1.986 1.93q-1.876-1.59-4.188-.734q-1.696.78-2.362 2.528a78 78 0 0 1-2.148-1.658a.26.26 0 0 0-.16-.027q1.683-3.245 5.26-3.86"