From 765c9f49a8b8cf3e9a3e47e9c93f56d9f4f220b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Wed, 10 Jun 2026 07:47:59 +0200 Subject: [PATCH] remove the map and ui shim packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both failed the deletion test in the telling direction: - @trails-cool/ui: Button/Input/Card had zero consumers — both apps roll their own elements inline. The only live part was a 6-line styles.css (the Tailwind entry + one keyframe), which now lives in each app as app/styles.css. - @trails-cool/map: MapView and RouteLayer had zero consumers; the package was otherwise a re-export of two map-core constants, and the two import sites now use @trails-cool/map-core directly. The "map components go in @trails-cool/map" convention had drifted long ago — the real map components live in apps/planner/app/components. CLAUDE.md's repository structure and conventions updated to match reality (including pointing shared-type guidance at the post-#515 sources: db row types, api contracts, Waypoint in types). Dockerfiles no longer COPY the deleted package manifests. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 8 +- apps/journal/Dockerfile | 2 - apps/journal/app/root.tsx | 2 +- .../ui/src => apps/journal/app}/styles.css | 0 apps/journal/package.json | 2 - apps/planner/Dockerfile | 2 - apps/planner/app/components/MapHelpers.tsx | 2 +- apps/planner/app/components/PlannerMap.tsx | 2 +- apps/planner/app/root.tsx | 2 +- apps/planner/app/styles.css | 6 ++ apps/planner/package.json | 2 - packages/map/package.json | 24 ----- packages/map/src/MapView.tsx | 34 -------- packages/map/src/RouteLayer.tsx | 18 ---- packages/map/src/index.test.ts | 19 ---- packages/map/src/index.ts | 6 -- packages/map/src/layers.ts | 2 - packages/map/tsconfig.json | 8 -- packages/map/vitest.config.ts | 1 - packages/ui/package.json | 16 ---- packages/ui/src/Button.tsx | 32 ------- packages/ui/src/Card.tsx | 12 --- packages/ui/src/Input.tsx | 25 ------ packages/ui/src/components.test.tsx | 87 ------------------- packages/ui/src/index.ts | 6 -- packages/ui/tsconfig.json | 8 -- packages/ui/vitest.config.ts | 8 -- pnpm-lock.yaml | 34 -------- 28 files changed, 13 insertions(+), 357 deletions(-) rename {packages/ui/src => apps/journal/app}/styles.css (100%) create mode 100644 apps/planner/app/styles.css delete mode 100644 packages/map/package.json delete mode 100644 packages/map/src/MapView.tsx delete mode 100644 packages/map/src/RouteLayer.tsx delete mode 100644 packages/map/src/index.test.ts delete mode 100644 packages/map/src/index.ts delete mode 100644 packages/map/src/layers.ts delete mode 100644 packages/map/tsconfig.json delete mode 100644 packages/map/vitest.config.ts delete mode 100644 packages/ui/package.json delete mode 100644 packages/ui/src/Button.tsx delete mode 100644 packages/ui/src/Card.tsx delete mode 100644 packages/ui/src/Input.tsx delete mode 100644 packages/ui/src/components.test.tsx delete mode 100644 packages/ui/src/index.ts delete mode 100644 packages/ui/tsconfig.json delete mode 100644 packages/ui/vitest.config.ts diff --git a/CLAUDE.md b/CLAUDE.md index 3a71dc9..2109593 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,9 +41,7 @@ apps/ planner/ — Planner app (React Router 7) journal/ — Journal app (React Router 7 + Fedify) packages/ - types/ — Shared TypeScript interfaces (Route, Activity, Waypoint) - ui/ — Shared React components (Tailwind) - map/ — React/Leaflet components (MapView, RouteLayer); re-exports map-core + types/ — Shared wire types both apps exchange (Waypoint) map-core/ — Framework-free map constants (colors, tiles, POI, z-index, snap); safe to import server-side gpx/ — GPX parsing, generation, validation fit/ — FIT file generation (Wahoo route push) @@ -125,8 +123,8 @@ local config symmetric. - **Route registration**: Both apps use explicit `routes.ts` (not file-based routing). When adding a new route file, you **must** add it to `apps/*/app/routes.ts` or it won't be compiled into the build. - All user-facing strings must use i18n (`useTranslation()` hook, never hardcode strings) -- Use `@trails-cool/types` for shared interfaces — don't duplicate type definitions -- Map components go in `@trails-cool/map`, not in individual apps +- Database row types are derived from the Drizzle schema (`@trails-cool/db`); API wire shapes are the Zod contracts in `@trails-cool/api`; only types both apps exchange (e.g. Waypoint) live in `@trails-cool/types` +- Map constants (colors, tiles, POI categories, z-indexes) go in `@trails-cool/map-core`; React/Leaflet map components live in the app that uses them - GPX parsing/generation goes in `@trails-cool/gpx` - Database schemas: `planner.*` for Planner data, `journal.*` for Journal data - Route geometry must be stored as PostGIS LineString (extracted from GPX on save) diff --git a/apps/journal/Dockerfile b/apps/journal/Dockerfile index caada24..8121fa3 100644 --- a/apps/journal/Dockerfile +++ b/apps/journal/Dockerfile @@ -9,8 +9,6 @@ FROM base AS deps COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/journal/package.json apps/journal/ COPY packages/types/package.json packages/types/ -COPY packages/ui/package.json packages/ui/ -COPY packages/map/package.json packages/map/ COPY packages/gpx/package.json packages/gpx/ COPY packages/i18n/package.json packages/i18n/ COPY packages/sentry-config/package.json packages/sentry-config/ diff --git a/apps/journal/app/root.tsx b/apps/journal/app/root.tsx index 31e6782..dd74a92 100644 --- a/apps/journal/app/root.tsx +++ b/apps/journal/app/root.tsx @@ -15,7 +15,7 @@ import { AccountDropdown } from "~/components/AccountDropdown"; import { MobileNavMenu } from "~/components/MobileNavMenu"; import { initSentryClient, stopSentryClient } from "~/lib/sentry.client"; import { TERMS_VERSION } from "~/lib/legal"; -import stylesheet from "@trails-cool/ui/styles.css?url"; +import stylesheet from "./styles.css?url"; // Paths that must stay reachable even when the user has a stale // terms_version, so they can read the Terms, accept them, or log out. diff --git a/packages/ui/src/styles.css b/apps/journal/app/styles.css similarity index 100% rename from packages/ui/src/styles.css rename to apps/journal/app/styles.css diff --git a/apps/journal/package.json b/apps/journal/package.json index b5eb55a..d044972 100644 --- a/apps/journal/package.json +++ b/apps/journal/package.json @@ -27,10 +27,8 @@ "@trails-cool/gpx": "workspace:*", "@trails-cool/i18n": "workspace:*", "@trails-cool/jobs": "workspace:*", - "@trails-cool/map": "workspace:*", "@trails-cool/sentry-config": "workspace:*", "@trails-cool/types": "workspace:*", - "@trails-cool/ui": "workspace:*", "drizzle-orm": "catalog:", "isbot": "^5.1.41", "jose": "^6.2.3", diff --git a/apps/planner/Dockerfile b/apps/planner/Dockerfile index 2130008..6f4e05f 100644 --- a/apps/planner/Dockerfile +++ b/apps/planner/Dockerfile @@ -9,8 +9,6 @@ FROM base AS deps COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/planner/package.json apps/planner/ COPY packages/types/package.json packages/types/ -COPY packages/ui/package.json packages/ui/ -COPY packages/map/package.json packages/map/ COPY packages/gpx/package.json packages/gpx/ COPY packages/i18n/package.json packages/i18n/ COPY packages/sentry-config/package.json packages/sentry-config/ diff --git a/apps/planner/app/components/MapHelpers.tsx b/apps/planner/app/components/MapHelpers.tsx index 2e4b184..4443cc6 100644 --- a/apps/planner/app/components/MapHelpers.tsx +++ b/apps/planner/app/components/MapHelpers.tsx @@ -2,7 +2,7 @@ import { useEffect, useState, useRef } from "react"; import { useMap, useMapEvents, Marker } from "react-leaflet"; import L from "leaflet"; import type { YjsState } from "~/lib/use-yjs"; -import { overlayLayers } from "@trails-cool/map"; +import { overlayLayers } from "@trails-cool/map-core"; import { usePois } from "~/lib/use-pois"; import { Z_CURSOR } from "@trails-cool/map-core"; import { getBaseLayer, getOverlays, setBaseLayer, setOverlays } from "~/lib/route-data"; diff --git a/apps/planner/app/components/PlannerMap.tsx b/apps/planner/app/components/PlannerMap.tsx index 0a135d3..e3a1284 100644 --- a/apps/planner/app/components/PlannerMap.tsx +++ b/apps/planner/app/components/PlannerMap.tsx @@ -4,7 +4,7 @@ import L from "leaflet"; import { useTranslation } from "react-i18next"; import type { DayStage } from "@trails-cool/gpx"; import type { YjsState } from "~/lib/use-yjs"; -import { baseLayers, overlayLayers } from "@trails-cool/map"; +import { baseLayers, overlayLayers } from "@trails-cool/map-core"; import { setOvernight } from "~/lib/overnight"; import { usePois } from "~/lib/use-pois"; import { useProfileDefaults } from "~/lib/use-profile-defaults"; diff --git a/apps/planner/app/root.tsx b/apps/planner/app/root.tsx index b86db49..b116079 100644 --- a/apps/planner/app/root.tsx +++ b/apps/planner/app/root.tsx @@ -3,7 +3,7 @@ import type { LinksFunction } from "react-router"; import type { Route } from "./+types/root"; import * as Sentry from "@sentry/react"; import { useTranslation } from "react-i18next"; -import stylesheet from "@trails-cool/ui/styles.css?url"; +import stylesheet from "./styles.css?url"; export const links: LinksFunction = () => [{ rel: "stylesheet", href: stylesheet }]; diff --git a/apps/planner/app/styles.css b/apps/planner/app/styles.css new file mode 100644 index 0000000..2fa790e --- /dev/null +++ b/apps/planner/app/styles.css @@ -0,0 +1,6 @@ +@import "tailwindcss"; + +@keyframes slide { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(400%); } +} diff --git a/apps/planner/package.json b/apps/planner/package.json index 15da606..5bffc1f 100644 --- a/apps/planner/package.json +++ b/apps/planner/package.json @@ -27,11 +27,9 @@ "@trails-cool/gpx": "workspace:*", "@trails-cool/i18n": "workspace:*", "@trails-cool/jobs": "workspace:*", - "@trails-cool/map": "workspace:*", "@trails-cool/map-core": "workspace:*", "@trails-cool/sentry-config": "workspace:*", "@trails-cool/types": "workspace:*", - "@trails-cool/ui": "workspace:*", "codemirror": "^6.0.2", "drizzle-orm": "catalog:", "isbot": "^5.1.41", diff --git a/packages/map/package.json b/packages/map/package.json deleted file mode 100644 index e0575ea..0000000 --- a/packages/map/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@trails-cool/map", - "version": "0.0.1", - "type": "module", - "exports": { - ".": "./src/index.ts" - }, - "main": "./src/index.ts", - "types": "./src/index.ts", - "scripts": { - "test": "vitest run", - "lint": "eslint .", - "typecheck": "tsc" - }, - "dependencies": { - "@trails-cool/map-core": "workspace:*" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18", - "leaflet": ">=1.9", - "react-leaflet": ">=5" - } -} diff --git a/packages/map/src/MapView.tsx b/packages/map/src/MapView.tsx deleted file mode 100644 index 4508793..0000000 --- a/packages/map/src/MapView.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { MapContainer, TileLayer, LayersControl } from "react-leaflet"; -import { baseLayers } from "./layers.ts"; -import "leaflet/dist/leaflet.css"; - -export interface MapViewProps { - center?: [number, number]; - zoom?: number; - className?: string; - children?: React.ReactNode; -} - -export function MapView({ - center = [50.1, 10.0], - zoom = 6, - className = "h-full w-full", - children, -}: MapViewProps) { - return ( - - - {baseLayers.map((layer, i) => ( - - - - ))} - - {children} - - ); -} diff --git a/packages/map/src/RouteLayer.tsx b/packages/map/src/RouteLayer.tsx deleted file mode 100644 index 7c16389..0000000 --- a/packages/map/src/RouteLayer.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { GeoJSON } from "react-leaflet"; -import type { GeoJsonObject } from "geojson"; - -export interface RouteLayerProps { - data: GeoJsonObject; - color?: string; - weight?: number; -} - -export function RouteLayer({ data, color = "#2563eb", weight = 4 }: RouteLayerProps) { - return ( - - ); -} diff --git a/packages/map/src/index.test.ts b/packages/map/src/index.test.ts deleted file mode 100644 index 3014fe4..0000000 --- a/packages/map/src/index.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @vitest-environment jsdom - */ -import { describe, it, expect } from "vitest"; -import { baseLayers, overlayLayers } from "./index.ts"; - -describe("map package exports", () => { - it("re-exports baseLayers from map-core", () => { - expect(baseLayers).toBeDefined(); - expect(baseLayers.length).toBeGreaterThan(0); - expect(baseLayers[0]!.name).toBe("OpenStreetMap"); - }); - - it("re-exports overlayLayers from map-core", () => { - expect(overlayLayers).toBeDefined(); - expect(overlayLayers.length).toBeGreaterThan(0); - expect(overlayLayers.every((l) => l.id)).toBe(true); - }); -}); diff --git a/packages/map/src/index.ts b/packages/map/src/index.ts deleted file mode 100644 index 7190941..0000000 --- a/packages/map/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { MapView } from "./MapView.tsx"; -export type { MapViewProps } from "./MapView.tsx"; -export { RouteLayer } from "./RouteLayer.tsx"; -export type { RouteLayerProps } from "./RouteLayer.tsx"; -export { baseLayers, overlayLayers } from "@trails-cool/map-core"; -export type { TileLayerConfig, OverlayLayerConfig } from "@trails-cool/map-core"; diff --git a/packages/map/src/layers.ts b/packages/map/src/layers.ts deleted file mode 100644 index c627c48..0000000 --- a/packages/map/src/layers.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { TileLayerConfig, OverlayLayerConfig } from "@trails-cool/map-core"; -export { baseLayers, overlayLayers } from "@trails-cool/map-core"; diff --git a/packages/map/tsconfig.json b/packages/map/tsconfig.json deleted file mode 100644 index 5a24989..0000000 --- a/packages/map/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src"] -} diff --git a/packages/map/vitest.config.ts b/packages/map/vitest.config.ts deleted file mode 100644 index 8a07b14..0000000 --- a/packages/map/vitest.config.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "../../vitest.shared.ts"; diff --git a/packages/ui/package.json b/packages/ui/package.json deleted file mode 100644 index df5f7fb..0000000 --- a/packages/ui/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "@trails-cool/ui", - "version": "0.0.1", - "type": "module", - "exports": { - ".": "./src/index.ts", - "./styles.css": "./src/styles.css" - }, - "main": "./src/index.ts", - "types": "./src/index.ts", - "scripts": { - "test": "vitest run", - "lint": "eslint .", - "typecheck": "tsc" - } -} diff --git a/packages/ui/src/Button.tsx b/packages/ui/src/Button.tsx deleted file mode 100644 index 0a436b7..0000000 --- a/packages/ui/src/Button.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { ButtonHTMLAttributes } from "react"; - -export interface ButtonProps extends ButtonHTMLAttributes { - variant?: "primary" | "secondary" | "ghost"; - size?: "sm" | "md" | "lg"; -} - -const variantStyles = { - primary: "bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800", - secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 active:bg-gray-300", - ghost: "text-gray-700 hover:bg-gray-100 active:bg-gray-200", -}; - -const sizeStyles = { - sm: "px-2.5 py-1.5 text-sm", - md: "px-4 py-2 text-sm", - lg: "px-6 py-3 text-base", -}; - -export function Button({ - variant = "primary", - size = "md", - className = "", - ...props -}: ButtonProps) { - return ( - ); - expect(screen.getByRole("button", { name: "Click me" })).toBeDefined(); - }); - - it("applies primary variant by default", () => { - render(); - expect(screen.getByRole("button").className).toContain("bg-blue-600"); - }); - - it("applies secondary variant", () => { - render(); - expect(screen.getByRole("button").className).toContain("bg-gray-100"); - }); - - it("applies ghost variant", () => { - render(); - expect(screen.getByRole("button").className).toContain("text-gray-700"); - }); - - it("applies size classes", () => { - render(); - expect(screen.getByRole("button").className).toContain("px-6"); - }); - - it("forwards disabled prop", () => { - render(); - expect(screen.getByRole("button")).toBeDisabled(); - }); -}); - -describe("Input", () => { - it("renders with label", () => { - render(); - expect(screen.getByLabelText("Email")).toBeDefined(); - }); - - it("generates id from label", () => { - render(); - expect(screen.getByLabelText("First Name").id).toBe("first-name"); - }); - - it("uses provided id over generated one", () => { - render(); - expect(screen.getByLabelText("Email").id).toBe("custom-id"); - }); - - it("shows error message", () => { - render(); - expect(screen.getByText("Required")).toBeDefined(); - }); - - it("applies error border class", () => { - render(); - expect(screen.getByLabelText("Email").className).toContain("border-red-500"); - }); - - it("renders without label", () => { - render(); - expect(screen.getByPlaceholderText("Type here")).toBeDefined(); - }); -}); - -describe("Card", () => { - it("renders children", () => { - render(Card content); - expect(screen.getByText("Card content")).toBeDefined(); - }); - - it("applies additional className", () => { - const { container } = render(Content); - expect(container.firstElementChild?.className).toContain("mt-4"); - }); -}); diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts deleted file mode 100644 index b68472e..0000000 --- a/packages/ui/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { Button } from "./Button.tsx"; -export type { ButtonProps } from "./Button.tsx"; -export { Input } from "./Input.tsx"; -export type { InputProps } from "./Input.tsx"; -export { Card } from "./Card.tsx"; -export type { CardProps } from "./Card.tsx"; diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json deleted file mode 100644 index 5a24989..0000000 --- a/packages/ui/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src"] -} diff --git a/packages/ui/vitest.config.ts b/packages/ui/vitest.config.ts deleted file mode 100644 index d70f8aa..0000000 --- a/packages/ui/vitest.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineConfig, mergeConfig } from "vitest/config"; -import shared from "../../vitest.shared.ts"; - -export default mergeConfig(shared, defineConfig({ - test: { - setupFiles: ["../../vitest.setup.ts"], - }, -})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 556c294..e89d44d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -241,18 +241,12 @@ importers: '@trails-cool/jobs': specifier: workspace:* version: link:../../packages/jobs - '@trails-cool/map': - specifier: workspace:* - version: link:../../packages/map '@trails-cool/sentry-config': specifier: workspace:* version: link:../../packages/sentry-config '@trails-cool/types': specifier: workspace:* version: link:../../packages/types - '@trails-cool/ui': - specifier: workspace:* - version: link:../../packages/ui drizzle-orm: specifier: 'catalog:' version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(expo-sqlite@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.7))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.7))(react@19.2.7))(pg@8.20.0)(postgres@3.4.9) @@ -495,9 +489,6 @@ importers: '@trails-cool/jobs': specifier: workspace:* version: link:../../packages/jobs - '@trails-cool/map': - specifier: workspace:* - version: link:../../packages/map '@trails-cool/map-core': specifier: workspace:* version: link:../../packages/map-core @@ -507,9 +498,6 @@ importers: '@trails-cool/types': specifier: workspace:* version: link:../../packages/types - '@trails-cool/ui': - specifier: workspace:* - version: link:../../packages/ui codemirror: specifier: ^6.0.2 version: 6.0.2 @@ -663,24 +651,6 @@ importers: specifier: 'catalog:' version: 22.19.20 - packages/map: - dependencies: - '@trails-cool/map-core': - specifier: workspace:* - version: link:../map-core - leaflet: - specifier: '>=1.9' - version: 1.9.4 - react: - specifier: ^19.2.5 - version: 19.2.7 - react-dom: - specifier: ^19.2.7 - version: 19.2.7(react@19.2.7) - react-leaflet: - specifier: '>=5' - version: 5.0.0(leaflet@1.9.4)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - packages/map-core: {} packages/sentry-config: @@ -691,8 +661,6 @@ importers: packages/types: {} - packages/ui: {} - scripts: devDependencies: '@types/node': @@ -10453,9 +10421,7 @@ snapshots: metro-runtime: 0.84.4 transitivePeerDependencies: - '@babel/core' - - bufferutil - supports-color - - utf-8-validate '@react-native/normalize-colors@0.85.3': {}