# traveling-around-the-world 一個以 React 為核心的單頁式應用程式(SPA),用來記錄自己走訪過的國家、城市與時間軸。初期資料儲存在瀏覽器 LocalStorage,並預留後端 Express + MongoDB 的擴充架構,後續可平滑升級成多人同步的雲端服務。 ## Monorepo 結構 ``` traveling-around-the-world/ ├─ client/ # Vite + React + TS 的前端 SPA ├─ server/ # Express + MongoDB (未來) API 服務 └─ shared/ # 前後端共用的型別定義 ``` - `client` 預設採 LocalStorage 擷取資料,並透過 `VisitProvider` 可快速切換成 API 模式。 - `server` 已放好 Express + Mongoose 架構與 RESTful routes,只需接上 MongoDB 並啟動即可提供資料。 - `shared` 放行程型別 `VisitDto`,讓前後端保持相同資料契約。 ## 前端(client) ### 技術與工具 - Vite + React 18 + TypeScript - Material UI + Emotion 建構一致的 UI 與主題 - React Router 規劃 SPA 路由 - Zustand 追蹤 UI 狀態(訪問表單 Modal 與初始座標) - React Query 統一處理資料讀寫與快取 - React Hook Form + Zod 表單驗證 - react-leaflet + OpenStreetMap 圖資,支援地圖點擊新增足跡 - i18next 支援中英文切換,分類與設定等字串同步集中管理 ### 模組切分 - `features/map`:Leaflet 地圖、點擊事件、旅遊標記 - `features/visits`:資料型別、LocalStorage Repository、React Query hooks、統計卡片與表單元件 - `features/categories`:旅程分類 CRUD、React Query 與 sidebar 管理介面 - `components/layout`:MUI AppBar + Drawer 布局、語言切換等元件 - `state`:Zustand store 控制訪問建立/編輯的 Modal - `components/overlay/VisitModal`:以 Modal 呈現足跡表單,必須先點擊地圖右下角的 `+` 漂浮按鈕啟用定位,再第二次點擊地圖開啟表單 - `components/overlay/NavControlsDialog`:App Bar 右上角的「更多」按鈕會開啟的快速操作 Modal(語言、分類切換、相關連結) ### LocalStorage → API 的切換 - 預設 `VisitProvider mode="local"` 會載入 `localVisitRepository`。 - 改為 `mode="api"` 並設定 `apiBaseUrl` 後,就會改用 `VisitApiClient` 透過 REST API 存取。 - Vite `proxy` 已轉發 `/api` 至 `http://localhost:4000`,未來後端啟動後無需改動前端呼叫。 ### 旅程分類管理 - 所有旅程可指定分類,未指定時會自動歸入「未分類」。 - 側邊欄可透過 chip 快速篩選「全部 / 未分類 / 自訂分類」。 - 在「管理分類」清單可新增、重新命名或刪除分類;刪除時相關旅程會回到「未分類」。 - 當前分類會記錄在 LocalStorage,在地圖右下角新增旅程時會自動帶入。 ### 快速操作面板 - App Bar 右上角改為「更多」圖示,點擊後會開啟 Modal。 - Modal 集中展示語言切換、目前分類切換以及開發相關連結(OpenStreetMap、React Leaflet)。 - 便於未來擴充更多設定選項,而不把 App Bar 擠得太滿。 ### PWA 支援 - 新增 Web App Manifest(`client/public/manifest.webmanifest`)與 service worker 註冊,可在行動裝置安裝為獨立 App。 - 透過 `vite-plugin-pwa` 自動產生快取策略,支援離線瀏覽與版號自動更新。 - 內建 `icons/icon-192.svg`、`icons/icon-512.svg`,可於日後替換為專屬圖示。 ### 多語系支援 - 透過 `react-i18next` 提供繁體中文與英文,語系檔位於 `client/src/locales/`。 - 右上角的語言選單 (`LanguageSwitcher`) 可即時切換語言,並記錄在 LocalStorage 方便下次載入。 ### 開發啟動 1. 安裝相依套件:`cd client && npm install` 2. 啟動開發伺服器:`npm run dev` 3. 瀏覽器開啟 `http://localhost:5173` > **注意**:Leaflet 需要載入 CSS(已在 `TravelMap` 中引入 `leaflet/dist/leaflet.css`)。 ### 專案建置 - 推薦將 `client/package.json` 的 `build` 指令改為 `"build": "tsc --noEmit && vite build"`,避免 `tsconfig.node.json` 的 `noEmit` 限制造成錯誤。 - 調整後可執行 `npm run build`,產出靜態檔案於 `client/dist`。 - 首次啟用 PWA 時記得在 `client` 目錄重新執行 `npm install`,以安裝 `vite-plugin-pwa` 相依套件。 ### 部署到 Vercel 1. 先在本地確認 `npm run build` 可成功(參考上節的建置指令變更)。 2. 將整個專案推送至 GitHub / GitLab / Bitbucket。 3. 在 Vercel 建立新專案時: - Project Root:`client` - Build Command:`npm run build` - Output Directory:`dist` 4. 連續部署:Vercel 會在每次 push default branch、自動建立 preview 的 PR 觸發建置並更新站台。 5. 若後續要加上環境變數,可在 Vercel 專案設定內補上即可。 ### Docker Compose 啟動 若想直接以容器方式啟動整個堆疊(MongoDB + Node API + Nginx 前端): 1. 建立映像:`docker compose build` 2. 啟動服務:`docker compose up -d` 3. 前端: 4. API: > 預設前端仍採 LocalStorage。如果要改用 API 資料來源,請在 `client/src/app/App.tsx` 把 `VisitProvider mode="local"` 改成 `mode="api"`。 ## 後端(server) ### 技術堆疊 - Express 4 - Mongoose 8 + MongoDB(內建 visit schema,資料結構與前端共用) - Zod 驗證 API 請求 payload - Helmet、CORS、Morgan 提供基礎安全與紀錄 ### 架構亮點 - `src/app.ts` 建立 Express App、共用中介層、`/health` 健康檢查 - `src/modules/visits`:模型、Service、Controller、Router 切分清晰 - `visit.mapper.ts` 將 Mongoose Doc 轉成 `shared/visit.ts` 的 DTO - `infra/db/mongo.ts` 佈建 Mongo 連線抽象 ### 啟動指令 1. 安裝套件:`cd server && npm install` 2. 建立 `.env`(可複製 `.env.example`)並設定 `MONGODB_URI` 3. 開發模式:`npm run dev` 4. 編譯產出:`npm run build` -> `dist/` RESTful 端點: - `GET /api/visits`:取得全部足跡 - `POST /api/visits`:新增足跡 - `GET /api/visits/:id` - `PUT /api/visits/:id` - `DELETE /api/visits/:id` ## 共享型別(shared) - `shared/visit.ts` 定義 Visit DTO,前端 `Visit` 及後端回傳皆採相同結構。 - Vite 透過 `@shared/*` alias 匯入,後端則直接使用相對路徑,確保型別同步。 ## 開發流程建議 1. **LocalStorage MVP**:僅啟動前端即可使用,點擊地圖開啟表單、資料存在瀏覽器。 2. **串接 API**:建立 MongoDB,啟動 `npm run dev` 後將前端 `VisitProvider` 切換到 API 模式。 3. **Geocoding**:前端已透過 Nominatim 反向地理編碼在點擊地圖時預填國家 / 城市;若需更高配額可在後端建立 Proxy。 4. **照片/檔案**:後端可擴增 S3/Cloudinary 上傳,再於前端 `VisitCreateInput` 增加檔案欄位。 ## 待辦與下一步 - [ ] 實作 geocoding proxy,於點擊地圖後自動填入國家 / 城市 - [ ] 加入使用者認證(如 Auth0 或自建 JWT)以支援多人服務 - [ ] 將訪問紀錄導出成時間線或統計圖表(D3 / Recharts) - [ ] 建立 Vitest / Jest 單元測試與 React Testing Library 元件測試 - [ ] 規劃 CI(GitHub Actions)自動執行 `lint` 與 `test` - [ ] 自動化部署流程模板(Vercel / GitHub Actions) ## 授權 本專案採用 MIT License,詳見 `LICENSE`。