C5 — Implementation Plan

對應 design:design-C5.md 級別:中 預估工時:FE 4~5 天


§1

Pre-flight 驗證

cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe

# 1. C2.1 endpoints 已 ship?
# 確認以下 API 在 dev 環境可用:
# GET /projects/<uid>/current-ssp-uid
# GET /ssp/<uid>/system-characteristic
# GET /ssp/<uid>/parties
# GET /ssp/<uid>/ssp-resources
# PUT /ssp/<uid>/parties/<party_uid>
# POST /ssp/<uid>/ssp-resources/items

# 2. 找專案規劃頁 root component
find src/views -name "*Planning*" -o -name "*ProjectDetail*" | head -5

# 3. 找既有 MF panel 元件(可複用)
ls src/components/module_frame/

# 4. 確認既有 composable
ls src/composables/ | grep -E "ModuleFrame|Ssp"

# 5. 確認 PrimeVue 版本 + 已有 Accordion 元件
grep -n "primevue" package.json

§2

任務清單

T1 — 建立 composables(FE)

T1.1 useSspContext.js

新檔src/composables/useSspContext.js

內容:對齊 design-C5.md §4.3

Unit test:(Vitest if 有)

  • loadCurrent 呼叫 /projects/<uid>/current-ssp-uid 並設定 ref
  • 404 處理

Commitfeat(composables): C5 useSspContext

T1.2 useSspParties.js / useSspResources.js / useSspSystemCharacteristic.js / useSspLeveraged.js

新檔:對應 4 個 composable

每個 composable 包含:

  • fetch / create / update / delete actions
  • reactive state (loading / error / data)
  • Module-scope cache(仿 useModuleFrameParties 模式)+ invalidate(sspUid) 清快取

Commitfeat(composables): C5 useSspParties / useSspResources / useSspSystemCharacteristic / useSspLeveraged


T2 — Service 層 wrapper(FE)

新檔

  • src/service/SspService.js(仿 ModuleFrameService 模式,但 entry 改 ssp_uid)

內容:封裝 axios call for 所有 SSP-scoped endpoint

Commitfeat(service): C5 SspService


T3 — SspTabPanel.vue root 元件(FE)

新檔src/views/project/planning/SspTabPanel.vue

Props

  • projectUid: String

內容

  • AP selector dropdown
  • 五個 accordion + sections(lazy load)
  • 權限 + AP 狀態判斷邏輯
  • 控制項 / 程序書 link 區塊

State

const sspContext = useSspContext(toRef(props, 'projectUid'))
const selectedApUid = ref(null)
const expandedSections = ref(['basic'])  // 預設展開

const isEditable = computed(() =>
  sspContext.isEditable.value && currentUserRole.value === 'manager'
)

// AP selector
const apMenu = ref([])
async function loadApMenu() {
  apMenu.value = await GrcProjectService.getApMenu(props.projectUid)
}

// Deep link
onMounted(() => {
  const { section } = route.query
  if (section) {
    expandedSections.value = [section === 'scope' ? ['scope-devices', 'scope-info-systems'] : section]
  }
})

Commitfeat(project-planning): C5 SspTabPanel root component


T4 — SspBasicSection.vue 基本資料區塊(FE)

新檔src/components/project/ssp/SspBasicSection.vue

內容

  • 顯示 + 編輯 system_characteristic(name / system_identifier / sensitivity / scope_description / status / system_owner)
  • useSspSystemCharacteristic composable
  • editable 模式:表單;readonly:純顯示
  • 儲存按鈕 → PUT API + 顯示 toast

Commitfeat(project-planning): C5 SspBasicSection


T5 — SspScopeDevicesSection.vue + SspScopeInfoSystemsSection.vue(FE)

新檔

  • src/components/project/ssp/SspScopeDevicesSection.vue
  • src/components/project/ssp/SspScopeInfoSystemsSection.vue

仿 MF 版本

  • src/components/module_frame/ModuleFrameSspDevicesPanel.vue
  • src/components/module_frame/ModuleFrameSspInfoSystemsPanel.vue

改動

  • props 改吃 sspUid
  • API call 改 /ssp/<sspUid>/ssp-resources/...
  • useSspResources composable

Commitfeat(project-planning): C5 SspScopeDevicesSection + SspScopeInfoSystemsSection


T6 — SspPartiesSection.vue 參與人員區塊(FE)

新檔src/components/project/ssp/SspPartiesSection.vue

仿 MF 版本src/components/module_frame/ModuleFramePartiesPanel.vue

改動

  • props 改吃 sspUid
  • API call 改 /ssp/<sspUid>/parties/...
  • role dropdown 改吃 GET /system/menu/ssp_party_role (C1)
  • role 顯示用 $t('lang.oscal_role.ssp_party_role.<key>.label') (C1 i18n)
  • useSspParties composable

Commitfeat(project-planning): C5 SspPartiesSection


T7 — SspLeveragedSection.vue 外部利用服務(FE,待 C2.2 後可實作)

新檔src/components/project/ssp/SspLeveragedSection.vue

Phase 1(C5 first ship):placeholder 顯示「尚未開放,待 C2.2」

Phase 2(C2.2 ship 後補):

  • DataTable: service_name / provider / purpose
  • 仿 SspScopeDevicesSection pattern,API 改 /ssp/<uid>/leveraged

Commitfeat(project-planning): C5 SspLeveragedSection placeholder(C5 階段)/ feat(project-planning): C5.2 SspLeveragedSection full impl(C2.2 後)


內容

  • 在 SspTabPanel 底部加兩個 link button
  • 「→ 前往控制項實作」→ router.push 到既有 SSP control-implementation 頁
  • 「→ 前往程序書管理」→ router.push 到既有 ref-document 頁

Commitfeat(project-planning): C5 控制項/程序書 link 區塊


T9 — Tab 整合 + AP selector(FE)

檔案:專案規劃頁 root component(pre-flight 確認檔名)

動作

  • 加新 tab「SSP」進入既有 TabView
  • 加 AP selector dropdown
  • AP 切換邏輯:清空各 section state,重 fetch
  • Deep link ?tab=ssp&section=... 處理

Commitfeat(project-planning): C5 SSP tab + AP selector 整合


T10 — i18n 文案(FE)

檔案

  • src/config/locales/i18n/zh-tw/project-planning.json
  • src/config/locales/i18n/en/project-planning.json

新增 key

{
  "project_planning": {
    "tab_ssp": "SSP",
    "ssp_no_active_ap": "請先啟動 AP",
    "ssp_ap_closed_hint": "此 AP 已關閉,僅可檢視",
    "ssp_historical_ap_hint": "歷史 AP,僅可檢視",
    "ssp_no_manager_permission": "需 manager 權限",
    "section": {
      "basic": "基本資料",
      "scope_devices": "受評範圍 — 設備",
      "scope_info_systems": "受評範圍 — 資訊系統",
      "leveraged": "外部利用服務",
      "parties": "參與人員",
      "controls_link": "前往控制項實作",
      "ref_docs_link": "前往程序書管理"
    }
  }
}

Commitfeat(i18n): C5 project-planning SSP tab 文案


T11 — Manual smoke(FE)

步驟

  1. 建測試專案 + 啟動 AP(C2 + C3 PR1 已 ship 後)
  2. 進專案規劃頁 → 點「SSP」tab
  3. 預期:看到 AP selector + 五個 accordion(basic 預展開)
  4. 編輯基本資料 → 儲存 → 重 fetch 看到新值
  5. 展開受評範圍-設備 → 新增 device → table 內看到
  6. 展開參與人員 → 新增 party + 選 OSCAL role → table 內看到
  7. 切到非 active AP → 全 section readonly + hint
  8. 用非 manager 帳號進來 → 編輯按鈕 disabled + tooltip
  9. 從專案詳情頁點「前往 SSP 編輯」link → 跳到 SSP tab + 自動展開對應 section

T12 — Changelog (FE)

檔案:FE repo docs/changelog/YYYY-MM-DD-feat-project-planning-ssp-tab.md

---
type: feat
breaking: false
modules: [project-planning]
---

## 需求說明
C5:專案規劃頁加 SSP tab,讓 manager 在專案內直接編輯 current AP 對應的 SSP 內容。

## 變更範圍
### FE
- 5 個新 composable: useSspContext / useSspParties / useSspResources / useSspSystemCharacteristic / useSspLeveraged
- 1 個 service: SspService
- SspTabPanel root + 5 個 section components
- AP selector + 權限 / AP 狀態雙判斷 readonly mode
- Deep link `?tab=ssp&section=...` 支援
- i18n 文案

## UX
- Tab 切到 SSP 才 fetch(lazy load)
- 預設展開「基本資料」accordion
- 從專案詳情頁的範圍 card 點「前往 SSP 編輯」可直跳 + 自動展開 scope section
- 控制項實作 / 程序書連結既有頁面

## 後續
- Leveraged section 待 C2.2 後補
- Excel import 入口待 C2.2 後補

§3

測試規格

測試 內容
Manual smoke 上面 T11 全 case
Manual smoke Deep link 跳轉行為(從詳情頁 + 直接貼 URL)
Manual smoke AP 切換 + lazy load 行為
Manual smoke 權限:manager / reviewer / auditor / viewer 四角色 UI 差異
Vitest (if exists) composable cache invalidation

§4

完成標準(DoD)


§5

風險 / Rollback

風險 緩解
C2.1 endpoint 還沒 ship FE 撞 404 開工前 verify C2.1 全綠
既有 MF 元件複用後改不乾淨造成 MF 頁面 break 複用採 複製整檔再改 ssp 版本,而非抽 base,MF 完全不動
大型 component 一次性 ship 撞 review block 拆 commit by section(每個 section 一個 commit)
Deep link 與 router 行為衝突 beforeEach 或 onMounted 處理,加 unit test

Rollback:FE-only,新增檔案,git revert 即可。專案規劃頁原本的 tab 邏輯不動。