From f3493d24fbfb25a4465465b1ff4b89d60d5f9dd1 Mon Sep 17 00:00:00 2001
From: Greg Konush <12027037+gregkonush@users.noreply.github.com>
Date: Mon, 23 Dec 2024 22:22:11 -0800
Subject: [PATCH] feat: remove prop drill, add messages (#647)
---
apps/findbobastore/components.json | 21 ++++++
apps/findbobastore/package.json | 5 +-
apps/findbobastore/src/app/globals.css | 71 +++++++++++++++----
apps/findbobastore/src/app/layout.tsx | 24 ++-----
apps/findbobastore/src/app/page.tsx | 5 +-
.../src/components/error-message.tsx | 20 ++++++
.../findbobastore/src/components/map-view.tsx | 43 +++++++----
.../findbobastore/src/components/ui/alert.tsx | 38 ++++++++++
apps/findbobastore/src/lib/fonts.ts | 7 ++
apps/findbobastore/src/lib/utils.ts | 4 +-
apps/findbobastore/tailwind.config.ts | 53 +++++++++++++-
pnpm-lock.yaml | 9 +++
12 files changed, 247 insertions(+), 53 deletions(-)
create mode 100644 apps/findbobastore/components.json
create mode 100644 apps/findbobastore/src/components/error-message.tsx
create mode 100644 apps/findbobastore/src/components/ui/alert.tsx
create mode 100644 apps/findbobastore/src/lib/fonts.ts
diff --git a/apps/findbobastore/components.json b/apps/findbobastore/components.json
new file mode 100644
index 00000000..91b839b3
--- /dev/null
+++ b/apps/findbobastore/components.json
@@ -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"
+}
\ No newline at end of file
diff --git a/apps/findbobastore/package.json b/apps/findbobastore/package.json
index 02fb4337..8cd4c05a 100644
--- a/apps/findbobastore/package.json
+++ b/apps/findbobastore/package.json
@@ -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",
diff --git a/apps/findbobastore/src/app/globals.css b/apps/findbobastore/src/app/globals.css
index 6b717ad3..3e696779 100644
--- a/apps/findbobastore/src/app/globals.css
+++ b/apps/findbobastore/src/app/globals.css
@@ -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;
+ }
}
diff --git a/apps/findbobastore/src/app/layout.tsx b/apps/findbobastore/src/app/layout.tsx
index 32800f14..c4180787 100644
--- a/apps/findbobastore/src/app/layout.tsx
+++ b/apps/findbobastore/src/app/layout.tsx
@@ -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 (
-
-
{children}
+
+ {children}
)
}
diff --git a/apps/findbobastore/src/app/page.tsx b/apps/findbobastore/src/app/page.tsx
index fd765829..319d53bc 100644
--- a/apps/findbobastore/src/app/page.tsx
+++ b/apps/findbobastore/src/app/page.tsx
@@ -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 (
Find Boba Store
-
+
)
}
diff --git a/apps/findbobastore/src/components/error-message.tsx b/apps/findbobastore/src/components/error-message.tsx
new file mode 100644
index 00000000..5d726cd3
--- /dev/null
+++ b/apps/findbobastore/src/components/error-message.tsx
@@ -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 (
+
+
+ {title}
+ {description && {description}}
+
+ )
+}
diff --git a/apps/findbobastore/src/components/map-view.tsx b/apps/findbobastore/src/components/map-view.tsx
index 5a562059..4e814954 100644
--- a/apps/findbobastore/src/components/map-view.tsx
+++ b/apps/findbobastore/src/components/map-view.tsx
@@ -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 }) {
@@ -33,7 +34,7 @@ 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(null)
const [viewState, setViewState] = useState({
longitude: -122.4194,
@@ -41,6 +42,7 @@ export const MapView = memo(function MapView({ token }: { token: string | undefi
zoom: 12,
})
const [locationError, setLocationError] = useState(null)
+ const [mapError, setMapError] = useState<{ message: string; details?: string } | null>(null)
const getUserLocation = useCallback(() => {
if (!navigator.geolocation) {
@@ -67,28 +69,26 @@ export const MapView = memo(function MapView({ token }: { token: string | undefi
)
}, [])
- if (!token) {
- return (
-
-
-
Mapbox token is missing
-
-
- )
- }
-
return (
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,
+ })
+ }}
>
@@ -124,7 +124,24 @@ export const MapView = memo(function MapView({ token }: { token: string | undefi
)}
- {locationError &&
{locationError}
}
+ {locationError && (
+
+
+
+ )}
+ {mapError && (
+
+
+ {mapError.message}
+ {mapError.details && {mapError.details}
}
+
+ }
+ />
+
+ )}
)
})
diff --git a/apps/findbobastore/src/components/ui/alert.tsx b/apps/findbobastore/src/components/ui/alert.tsx
new file mode 100644
index 00000000..160a65ed
--- /dev/null
+++ b/apps/findbobastore/src/components/ui/alert.tsx
@@ -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 & VariantProps>(
+ ({ className, variant, ...props }, ref) => (
+
+ ),
+)
+Alert.displayName = 'Alert'
+
+const AlertTitle = React.forwardRef>(({ className, ...props }, ref) => (
+
+))
+AlertTitle.displayName = 'AlertTitle'
+
+const AlertDescription = React.forwardRef>(
+ ({ className, ...props }, ref) => ,
+)
+AlertDescription.displayName = 'AlertDescription'
+
+export { Alert, AlertTitle, AlertDescription }
diff --git a/apps/findbobastore/src/lib/fonts.ts b/apps/findbobastore/src/lib/fonts.ts
new file mode 100644
index 00000000..c9a736bb
--- /dev/null
+++ b/apps/findbobastore/src/lib/fonts.ts
@@ -0,0 +1,7 @@
+import { Inter } from 'next/font/google'
+
+export const inter = Inter({
+ subsets: ['latin'],
+ display: 'swap',
+ variable: '--font-inter',
+})
diff --git a/apps/findbobastore/src/lib/utils.ts b/apps/findbobastore/src/lib/utils.ts
index d32b0fe6..bd0c391d 100644
--- a/apps/findbobastore/src/lib/utils.ts
+++ b/apps/findbobastore/src/lib/utils.ts
@@ -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))
diff --git a/apps/findbobastore/tailwind.config.ts b/apps/findbobastore/tailwind.config.ts
index d27b0cdc..cbc1a173 100644
--- a/apps/findbobastore/tailwind.config.ts
+++ b/apps/findbobastore/tailwind.config.ts
@@ -1,14 +1,61 @@
import type { Config } from 'tailwindcss'
export default {
+ darkMode: ['class'],
content: ['./src/pages/**/*.{js,ts,jsx,tsx,mdx}', './src/components/**/*.{js,ts,jsx,tsx,mdx}', './src/app/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {
+ fontFamily: {
+ sans: ['var(--font-inter)'],
+ },
colors: {
- background: 'var(--background)',
- foreground: 'var(--foreground)',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))',
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))',
+ },
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))',
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))',
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))',
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))',
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))',
+ },
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ chart: {
+ '1': 'hsl(var(--chart-1))',
+ '2': 'hsl(var(--chart-2))',
+ '3': 'hsl(var(--chart-3))',
+ '4': 'hsl(var(--chart-4))',
+ '5': 'hsl(var(--chart-5))',
+ },
+ },
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)',
},
},
},
- plugins: [],
+ plugins: [require('tailwindcss-animate')],
} satisfies Config
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 897a293a..46f79163 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,12 +20,18 @@ importers:
apps/findbobastore:
dependencies:
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
clsx:
specifier: ^2.1.1
version: 2.1.1
framer-motion:
specifier: ^11.15.0
version: 11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ lucide-react:
+ specifier: ^0.469.0
+ version: 0.469.0(react@19.0.0)
mapbox-gl:
specifier: ^3.9.1
version: 3.9.1
@@ -44,6 +50,9 @@ importers:
tailwind-merge:
specifier: ^2.5.5
version: 2.5.5
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@3.4.17)
devDependencies:
'@eslint/eslintrc':
specifier: ^3