Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove prop drill, add messages #647

Merged
merged 1 commit into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions apps/findbobastore/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
5 changes: 4 additions & 1 deletion apps/findbobastore/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@
"lint": "next lint"
},
"dependencies": {
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^11.15.0",
"lucide-react": "^0.469.0",
"mapbox-gl": "^3.9.1",
"next": "15.1.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-map-gl": "^7.1.8",
"tailwind-merge": "^2.5.5"
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
Expand Down
71 changes: 59 additions & 12 deletions apps/findbobastore/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,67 @@
@tailwind components;
@tailwind utilities;

:root {
--background: #ffffff;
--foreground: #171717;
}

@media (prefers-color-scheme: dark) {
@layer base {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}

body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
24 changes: 5 additions & 19 deletions apps/findbobastore/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,16 @@
import type { Metadata } from 'next'
import { Geist, Geist_Mono } from 'next/font/google'
import { inter } from '@/lib/fonts'
import './globals.css'

const geistSans = Geist({
variable: '--font-geist-sans',
subsets: ['latin'],
})

const geistMono = Geist_Mono({
variable: '--font-geist-mono',
subsets: ['latin'],
})

export const metadata: Metadata = {
title: 'Find Boba Store',
description: 'Find Boba Store',
description: 'Find the best boba stores near you',
}

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>{children}</body>
<html lang="en" className={inter.variable}>
<body className="font-sans antialiased">{children}</body>
</html>
)
}
5 changes: 2 additions & 3 deletions apps/findbobastore/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { MapView } from '@/components/map-view'

export default async function Home() {
const mapboxToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN
export default function Home() {
return (
<main className="relative w-full h-screen">
<div className="absolute top-4 left-4 z-10 bg-slate-900/50 px-4 py-2 rounded-lg">
<h1 className="text-xl font-bold text-white">Find Boba Store</h1>
</div>
<MapView token={mapboxToken} />
<MapView />
</main>
)
}
20 changes: 20 additions & 0 deletions apps/findbobastore/src/components/error-message.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AlertCircle } from 'lucide-react'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { cn } from '@/lib/utils'
import type { ReactNode } from 'react'

interface ErrorMessageProps {
title: string
description?: ReactNode
className?: string
}

export function ErrorMessage({ title, description, className }: ErrorMessageProps) {
return (
<Alert variant="destructive" className={cn('border-red-900/50 bg-red-900/10', className)}>
<AlertCircle className="h-4 w-4" />
<AlertTitle>{title}</AlertTitle>
{description && <AlertDescription>{description}</AlertDescription>}
</Alert>
)
}
43 changes: 30 additions & 13 deletions apps/findbobastore/src/components/map-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ReactMapGL, { Marker, NavigationControl, GeolocateControl } from 'react-m
import { motion, AnimatePresence } from 'framer-motion'
import { cn } from '@/lib/utils'
import { stores, type BobaStore } from '@/data/stores'
import { ErrorMessage } from '@/components/error-message'
import 'mapbox-gl/dist/mapbox-gl.css'

function MapOverlay({ store }: { store: BobaStore | null }) {
Expand Down Expand Up @@ -33,14 +34,15 @@ function MapOverlay({ store }: { store: BobaStore | null }) {
)
}

export const MapView = memo(function MapView({ token }: { token: string | undefined }) {
export const MapView = memo(function MapView() {
const [selectedStore, setSelectedStore] = useState<BobaStore | null>(null)
const [viewState, setViewState] = useState({
longitude: -122.4194,
latitude: 37.7749,
zoom: 12,
})
const [locationError, setLocationError] = useState<string | null>(null)
const [mapError, setMapError] = useState<{ message: string; details?: string } | null>(null)

const getUserLocation = useCallback(() => {
if (!navigator.geolocation) {
Expand All @@ -67,28 +69,26 @@ export const MapView = memo(function MapView({ token }: { token: string | undefi
)
}, [])

if (!token) {
return (
<div className="flex items-center justify-center w-full h-full bg-slate-900 text-slate-100">
<div className="p-4 bg-slate-800 rounded-lg">
<p className="text-lg">Mapbox token is missing</p>
</div>
</div>
)
}

return (
<div className="relative w-full h-full">
<ReactMapGL
{...viewState}
onMove={(evt) => setViewState(evt.viewState)}
mapboxAccessToken={token}
mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN}
style={{ width: '100%', height: '100%' }}
mapStyle="mapbox://styles/mapbox/dark-v11"
maxZoom={15}
onLoad={() => {
getUserLocation()
}}
onError={(evt) => {
const errorDetails = evt?.error?.message || JSON.stringify(evt, null, 2)
console.error('Map error:', errorDetails)
setMapError({
message: 'Failed to load map',
details: errorDetails,
})
}}
>
<NavigationControl />
<GeolocateControl positionOptions={{ enableHighAccuracy: true }} trackUserLocation />
Expand Down Expand Up @@ -124,7 +124,24 @@ export const MapView = memo(function MapView({ token }: { token: string | undefi
)}
</ReactMapGL>
<MapOverlay store={selectedStore} />
{locationError && <div className="absolute top-4 left-4 p-4 bg-slate-900/90 rounded-lg text-slate-100 text-sm">{locationError}</div>}
{locationError && (
<div className="absolute top-4 right-4 z-10">
<ErrorMessage title="Location Error" description={locationError} />
</div>
)}
{mapError && (
<div className="absolute top-4 right-4 z-10">
<ErrorMessage
title="Map Error"
description={
<div className="space-y-2">
<p>{mapError.message}</p>
{mapError.details && <pre className="text-xs bg-slate-900/50 p-2 rounded overflow-auto max-h-32">{mapError.details}</pre>}
</div>
}
/>
</div>
)}
</div>
)
})
38 changes: 38 additions & 0 deletions apps/findbobastore/src/components/ui/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as React from 'react'
import { cva, type VariantProps } from 'class-variance-authority'

import { cn } from '@/lib/utils'

const alertVariants = cva(
'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7',
{
variants: {
variant: {
default: 'bg-background text-foreground',
destructive: 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
},
},
defaultVariants: {
variant: 'default',
},
},
)

const Alert = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>>(
({ className, variant, ...props }, ref) => (
<div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
),
)
Alert.displayName = 'Alert'

const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(({ className, ...props }, ref) => (
<h5 ref={ref} className={cn('mb-1 font-medium leading-none tracking-tight', className)} {...props} />
))
AlertTitle.displayName = 'AlertTitle'

const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
({ className, ...props }, ref) => <div ref={ref} className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />,
)
AlertDescription.displayName = 'AlertDescription'

export { Alert, AlertTitle, AlertDescription }
7 changes: 7 additions & 0 deletions apps/findbobastore/src/lib/fonts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Inter } from 'next/font/google'

export const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
})
4 changes: 2 additions & 2 deletions apps/findbobastore/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
Expand Down
Loading