da48a3652d
後端: - Migration:diving_offers 新增 provider_id(nullable FK) - Migration:users.role ENUM 加入 provider 值 - Migration:diving_offers.spot 改為 nullable - AuthController:registerProvider business_name 改為選填 - AuthController:updateProviderProfile 補上 certifications / dive_sites / services / facilities / website / social_media - ProviderOfferController:教練課程 CRUD(index/show/store/update/destroy),實作 provider_id 所有權不變式(404 → 403 兩步驟) 前端(frontend/): - coachAuth store、coachAxios(獨立於 member auth) - /coach/* 路由群組 + beforeEach guard - CoachNavBar、CoachLayout(教練頁隱藏會員 NavBar) - LoginView、RegisterView、DashboardView(表格 + 刪除確認) - OfferFormView(新增/編輯共用)、ProfileView OpenSpec: - openspec/config.yaml 補入專案 context 與 rules - 新增 specs:coach-offers-api / coach-portal-ui / provider-auth - 更新 spec:diving-offers-api 加入 provider_id - 歸檔:openspec/changes/archive/2026-05-10-coach-portal Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
6.1 KiB
6.1 KiB
1. [後端] 資料庫 Migration
- 1.1 建立 migration:
diving_offers新增provider_id欄位(unsignedBigIntegernullable,外鍵關聯users.id,onDelete set null) - 1.2 執行
docker exec cfdive-app php artisan migrate,確認欄位新增成功 - 1.3 更新
DivingOfferModel:$fillable加入provider_id
2. [後端] Provider Auth API 調整
- 2.1 修改
registerProvider():將business_name改為 nullable(單人教練不一定有業者名稱),驗證規則從required改為nullable|string|max:255 - 2.2
loginProvider()✅ 直接可用,不需改動(role 驗證、token、load profile 皆正確) - 2.3
logoutProvider()✅ 直接可用,不需改動 - 2.4
providerProfile()✅ 直接可用,不需改動 - 2.5 補完
updateProviderProfile():在現有更新邏輯後補上以下欄位的更新處理:certifications(PADI / SSI 等認證資訊)dive_sites(常駐潛點,逗號分隔字串)services(提供服務類型)facilities(設施說明)website(官網連結)social_media(社群媒體連結)- 同時在 Validator 規則加入這六個欄位(皆為
nullable|string)
- 2.6 用 Postman 驗證:register(business_name 選填)→ login → GET profile → PUT profile(含新增欄位)→ logout
3. [後端] Coach 課程管理 API
- 3.1 建立
ProviderOfferController,實作index():只回傳provider_id = auth()->id()的課程,含分頁 - 3.2 實作
show($id):find()null → 404;provider_id !== auth()->id()→ 403;否則回傳課程資料 - 3.3 實作
store():驗證必填欄位(title / location / spot / price / region),強制將provider_id設為auth()->id()(忽略 body 傳入值),回傳 201 - 3.4 實作
update($id):find()null → 404;provider_id !== auth()->id()→ 403;更新欄位回傳 200 - 3.5 實作
destroy($id):find()null → 404;provider_id !== auth()->id()→ 403;刪除回傳 200 - 3.6 在
routes/api.php的providermiddleware group 新增課程路由:GET(index) / GET(show) / POST / PUT / DELETE - 3.7 用 Postman 驗證:新增 → 列表 → 單筆詳情 → 更新 → 刪除;另測試跨教練操作:存在課程回 403、不存在 ID 回 404
4. [前端] coachAuth Store 與基礎設施
- 4.1 建立
frontend/src/stores/coachAuth.js:管理coach_token/coach_user,實作init()/setAuth()/logout() - 4.2 建立
frontend/src/api/coachAxios.js:獨立 Axios instance,request interceptor 讀coach_token - 4.3 在
frontend/src/router/index.js新增/coach/*路由群組:login / dashboard / offers/new / offers/:id/edit / profile - 4.4
/coach/*(login 除外)加上 beforeEach guard,未登入導向/coach/login - 4.5 在
App.vue的onMounted加入coachAuth.init()
5. [前端] Coach Layout 與導覽
- 5.1 建立
frontend/src/components/CoachNavBar.vue:顯示教練姓名、「我的課程」、「個人資料」連結與登出按鈕 - 5.2 建立
frontend/src/layouts/CoachLayout.vue:包含 CoachNavBar +<RouterView>,供所有/coach/*頁面使用
6. [前端] 教練認證頁面
- 6.1 建立
frontend/src/views/coach/RegisterView.vue:帳號資訊 + 業者資訊兩段表單,送出呼叫POST /api/provider/register,成功導向/coach/login?registered=1,失敗顯示欄位錯誤;business_name 選填 - 6.2 建立
frontend/src/views/coach/LoginView.vue:email/password 表單,送出呼叫POST /api/provider/login,成功存 token 並導向/coach/dashboard,失敗顯示錯誤;若 query 有?registered=1顯示「註冊成功,請登入」
7. [前端] 課程 Dashboard
- 7.1 建立
frontend/src/views/coach/DashboardView.vue:掛載時呼叫GET /api/provider/offers,以表格列出課程(標題、地點、價格) - 7.2 新增「新增課程」按鈕,點擊導向
/coach/offers/new - 7.3 每列新增「編輯」按鈕,點擊導向
/coach/offers/:id/edit - 7.4 每列新增「刪除」按鈕:顯示確認 dialog,確認後呼叫
DELETE /api/provider/offers/{id},成功後重新載入列表 - 7.5 無課程時顯示空狀態提示
8. [前端] 課程表單(新增 / 編輯)
- 8.1 建立
frontend/src/views/coach/OfferFormView.vue(新增與編輯共用同一個組件,以 route param 判斷模式) - 8.2 欄位:title(必填)、location(必填)、spot、price(必填)、region、tag、badges(多選或逗號分隔輸入)、description
- 8.3 新增模式:送出呼叫
POST /api/provider/offers,成功後導向 Dashboard - 8.4 編輯模式:掛載時取得課程資料預填,送出呼叫
PUT /api/provider/offers/{id},成功後導向 Dashboard - 8.5 前端必填欄位驗證(title / location / price 為空時不送出)
9. [前端] 教練個人資料頁
- 9.1 建立
frontend/src/views/coach/ProfileView.vue:掛載時呼叫GET /api/provider/profile,顯示以下欄位:- 基本:name、email、phone
- 業者:business_name(工作室/個人教練名稱)、description(自我介紹)
- 專業:certifications(認證)、dive_sites(常駐潛點)、services(服務類型)
- 聯絡:contact_person、contact_phone、contact_email、address、business_hours
- 網路:website、social_media
- 唯讀顯示(不可自改):is_verified、rating
- 9.2 實作編輯表單,送出呼叫
PUT /api/provider/profile(包含 task 2.5 補完的新欄位),成功顯示「資料已更新」提示
10. [整合測試] 端對端驗證
- 10.1 驗證教練完整認證流程:註冊 → 登入 → 登出 → 重新登入
- 10.2 驗證課程 CRUD:新增 → Dashboard 出現 → 編輯 → 刪除
- 10.3 驗證 route guard:未登入訪問
/coach/dashboard自動跳轉/coach/login - 10.4 驗證權限隔離:教練 A 無法編輯/刪除教練 B 的課程(API 層回傳 403)
- 10.5 驗證公開課程列表(
/courses)能看到教練新增的課程