For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Merge the "Information System" and "Device" sections in ProjectCreateView and ProjectSettingsView into a single "受評範圍 (Audit Scope)" section, frontend-only, no BE / schema changes.
Architecture: Build a new AuditScopeSection.vue wrapper component that hosts the existing SystemPicker and DevicePicker components stacked vertically. Wire it into both views, replacing the separate tabs/steps. All save logic stays exactly the same — syncProjectSystems() and syncProjectDevices() continue to fire independently from the wrapper's events.
Tech Stack: Vue 3 (Composition API + <script setup lang="ts">), PrimeVue (TabView, Steps), vue-i18n, Pinia (menuStore), Vuelidate (existing).
Repository: All code work happens in ~/Projects/Billows/Audit-Manager/compliance-manager-fe/. Design + plan + changelog live in ~/Projects/Billows/Audit-Manager/compliance-manager-be/docs/.
Spec reference: docs/features/FR-017-2604-audit-scope-merge/design.md
This is a Vue 3 frontend project that does NOT have a unit test framework for components (no Vitest / no @vue/test-utils in package.json). Only Cypress E2E tests exist (cypress:open, cypress:run).
Therefore TDD as "write failing test first" does not apply here. Each task uses manual browser verification as the validation step:
npm run dev (port 5180)If there is an existing Cypress test for the affected view, run it after the change. Do NOT add new Cypress tests as part of this plan unless explicitly extending coverage — out of scope.
Work in branch feature/merge-project-setting-item (already created in BE repo; create matching branch in FE repo: git checkout -b feature/merge-project-setting-item).
Mirror the BE repo's recent style:
feat: ... for new componentsrefactor: ... for view restructuringchore(i18n): ... for translation key changesdocs: ... for changelogEach commit must be focused — one task = one commit unless explicitly noted.
SystemPicker.vue or DevicePicker.vue (use them as-is via the wrapper)DeviceManage.vue or InformationSystemManage.vue (independent management pages, out of scope)tab_audit_systems, tab_devices, step_audit_systems, step_devices, etc.) — keep them for safety; cleanup is a follow-up PR| File | Action | Responsibility |
|---|---|---|
src/components/grc/AuditScopeSection.vue |
Create | Wrapper that stacks SystemPicker (top) and DevicePicker (bottom). Pure layout + props/emits passthrough. No API calls, no internal state. |
src/views/project/ProjectSettingsView.vue |
Modify | Collapse Tab 2 (Audit Systems) and Tab 4 (Devices) into a single new Tab 2 (Audit Scope) backed by AuditScopeSection. |
src/views/project/ProjectCreateView.vue |
Modify | 5 steps → 4 steps with order: basic / participants / audit-scope / review. Replace step 1 (system) and step 3 (device) with a single audit-scope step. Update review section accordingly. |
src/config/locales/i18n/zh-tw/project-settings.json |
Modify | Add tab_audit_scope, section_audit_scope keys |
src/config/locales/i18n/en/project-settings.json |
Modify | Same as above (English) |
src/config/locales/i18n/zh-tw/project-create.json |
Modify | Add step_audit_scope, audit_scope_subtitle, review_audit_scope keys |
src/config/locales/i18n/en/project-create.json |
Modify | Same as above (English) |
~/Projects/Billows/Audit-Manager/compliance-manager-be/docs/changelog/2026-04-25-audit-scope-merge.md |
Create | Changelog (in BE repo) |
Goal: Confirm clean baseline before changes.
Files: None (verification only)
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git status
git log -1 --onelineExpected: on branch feature/merge-project-setting-item, last commit is 33d592d docs: 新增 audit-scope-merge ..., working tree clean.
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
git status
git checkout -b feature/merge-project-setting-item || git checkout feature/merge-project-setting-item
git statusExpected: clean working tree, on branch feature/merge-project-setting-item.
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
npm run devIn browser, open /project/projects/new and /project/projects/<some-existing-uid>/settings. Verify both load. Note current Tab 2 = Audit Systems and Tab 4 = Devices in Settings; Step 1 = Audit Systems and Step 3 = Devices in Create.
Stop the dev server (Ctrl-C). No commit needed for this task.
Goal: Define all new translation keys before they're referenced from views.
Files:
src/config/locales/i18n/zh-tw/project-settings.jsonsrc/config/locales/i18n/en/project-settings.jsonsrc/config/locales/i18n/zh-tw/project-create.jsonsrc/config/locales/i18n/en/project-create.jsoncd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
grep -n "tab_audit_systems\|tab_devices" src/config/locales/i18n/zh-tw/project-settings.json
grep -n "step_audit_systems\|step_devices\|review_audit_systems\|review_devices" src/config/locales/i18n/zh-tw/project-create.jsonNote the surrounding key naming and JSON nesting (e.g., flat keys vs nested). All new keys must follow the same pattern.
Add these new keys (place near existing tab_* keys, do NOT remove old ones):
"tab_audit_scope": "受評範圍 ({systems} 系統 / {devices} 設備)",
"section_audit_scope": "受評範圍",
"section_audit_scope_desc": "本專案要稽核的資訊系統與設備""tab_audit_scope": "Audit Scope ({systems} systems / {devices} devices)",
"section_audit_scope": "Audit Scope",
"section_audit_scope_desc": "Information systems and devices in scope for this audit"Add these (place near existing step_* keys):
"step_audit_scope": "受評範圍",
"audit_scope_title": "受評範圍",
"audit_scope_subtitle": "選擇本專案要稽核的系統與設備",
"review_audit_scope": "受評範圍",
"review_audit_scope_systems": "資訊系統",
"review_audit_scope_devices": "設備""step_audit_scope": "Audit Scope",
"audit_scope_title": "Audit Scope",
"audit_scope_subtitle": "Select systems and devices for this audit",
"review_audit_scope": "Audit Scope",
"review_audit_scope_systems": "Information Systems",
"review_audit_scope_devices": "Devices"cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
for f in src/config/locales/i18n/zh-tw/project-settings.json \
src/config/locales/i18n/en/project-settings.json \
src/config/locales/i18n/zh-tw/project-create.json \
src/config/locales/i18n/en/project-create.json; do
python3 -c "import json; json.load(open('$f'))" && echo "OK: $f"
doneExpected: 4 lines of OK: .... If any fails, fix the JSON before continuing.
git add src/config/locales/i18n/zh-tw/project-settings.json \
src/config/locales/i18n/en/project-settings.json \
src/config/locales/i18n/zh-tw/project-create.json \
src/config/locales/i18n/en/project-create.json
git commit -m "chore(i18n): 新增 audit-scope-merge 翻譯 key (zh-tw + en)"AuditScopeSection.vue componentGoal: Build the wrapper component that hosts SystemPicker + DevicePicker stacked.
Files:
src/components/grc/AuditScopeSection.vueThe wrapper uses these existing keys for inner picker headers; confirm they're already present:
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
grep -n "section_audit_systems\|section_devices" \
src/config/locales/i18n/zh-tw/project-settings.json \
src/config/locales/i18n/en/project-settings.jsonExpected: 4 matches (2 keys × 2 locales). If any are missing, add them BEFORE proceeding (use the same wording you'd see in current SystemPicker / DevicePicker #header slots).
<script setup lang="ts">
/**
* 受評範圍 section — 包裝 SystemPicker + DevicePicker,stacked 版面
* 用於 ProjectCreateView 和 ProjectSettingsView
*
* 設計:純 layout wrapper。所有 props/emits 透傳給內部 picker。
* 不做 API 呼叫、不存內部狀態。
*/
import { useI18n } from 'vue-i18n'
import SystemPicker from '@/components/grc/SystemPicker.vue'
import DevicePicker from '@/components/grc/DevicePicker.vue'
interface SystemMenuItem {
id: string
name: string
abbreviation: string | null
}
withDefaults(defineProps<{
// System
selectedSystemIds: string[]
selectedSystemDetails: Record<string, any>
infoSystemMenu: SystemMenuItem[]
// Device
selectedDevices: string[]
selectedDeviceDetails: Record<string, any>
deviceMenu: any[]
// Shared
userMenu: any[]
readonly?: boolean
confirmBeforeRemove?: boolean
/** 是否顯示 section 標題(Settings tab 用 false,Create step 用 true) */
showSectionHeader?: boolean
}>(), {
readonly: false,
confirmBeforeRemove: false,
showSectionHeader: false,
})
defineEmits<{
// System
'update:selectedSystemIds': [ids: string[]]
'update:selectedSystemDetails': [details: Record<string, any>]
'update:infoSystemMenu': [menu: SystemMenuItem[]]
'change-systems': []
// Device
'update:selectedDevices': [ids: string[]]
'update:selectedDeviceDetails': [details: Record<string, any>]
'change-devices': []
'device-created': []
'device-detail-saved': []
}>()
const { t } = useI18n()
</script>
<template>
<div class="flex flex-column gap-4">
<!-- Optional section header (used by Create wizard step) -->
<div v-if="showSectionHeader">
<h3 class="m-0">{{ t('lang.project_create.audit_scope_title') }}</h3>
<p class="m-0 mt-2 text-sm" style="color: var(--text-muted);">
{{ t('lang.project_create.audit_scope_subtitle') }}
</p>
</div>
<!-- System block (top) -->
<div class="border-round-lg p-4 surface-section"
style="border: 1px solid var(--surface-border)">
<SystemPicker
:selectedIds="selectedSystemIds"
:systemMenu="infoSystemMenu"
:systemDetails="selectedSystemDetails"
:userMenu="userMenu"
:readonly="readonly"
:confirmBeforeRemove="confirmBeforeRemove"
@update:selectedIds="$emit('update:selectedSystemIds', $event)"
@update:systemMenu="$emit('update:infoSystemMenu', $event)"
@update:systemDetails="$emit('update:selectedSystemDetails', $event)"
@change="$emit('change-systems')"
>
<template #header>
<h4 class="m-0">{{ t('lang.project_settings.section_audit_systems') }}</h4>
</template>
</SystemPicker>
</div>
<!-- Device block (bottom) -->
<div class="border-round-lg p-4 surface-section"
style="border: 1px solid var(--surface-border)">
<DevicePicker
:selectedIds="selectedDevices"
:deviceDetails="selectedDeviceDetails"
:deviceMenu="deviceMenu"
:readonly="readonly"
:confirmBeforeRemove="confirmBeforeRemove"
@update:selectedIds="$emit('update:selectedDevices', $event)"
@update:deviceDetails="$emit('update:selectedDeviceDetails', $event)"
@change="$emit('change-devices')"
@created="$emit('device-created')"
@detailSaved="$emit('device-detail-saved')"
>
<template #header>
<h4 class="m-0">{{ t('lang.project_settings.section_devices') }}</h4>
</template>
</DevicePicker>
</div>
</div>
</template>
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
ls -la src/components/grc/AuditScopeSection.vueRun dev server briefly to check Vue compilation:
npm run devThe dev server should start without errors. AuditScopeSection isn't used yet, so no visual to check. Stop the server.
git add src/components/grc/AuditScopeSection.vue
git commit -m "feat(grc): 新增 AuditScopeSection 元件包裝 System+Device picker"ProjectSettingsView.vue — collapse 4 tabs to 3Goal: Replace Tab 2 (Audit Systems) and Tab 4 (Devices) with a single new Tab 2 (Audit Scope).
Files:
src/views/project/ProjectSettingsView.vueIn the <script setup> section, find these lines:
import SystemPicker from '@/components/grc/SystemPicker.vue'
import DevicePicker from '@/components/grc/DevicePicker.vue'Replace with:
import AuditScopeSection from '@/components/grc/AuditScopeSection.vue'(Remove both old picker imports — the wrapper handles them internally.)
In the <template>, locate the existing Tab 2 (Audit Systems, line ~254):
<!-- Tab 2: Audit Systems -->
<TabPanel :header="t('lang.project_settings.tab_audit_systems', { count: selectedSystemIds.length })">
<div class="border-round-xl p-5 shadow-3 surface-card"
style="border: 1px solid var(--surface-border)">
<SystemPicker v-model:selectedIds="selectedSystemIds"
v-model:systemMenu="infoSystemMenu"
v-model:systemDetails="selectedSystemDetails"
:userMenu="userMenu ?? []"
:readonly="!canEdit"
:confirmBeforeRemove="true"
@change="syncProjectSystems">
<template #header>...</template>
</SystemPicker>
</div>
</TabPanel>
Replace with the new merged Tab 2:
<!-- Tab 2: Audit Scope (merged Systems + Devices) -->
<TabPanel :header="t('lang.project_settings.tab_audit_scope', {
systems: selectedSystemIds.length,
devices: selectedDevices.length
})">
<div class="border-round-xl p-5 shadow-3 surface-card"
style="border: 1px solid var(--surface-border)">
<AuditScopeSection
v-model:selectedSystemIds="selectedSystemIds"
v-model:selectedSystemDetails="selectedSystemDetails"
v-model:infoSystemMenu="infoSystemMenu"
v-model:selectedDevices="selectedDevices"
v-model:selectedDeviceDetails="selectedDeviceDetails"
:deviceMenu="deviceMenu ?? []"
:userMenu="userMenu ?? []"
:readonly="!canEdit"
:confirmBeforeRemove="true"
@change-systems="syncProjectSystems"
@change-devices="syncProjectDevices"
@device-created="menuStore.fetchDeviceMenu()"
@device-detail-saved="fetchProject(); menuStore.fetchDeviceMenu()"
/>
</div>
</TabPanel>
Locate the existing Tab 4 (line ~291 in the original file):
<!-- Tab 4: Devices -->
<TabPanel :header="t('lang.project_settings.tab_devices', { count: selectedDevices.length })">
<div class="border-round-xl p-5 shadow-3 surface-card"
style="border: 1px solid var(--surface-border)">
<DevicePicker v-model:selectedIds="selectedDevices" .../>
</div>
</TabPanel>
Delete the entire <TabPanel> block (Tab 3 Participants stays unchanged and becomes the new Tab 3).
The TabView should now have exactly 3 panels in this order:
npm run devOpen /project/projects/<existing-uid>/settings (use a real project UID from your dev environment).
Verify:
Stop the dev server.
git add src/views/project/ProjectSettingsView.vue
git commit -m "refactor(project-settings): 合併稽核系統與設備 tab 為「受評範圍」"ProjectCreateView.vue — 5 steps to 4 steps with reorderGoal: Reorder steps to (basic / participants / audit-scope / review). Replace step 1 (system) and step 3 (device) with a single audit-scope step. Update review section.
Files:
src/views/project/ProjectCreateView.vueFind these lines:
import SystemPicker from '@/components/grc/SystemPicker.vue'
import DevicePicker from '@/components/grc/DevicePicker.vue'Replace with:
import AuditScopeSection from '@/components/grc/AuditScopeSection.vue'Find the existing stepsItems computed (line ~38):
const stepsItems = computed(() => [
{ label: t('lang.project_create.step_basic_info') },
{ label: t('lang.project_create.step_audit_systems') },
{ label: t('lang.project_create.step_participants_depts') },
{ label: t('lang.project_create.step_devices') },
{ label: t('lang.project_create.step_review') },
])Replace with:
const stepsItems = computed(() => [
{ label: t('lang.project_create.step_basic_info') },
{ label: t('lang.project_create.step_participants_depts') },
{ label: t('lang.project_create.step_audit_scope') },
{ label: t('lang.project_create.step_review') },
])New step indices:
⚠️ Critical: do all 4 of these changes in a single edit pass. Mid-state (e.g., two blocks both keyed currentStep === 2) will break the wizard.
The template currently has 5 v-if / v-else-if blocks for currentStep === 0..4. Restructure to 4 blocks for currentStep === 0..3 as follows:
| Old block | Old condition | Action | New condition |
|---|---|---|---|
| Step 0 Basic Info | v-if="currentStep === 0" |
Keep as-is | v-if="currentStep === 0" |
| Step 1 Audit Systems | v-else-if="currentStep === 1" |
DELETE entirely | (gone) |
| Step 2 Participants | v-else-if="currentStep === 2" |
Renumber to 1 | v-else-if="currentStep === 1" |
| (NEW Audit Scope) | (none) | INSERT new block here | v-else-if="currentStep === 2" |
| Step 3 Devices | v-else-if="currentStep === 3" |
DELETE entirely | (gone) |
| Step 4 Review | v-else-if="currentStep === 4" |
Renumber to 3 | v-else-if="currentStep === 3" |
Recommended edit order to avoid duplicate keys mid-edit:
The new Audit Scope block to insert:
<!-- Step 2: Audit Scope (merged Systems + Devices) -->
<div v-else-if="currentStep === 2" class="flex flex-column gap-5">
<AuditScopeSection
v-model:selectedSystemIds="selectedSystemIds"
v-model:selectedSystemDetails="selectedSystemDetails"
v-model:infoSystemMenu="infoSystemMenu"
v-model:selectedDevices="selectedDevices"
v-model:selectedDeviceDetails="selectedDeviceDetails"
:deviceMenu="deviceMenu ?? []"
:userMenu="userMenu ?? []"
:showSectionHeader="true"
@device-created="menuStore.fetchDeviceMenu()"
/>
</div>
(Note: in Create wizard, change-systems / change-devices events are not wired — there's no real-time sync during create; everything saves via handleCreate at the final step.)
Step 3 (Review): confirmed renumbered to v-else-if="currentStep === 3" (per the table above).
Inside the Step 3 (review) block, locate these two existing sub-sections:
<!-- Audit Systems --> ... (currentStep = 1 edit link)
<!-- Devices --> ... (currentStep = 3 edit link)
Replace BOTH with this single combined block (place it AFTER the Participants block to match the new step order: basic → participants → audit-scope):
<!-- Audit Scope (merged Systems + Devices) -->
<div class="p-4 border-round-lg surface-section" style="border: 1px solid var(--surface-border)">
<div class="flex align-items-center justify-content-between mb-3">
<span class="font-medium">{{ t('lang.project_create.review_audit_scope') }}</span>
<button class="cursor-pointer border-none bg-transparent text-primary"
@click="currentStep = 2">{{ t('lang.project_create.btn_edit') }}</button>
</div>
<!-- Systems sub-block -->
<div class="mb-4">
<div class="text-sm text-color-secondary mb-2">{{ t('lang.project_create.review_audit_scope_systems') }}</div>
<div v-if="selectedSystemIds.length > 0" class="flex flex-column gap-2">
<div v-for="sysId in selectedSystemIds" :key="sysId"
class="flex align-items-center justify-content-between px-3 py-2 border-round surface-card"
style="border: 1px solid var(--surface-border)">
<span class="font-medium">{{ infoSystemMenu.find(s => s.id === sysId)?.name ?? sysId }}</span>
<span v-if="infoSystemMenu.find(s => s.id === sysId)?.abbreviation"
class="text-color-secondary text-sm">
{{ infoSystemMenu.find(s => s.id === sysId)?.abbreviation }}
</span>
</div>
</div>
<span v-else class="text-color-secondary">{{ t('lang.project_create.review_none') }}</span>
</div>
<!-- Devices sub-block -->
<div>
<div class="text-sm text-color-secondary mb-2">{{ t('lang.project_create.review_audit_scope_devices') }}</div>
<div v-if="selectedDevices.length > 0" class="flex flex-wrap gap-2">
<span v-for="devId in selectedDevices" :key="devId"
class="inline-flex align-items-center gap-1 px-2 py-1 border-round surface-card"
style="border: 1px solid var(--surface-border)">
<i class="pi pi-desktop"></i>
{{ (deviceMenu?.find((d: any) => d.uid === devId)?.name) ?? devId }}
</span>
</div>
<span v-else class="text-color-secondary">{{ t('lang.project_create.review_none') }}</span>
</div>
</div>
Also: update the Participants edit link (still in review block) — its @click="currentStep = 2" was pointing at old step 2 (participants). Now participants are at step 1, so change to @click="currentStep = 1".
Final review block sub-sections in this order:
@click="currentStep = 0")@click="currentStep = 1")@click="currentStep = 2")Look at handleNext (around line 108):
async function handleNext() {
if (currentStep.value === 0) {
const valid = await v$.value.$validate()
if (!valid) return
}
currentStep.value++
}This is fine — only step 0 has validation. New step 0 is still basic info. No change needed.
Look at handleBack (around line 116):
function handleBack() {
if (currentStep.value === 0) {
router.push({ name: 'project-list' })
} else {
currentStep.value--
if (currentStep.value === 0) v$.value.$reset()
}
}No change needed — still resets validation when returning to step 0.
Look at handleCreate (around line 125). It builds audit_systems from selectedSystemIds and devices from selectedDevices. This is unchanged because the underlying refs are unchanged — only the UI grouping moved. No code change needed here.
npm run devOpen /project/projects/new. Verify:
Empty-state check:
Stop the dev server.
git add src/views/project/ProjectCreateView.vue
git commit -m "refactor(project-create): 4 步驟重構 + 合併「受評範圍」步驟"Goal: Confirm unrelated views still work — especially anything that consumes Device or InformationSystem data outside the changed views.
Files: None (verification only)
npm run devOpen /project/projects/<existing-uid>/auditor-overview. Verify:
Open any project's job execution view, click into a job that has devices attached. Verify:
Open /device/device-manage. Verify:
Open /information-system/manage. Verify:
In Settings or anywhere with the language switcher, toggle between zh-tw and en. Verify:
Stop the dev server. No commit needed.
Goal: Confirm we didn't leave any orphaned/unused references to old keys, and check if old keys can be safely removed in a follow-up. This task does NOT delete keys — just produces a report.
Files: None (audit only)
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
echo "=== tab_audit_systems references ==="
grep -rn "tab_audit_systems" src/ --include="*.vue" --include="*.ts" --include="*.js"
echo "=== tab_devices references ==="
grep -rn "tab_devices" src/ --include="*.vue" --include="*.ts" --include="*.js"
echo "=== step_audit_systems references ==="
grep -rn "step_audit_systems" src/ --include="*.vue" --include="*.ts" --include="*.js"
echo "=== step_devices references ==="
grep -rn "step_devices" src/ --include="*.vue" --include="*.ts" --include="*.js"
echo "=== review_audit_systems references ==="
grep -rn "review_audit_systems" src/ --include="*.vue" --include="*.ts" --include="*.js"
echo "=== review_devices references ==="
grep -rn "review_devices" src/ --include="*.vue" --include="*.ts" --include="*.js"Expected: only the i18n JSON files themselves should match (no .vue / .ts / .js references). If any code file still uses these keys, that's a bug — report and fix.
echo "=== tab_audit_scope ==="
grep -rn "tab_audit_scope" src/
echo "=== step_audit_scope ==="
grep -rn "step_audit_scope" src/
echo "=== review_audit_scope ==="
grep -rn "review_audit_scope" src/Expected: each new key appears in 2 locale files + at least one Vue file.
If old keys are confirmed orphaned, note in the PR description that they can be removed in a follow-up cleanup PR. Do NOT remove them in this PR — keep for safety.
No commit needed.
Goal: Document the change per CLAUDE.md convention.
Files:
~/Projects/Billows/Audit-Manager/compliance-manager-be/docs/changelog/2026-04-25-audit-scope-merge.mdcd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git statusShould be on feature/merge-project-setting-item, clean working tree.
Create docs/changelog/2026-04-25-audit-scope-merge.md with this content:
# 2026-04-25 — 專案受評範圍合併(Audit Scope Consolidation)
## 需求說明
ProjectCreate 與 ProjectSettings 兩個畫面內,「資訊系統」與「設備」原本分屬獨立 step / tab。從 user 視角兩者都屬「本專案要稽核的範圍」之組成元件,分散造成認知負擔。
本次將兩者在 UI 層合併為單一 section「受評範圍 (Audit Scope)」。BE / schema 完全不動,作為將來 OSCAL SSP 匯出 / 跨模組引用的階段性踏腳石。
設計文件:`docs/features/FR-017-2604-audit-scope-merge/design.md`
實作計畫:`docs/features/FR-017-2604-audit-scope-merge/implementation-plan.md`
## 變更範圍
### Frontend (compliance-manager-fe)
| 檔案 | 動作 |
|------|------|
| `src/components/grc/AuditScopeSection.vue` | 新增 — 包裝 SystemPicker + DevicePicker,stacked 版面 |
| `src/views/project/ProjectSettingsView.vue` | 修改 — 4 tabs → 3 tabs(合併 Tab 2 系統 + Tab 4 設備 為新 Tab 2 受評範圍) |
| `src/views/project/ProjectCreateView.vue` | 修改 — 5 steps → 4 steps,順序改為:基本資訊 → 參與者 → 受評範圍 → 確認 |
| `src/config/locales/i18n/zh-tw/project-settings.json` | 修改 — 新增 `tab_audit_scope` 等 key(保留舊 key) |
| `src/config/locales/i18n/en/project-settings.json` | 修改 — 同上(英文) |
| `src/config/locales/i18n/zh-tw/project-create.json` | 修改 — 新增 `step_audit_scope`、`review_audit_scope` 等 key |
| `src/config/locales/i18n/en/project-create.json` | 修改 — 同上(英文) |
### Backend
無變更。
## API 變更
無。`PUT /api/1.0/grc-project/<id>` 仍以 `audit_systems` + `devices` 兩個 key 各自接收,FE 仍各自呼叫 sync。
## 行為差異
| 情境 | 之前 | 現在 |
|------|------|------|
| ProjectSettings tab 數 | 4(基本/系統/參與者/設備) | 3(基本/受評範圍/參與者) |
| ProjectSettings 受評範圍 tab header | n/a | `受評範圍 (3 系統 / 5 設備)` |
| ProjectCreate step 數 | 5 | 4 |
| ProjectCreate step 順序 | 基本→系統→參與者→設備→確認 | 基本→參與者→受評範圍→確認 |
| ProjectCreate 確認頁區段 | 4(基本/系統/參與者/設備) | 3(基本/參與者/受評範圍)|
| 既有 SystemPicker / DevicePicker 元件 | — | 完全不變,由 AuditScopeSection 包裝 |
| 存檔 API call | `PUT` 各自 sync | `PUT` 各自 sync(不變) |
## Phase 2 預留(不在本次)
- OSCAL SSP exporter(可選 a 純讀現有表 / b 加 mapping 小表 / c 加 FK)
- ComponentService 抽象(給 control-implementation / POA&M / evidence ref 引用)
- 樹狀 ComponentPicker(系統下層展示設備)
## 不做的事
- 不動 BE / schema
- 不動 jedi-device 套件 / jedi_information_system 模組
- 不動 SystemPicker / DevicePicker 元件本身
- 不動 DeviceManage / InformationSystemManage 獨立管理頁
- 不動 job_execution_device_mapping、evidence、device_view 聚合
- 不刪除舊的 i18n key(之後 PR 再清理)
## 參考
- 設計文件:`docs/features/FR-017-2604-audit-scope-merge/design.md`
- 實作計畫:`docs/features/FR-017-2604-audit-scope-merge/implementation-plan.md`git add docs/changelog/2026-04-25-audit-scope-merge.md
git commit -m "docs(changelog): audit-scope-merge 變更紀錄"Goal: Final sanity pass before declaring done.
Files: None (verification only)
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
git log --oneline feature/merge-project-setting-item ^main 2>/dev/null || git log --oneline -10Expected commits (in order):
chore(i18n): 新增 audit-scope-merge 翻譯 key (zh-tw + en)feat(grc): 新增 AuditScopeSection 元件包裝 System+Device pickerrefactor(project-settings): 合併稽核系統與設備 tab 為「受評範圍」refactor(project-create): 4 步驟重構 + 合併「受評範圍」步驟cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git log --oneline feature/merge-project-setting-item ^main 2>/dev/null || git log --oneline -5Expected commits:
docs: 新增 audit-scope-merge 功能設計文件 + 加 .superpowers/ 到 gitignoredocs(changelog): audit-scope-merge 變更紀錄Run dev server one more time. Walk through:
This implementation is complete. The implementer should report:
feature/merge-project-setting-itemPR descriptions should reference docs/features/FR-017-2604-audit-scope-merge/design.md and docs/changelog/2026-04-25-audit-scope-merge.md.
If you need a clean dev project to test with: log in as blsit / Billows@123! (tenant_id=102, org_unit_id=99). Use any existing project UID for Settings tests, or create a new one for the Create flow.
update:selectedSystemIds not firing: check the wrapper passes through the right event name (it's update:selectedIds from inner SystemPicker → re-emitted as update:selectedSystemIds from wrapper)selectedDevices through v-model; verify both :value binding and update: event are correctly wired