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