feat:實作 Member Portal MVP 前端與後端整合
後端: - 新增 DivingOffer Model / DivingOfferController(列表+詳情 API,支援搜尋/篩選/分頁) - 修正 Google OAuth callback 改為 redirect 至前端(SocialAuthController) - 新增 config/cors.php 允許前端 origin - .gitignore 新增 frontend/ 排除規則 前端(frontend/): - Vue 3 + Vite + Tailwind CSS + Pinia + Vue Router - 頁面:首頁、課程列表、課程詳情、登入、註冊、個人資料、OAuth callback - 整合至 Docker(multi-stage build,nginx 靜態服務於 port 5173) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
## 1. [後端] 環境與 CORS 設定
|
||||
|
||||
- [x] 1.1 在 `.env` 新增 `FRONTEND_URL=http://localhost:5173`、`GOOGLE_CLIENT_ID`、`GOOGLE_CLIENT_SECRET`、`GOOGLE_REDIRECT_URI=http://localhost:80/api/auth/google/callback`
|
||||
- [x] 1.2 執行 `php artisan config:publish cors` 建立 `config/cors.php`,設定 `allowed_origins=[FRONTEND_URL]`、`allowed_methods`、`allowed_headers`、`supports_credentials=false`(參考 design.md Contract 3)
|
||||
- [x] 1.3 確認 `bootstrap/app.php`(或 `app/Http/Kernel.php`)已啟用 `HandleCors` middleware
|
||||
|
||||
## 2. [後端] 修正 Google OAuth Callback
|
||||
|
||||
- [x] 2.1 修改 `SocialAuthController::handleGoogleCallback()`:成功時改為 `redirect(env('FRONTEND_URL') . '/auth/callback?token=' . $token)`
|
||||
- [x] 2.2 修改 catch 區塊:失敗時改為 `redirect(env('FRONTEND_URL') . '/login?error=oauth_failed')`
|
||||
- [x] 2.3 手動測試 OAuth 流程:點擊 Google 登入後確認瀏覽器最終落在 `:5173/auth/callback?token=...`
|
||||
|
||||
## 3. [後端] Diving Offers API
|
||||
|
||||
- [x] 3.1 更新 `DivingOffer` Model:設定 `$fillable`、`$table`,`badges` 欄位加上 `$casts = ['badges' => 'array']` 自動 JSON decode
|
||||
- [x] 3.2 建立 `DivingOfferController`,實作 `index()` 方法(支援 q / region / tag 篩選,分頁預設 12 筆,max 50)
|
||||
- [x] 3.3 實作 `show($id)` 方法:找不到時回傳 `{ "status": false, "message": "課程不存在" }`(HTTP 404)
|
||||
- [x] 3.4 在 `routes/api.php` 新增公開路由:`GET /diving-offers` 和 `GET /diving-offers/{id}`
|
||||
- [x] 3.5 用 Postman 驗證:列表(含 q / region / tag / 分頁)、詳情、404 情境,確認 response 結構符合 design.md Contract 1
|
||||
|
||||
## 4. [前端] 專案初始化
|
||||
|
||||
- [x] 4.1 在 Laravel repo 外建立新目錄 `cf-dive-frontend`,執行 `npm create vite@latest . -- --template vue`
|
||||
- [x] 4.2 安裝依賴:`npm install`,再安裝 `vue-router@4 pinia axios`
|
||||
- [x] 4.3 安裝並設定 Tailwind CSS(`tailwindcss postcss autoprefixer`,初始化 `tailwind.config.js`)
|
||||
- [x] 4.4 建立 `.env` 文件,設定 `VITE_API_URL=http://localhost:80`
|
||||
- [x] 4.5 建立 `src/api/axios.js`:設定 Axios instance,base URL 讀自 `import.meta.env.VITE_API_URL`,request interceptor 讀 localStorage token 並附加 `Authorization: Bearer <token>`
|
||||
- [x] 4.6 建立 `src/stores/auth.js`:Pinia store 管理 `user`、`token`、`isLoggedIn`,`init()` 從 localStorage 還原狀態
|
||||
- [x] 4.7 設定 Vue Router(`src/router/index.js`):定義所有路由(含 `/auth/callback`),`/profile` 加上 beforeEach navigation guard(未登入導向 `/login`)
|
||||
- [x] 4.8 在 `App.vue` 呼叫 `authStore.init()`,並加入 `<RouterView>`
|
||||
- [x] 4.9 執行 `npm run dev`,確認開發環境正常啟動無錯誤
|
||||
|
||||
## 5. [前端] Layout 與共用組件
|
||||
|
||||
- [x] 5.1 建立 `src/components/NavBar.vue`:顯示 logo、「探索課程」連結,已登入顯示「個人資料」和「登出」,未登入顯示「登入」和「註冊」
|
||||
- [x] 5.2 建立 `src/components/CourseCard.vue`:接收 offer 資料,顯示標題、地點、價格、評分、標籤
|
||||
|
||||
## 6. [前端] 首頁
|
||||
|
||||
- [x] 6.1 建立 `src/views/HomeView.vue`:Hero section(平台名稱、簡介)+ 「探索課程」CTA 按鈕,點擊導向 `/courses`
|
||||
|
||||
## 7. [前端] 課程列表頁
|
||||
|
||||
- [x] 7.1 建立 `src/views/CoursesView.vue`,掛載時呼叫 `GET /api/diving-offers`,渲染 `CourseCard` 列表
|
||||
- [x] 7.2 新增搜尋框:輸入後按 Enter 或點搜尋重新呼叫 API(帶 `q` 參數)
|
||||
- [x] 7.3 新增地區下拉選單:選擇後以 `region` 參數重新呼叫 API
|
||||
- [x] 7.4 處理無結果狀態:顯示「找不到符合的課程」提示
|
||||
|
||||
## 8. [前端] 課程詳情頁
|
||||
|
||||
- [x] 8.1 建立 `src/views/CourseDetailView.vue`,掛載時呼叫 `GET /api/diving-offers/:id`
|
||||
- [x] 8.2 顯示課程完整資訊:標題、地點、景點、價格、評分、評論數、描述、徽章(badges 陣列)、標籤
|
||||
- [x] 8.3 處理 404 情境:顯示「課程不存在」並提供「返回列表」按鈕
|
||||
|
||||
## 9. [前端] 認證頁面
|
||||
|
||||
- [x] 9.1 建立 `src/views/LoginView.vue`:email/password 表單,送出呼叫 `POST /api/member/login`,成功存 token + user 至 Pinia 並導向 `/courses`,失敗顯示錯誤訊息
|
||||
- [x] 9.2 在 `LoginView.vue` 加入「以 Google 登入」按鈕:點擊執行 `window.location.href = VITE_API_URL + '/api/auth/google/redirect'`
|
||||
- [x] 9.3 建立 `src/views/AuthCallbackView.vue`(路由 `/auth/callback`):讀取 `?token=` query param → 存入 Pinia + localStorage → 呼叫 `history.replaceState` 清除 URL token → 導向 `/courses`;若 `?error=oauth_failed` 則導向 `/login` 並顯示錯誤提示
|
||||
- [x] 9.4 建立 `src/views/RegisterView.vue`:name / email / password / password_confirmation 表單,送出呼叫 `POST /api/member/register`,成功導向 `/login` 並顯示成功提示,失敗顯示錯誤
|
||||
|
||||
## 10. [前端] 會員個人資料頁
|
||||
|
||||
- [x] 10.1 建立 `src/views/ProfileView.vue`,掛載時呼叫 `GET /api/member/profile`,顯示姓名、email、生日、性別、地址、緊急聯絡人
|
||||
- [x] 10.2 實作編輯表單:使用者修改後點擊「儲存」呼叫 `PUT /api/member/profile`,成功顯示「資料已更新」提示
|
||||
|
||||
## 11. [整合測試] 端對端驗證
|
||||
|
||||
- [x] 11.1 驗證訪客流程:首頁 → 課程列表(搜尋/篩選)→ 課程詳情(無需登入)
|
||||
- [x] 11.2 驗證 Email 認證流程:註冊 → 登入 → 個人資料 → 登出
|
||||
- [x] 11.3 驗證 Google OAuth 流程:點擊 Google 登入 → 同意 → 回到前端 `/auth/callback` → 自動存 token → 導向課程列表
|
||||
- [x] 11.4 驗證 navigation guard:未登入直接訪問 `/profile` 自動跳轉至 `/login`
|
||||
- [x] 11.5 驗證 CORS:確認 Network tab 無 CORS 錯誤,所有 API 請求正常回應
|
||||
Reference in New Issue
Block a user