Manage Device Access by Site
Visual basis: vortex-1.0 — verify behavior / copy / structure, not pixel style.
Why we're building this
VORTEX grants device access per user, per device — an Admin sets which roles a user holds on the org's devices. Two structural shifts change how: device grouping expands from a single-level camera group to a Sites tree (a Site with up to four Area levels), and every device now belongs to a Site.
So access is managed against that Site tree: check the sites or areas a user should keep, set a role, and review before submitting.
What value it brings
Manage device access by Site — from whole org to a single device.
- Batch — the whole org, or selected sites / areas
- Site-first by design — the Site / area is the primary unit; single-device access sits one level down, toward VORTEX's future site permissions
- Safe — every change, including removals, is reviewable before submit
對話框結構與進入點
Manage Device Access by Site 是一個模態對話框,讓管理者(Admin / Owner)設定單一使用者對整個組織裝置的存取角色。固定尺寸 1116 × 747,分三塊。
進入點與編輯模型 Entry & edit model
| 項目 | 規則 |
|---|---|
| 進入點 | Portal › Users › 某使用者的 detail page › Manage device access |
| 編輯權責 | 外層 user detail page 唯讀瀏覽現況;此對話框是唯一的編輯介面 |
| 開啟時 | 預載目前有效存取:自己直屬裝置保有存取的節點 pre-check(兩態 per-node,見 Section 02);值與外層一致 |
| 送出時 | 只送出本 session 的 diff,不是整份重寫 |
三個區塊 Regions
| 區塊 | 高 | 內容 |
|---|---|---|
| Header | 70px | 標題「Manage Device Access by Site」 + 右上關閉 |
| Body | 607px | 四種 mode 之一(見下);padding 8px 40px、雙欄間距 40px |
| Footer | 70px | 右下「Save changes」 + 「Cancel」(各 150px);drill-in mode 隱藏 footer |
Body 的四種 mode Four modes
| Mode | 觸發 | 版面 |
|---|---|---|
| 主 mode | 開啟預設 | 左 280px sidemenu + 右 716px 面板 |
| All sites 批次 | 點 sidemenu「All sites」的 checkbox——未勾或半選 → 勾選皆進批次(標準三態循環,見 Section 04) | sidemenu 不變 + 右單一 batch row(見 Section 04) |
| Drill-in | 點右面板某列的 chevron > |
body 換成單欄裝置清單(見 Section 05) |
| Review Changes | 點右面板「N changes」 | body 換成單欄 diff 清單(見 Section 07) |
兩種起始狀態 Initial states
| 起始態 | 判定 | 表現 |
|---|---|---|
| Empty | 從未設定過 | 右面板空狀態「No Device Access」 + 「Select sites from the side menu to manage device access.」;裝置類型 Tab 列仍保留,插圖置中於 Tab 列以下(見 Section 03) |
| Has value | 已有設定 | sidemenu 依各節點自身直屬裝置是否保有存取 pre-check(兩態 per-node);每列顯示目前角色,與外層一致 |
- 明確設為 No Access 算「已設定」:對話框以 Has value 起始;該節點依兩態推導為未勾、不在右面板列出(見 Section 02)。
貫穿全對話框的原則 Key principles
- checkbox = 該節點自己的直屬裝置是否保有存取(兩態 per-node、隨裝置狀態即時推導,見 Section 02)。不是「正在編輯誰」,也不是 subtree 聚合。
- 站點層 dropdown 沒有 No Access,移除存取靠 sidemenu uncheck(見 Section 03);No Access 只在裝置層(drill-in)可選。
- 三種破壞性編輯不用 confirm 擋:uncheck 保有存取的 site、drill-in 把直屬裝置全設 No Access、Multiple roles 壓平——一律記進 diff、送出前可檢視(見 Section 07)。例外(會即時跳 modal):撤銷牽動 linked 裝置的 link-confirm(見 Section 06)、All sites 的套用 confirm 與取消時的 Reset confirm(見 Section 04)。
- 變更清單必須能重建最終狀態:從已儲存狀態套用清單每一筆(範圍重疊取最具體者),須等於真正送出的結果;粗粒度那筆之後的細粒度例外一定單獨列出(見 Section 07)。
- 組裝既有 pattern:sidemenu 站點樹沿用 Design Pattern: Tree View;search / filter 沿用 Design Pattern: Filter Combo。
驗收條件 QA · Given-When-Then
- Given 從 user detail page 開啟,Then 以編輯模式開啟並預載現況;外層維持唯讀。
- Given 使用者已有設定,Then 自身直屬裝置保有存取的節點 pre-check(兩態,無半選;祖先不因子孫連動),各列角色與外層一致。
- Given 使用者從未設定,Then 顯示 Empty 空狀態「No Device Access」。
- Given 某使用者僅有「明確設為 No Access」的節點,Then 以 Has value 起始態開啟(不顯示 Empty),該節點 checkbox 未勾、不在右面板列出。
側邊站點樹
左側 280px 的 sidemenu 是選取「要管理哪些 site / area」的地方。由上到下:搜尋、選取計數、All sites 列、站點樹(含 checkbox)。
整體畫面見 Section 01 主 mode 截圖。站點樹本身沿用 Design Pattern: Tree View。
搜尋 Search
沿用 Filter Combo 的搜尋規則:
- 觸發門檻沿用 Filter Combo Section 05,此處不重列數值。
- 命中節點與其所有祖先顯示且可選;無關分支隱藏。
- 命中文字 highlight(
primary底 + 白字)。 - 無結果顯示
No Matches Found空狀態(文案與版面見 Design Pattern: Tree View §04)。 - 清除搜尋 → 還原搜尋前的展開狀態(此條權威是 Tree View Section 04,不是 Filter Combo)。
選取計數 Selection count(40px)
搜尋框與 All sites 列之間一行,primary/default 色、14px Semibold:
- 格式
N sites, M areas selected——其一為 0 則略去該段(只有 site 時為2 sites selected),單複數依數量(1 site/1 area)。 - 零選取為
0 selected,色改text/03。 - 算的是 sidemenu 上勾選的節點數(level 1 = sites、level 2–5 = areas),不是「有權限的節點數」——剛勾選還沒配角色的也算。
- 與裝置類型 Tab 無關。
與 Design Pattern: Filter Combo 的 site 計數同一套規則,含
selected後綴。
All sites 列 All sites row
- checkbox(未勾 / 勾選 / 半選 三態):進入 All sites 批次 mode 的唯一入口(見 Section 04)。未勾或半選 → 點 → 勾選進入批次;勾選 → 點 → 未勾一律先跳 Reset confirm。
- 列名稱不可點——checkbox 同時管 mode 與 selection,label 不參與。
- 右側雙箭頭 = Collapse all(收合到 level 1);再點還原全展開。
- 批次 mode 中整列有選中底色作為指示,樣式沿用站點樹的 Active 慣例(
surface/04底 + 8px 圓角,見 Design Pattern: Tree View)。
站點樹 checkbox 變體 Tree with checkbox
在 Tree View 既有規格上,每個節點於 chevron 後加一個 20×20 checkbox:
| 點擊目標 | 結果 |
|---|---|
| checkbox | 只動 selection,不影響展開;批次 mode 中也照常 toggle——selection 因此不再是全選,批次 mode 隨之自動退出(見 Section 04) |
| chevron | 只 toggle 展開,不動 selection |
| 列名稱 | 兩種 mode 都是 no-op |
- 每一列都顯示 chevron、都可展開,含當下沒有子項的 Area——箭頭規則依節點種類而非依當下有無子項,Site / Area 是容器種類(比照資料夾邏輯);權威為 Design Pattern: Tree View,此處不重列。
- 展開沒有子項的 Area 時不顯示任何列,直接接續下一個同層節點——空提示行(
No cameras/No floor plans)是 profile 層級的差異,site tree 不顯示;權威為該 deck Section 01 的邊界表。 - 預設全部展開到最深層(含 Empty 與 Has value)。
checkbox 兩態的意義 Two-state, per-node
站點樹 checkbox 採兩態 per-node(與 Design Pattern: Filter Combo 的 Sites 選取同一套規則),每個只回答一個問題:這個節點自己的直屬裝置是否保有存取。沒有半選,也沒有父子聚合語意——整個 sidemenu 唯一的三態是 All sites 列,它是批次 mode 的開關 + 全組織存取的摘要,不屬於樹的選取元件(見 Section 04)。
| 狀態 | 意義 |
|---|---|
| 勾選 | 該節點自己的直屬裝置至少一台保有存取(任一非 No Access 角色);或剛勾選、角色待指派(右面板顯示「Select role」) |
| 未勾 | 該節點直屬裝置全部 No Access,或沒有直屬裝置。不代表任何 subtree 資訊——子孫各自獨立判斷 |
| 操作 | 結果 |
|---|---|
| 未勾 → 點 → 勾選 | 該節點 + 整 subtree 全部勾選(cascade;角色待指派) |
| 勾選 → 點 → 未勾 | 該節點 + 整 subtree 全部設為 No Access(對稱 cascade,進 diff) |
| 子層獨立勾 / 取消 | 只影響自己;祖先永不自動連動(勾滿全部子層,父層也不會自動打勾) |
checkbox 即時反映裝置狀態 Live derivation
checkbox 顯示 = 「即時推導」或「勾選待配」二者之一,隨裝置資料更新:
- drill-in 把某節點直屬裝置全部改為 No Access → 該節點自動變未勾,右面板該列消失。這是顯示層推導,不產生額外 diff(裝置層變更已各自記錄)。
- 之後任何操作(含在 Review Changes 改回角色)讓任一直屬裝置恢復存取 → 自動變回勾選。
- 只清一部分 → 維持勾選,該列聚合顯示「Multiple roles」(見 Section 03)。
- 「剛勾選、還沒配角色」維持勾選直到被 uncheck;重開對話框時依裝置現況重新推導。
diff 何時產生 When a diff is recorded
- 未勾 → 勾選:不記 diff——右面板該列 dropdown 顯示「Select role」placeholder,選了角色才算 1 筆。
- 勾選 → 未勾:每個裝置類型至多 1 筆,兩條件都要成立:(1) 該類型在已儲存狀態下有非 No Access 裝置;(2) 本次 session 還沒有涵蓋此範圍的粗粒度移除(祖先 uncheck 或 All sites 重設)。已經有的話不記第二筆——不是基準被改寫,而是那筆較大範圍的變更已經涵蓋這次轉換。剛勾選還沒配角色就 uncheck、或已儲存狀態即全 No Access 的 subtree 同樣不記 diff。subtree cascade 不獨立列。
- uncheck 會一併清算 subtree 內本次 session 已記錄的節點 / 裝置變更。diff 帳本永遠與畫面一致,不會出現互相抵銷的兩筆。
- subtree 內裝置連動到其他 site / area 的裝置且會一併失去存取 → 額外跳 link-confirm(見 Section 06)。
- uncheck 之後再對範圍內節點賦權,一定會另記一筆——即使選的角色剛好是已儲存原值,因為記帳基準是 uncheck 之後的有效狀態(
No Access)。反之已被涵蓋的移除不重複記(見 Section 07)。
驗收條件 QA · Given-When-Then
- Given 勾選一個原本未勾的節點,Then 該節點與 subtree 加入 selection,但不改任何角色、不記 diff;右面板該列顯示「Select role」。
- Given 勾滿某父層的全部子層,Then 父層 checkbox 維持原狀不自動打勾(per-node,祖先永不連動)。
- Given uncheck 一個保有存取的 site,Then 整 subtree 設為 No Access 並記進 diff(不跳 blocking confirm;牽動 linked 裝置才跳 link-confirm);該 subtree 內本次已記的變更一併清算。
- Given 勾選節點、配了角色後再 uncheck 它(初始為 Empty),Then N changes 歸零、Save changes 停用。
- Given drill-in 把某節點直屬裝置全部設為 No Access 後返回,Then 該節點 checkbox 自動變未勾、右面板該列消失,且沒有額外的 uncheck diff。
- Given 搜尋輸入 2 字以上,Then 命中節點與祖先顯示、highlight,無關分支隱藏。
- Given 勾選 2 個 site 與 10 個 area,Then 計數列顯示
2 sites, 10 areas selected;全部取消後顯示0 selected。 - Given 一個沒有子項的 Area,Then 仍顯示 chevron 且可點(權威為 Tree View deck);展開後不顯示任何列,直接接續下一個同層節點。
- Given uncheck 一個 site 後,再把該 site 底下某個 area 的角色設回它原本已儲存的角色,Then 變更清單仍列出這一筆——基準是 uncheck 之後的有效狀態(No Access),不是原始儲存值。
右側面板(主 mode)
右側 716px 是設定角色的地方。由上到下:標題列、批次列、每個被選節點各一列。
標題列 Header row(40px)
沒有標題文字,只有兩個元素:
- 左:裝置類型 Tab(
Camera/Other devices,見下節)。 - 右:
N changes— 只在 N > 0 時顯示,點下去進 Review Changes(見 Section 07);單數為1 change。
選取計數在 sidemenu,不在此列(見 Section 02)。Empty 狀態下這一列仍保留——空狀態插圖置中於 Tab 列以下,不會連 Tab 一起被取代。
裝置類型 Tab Device type tabs
視覺與互動沿用 filter-combo 原型的 tab item 元件,樣式逐字取自該原型。Design Pattern: Filter Combo 沒有 tab item 的規格章節——該元件的權威在 filter-combo 的原型與 design doc,不在 deck 裡。本節不另定義樣式:
- 兩個固定 Tab:
Camera(預設)/Other devices,直接點擊即切換,不展開選單。 - 後者是 UI 分組標籤,不是裝置種類;hover 出 tooltip:
Other devices include NVR, Bridge, Network Speaker, PoE Switch, and VSS.。裝置種類的列舉字串只出現在此 tooltip——Review Changes 的Device type欄沿用同一個短標籤但不加 hover 說明(見 Section 07)。 - 切換時每列 dropdown 重算「該節點直屬該類型裝置的聚合角色」;顯示哪些列由 selection 決定,與類型無關。
- sidemenu 的選取計數與 Tab 無關。
批次列 Batch row(60px)
- 「Apply to all selected」 + 一個 200×32 dropdown。
- 選一個角色 → 套用到目前面板顯示的所有 site / area 列直屬的該類型裝置(每列各記 1 筆 diff)。
- 範圍只限顯示中的 sites,所以不需要 confirm modal——與 All sites 全 org 批次不同(見 Section 04)。
每個 site / area 列 Site/Area row(60px)
| 元件 | 規格 |
|---|---|
| 名稱 | 完整路徑 breadcrumb(Site > … > 該層),套用 Design Pattern: Sites Breadcrumbs 的漸進式截斷規則,見下節 |
| Dropdown | 200×32,顯示該節點直屬該類型裝置的聚合角色 |
Chevron > |
進入 drill-in(見 Section 05) |
顯示哪些列 = sidemenu 上所有勾選節點,展平、按 sidemenu 順序(site 與 area 穿插,不分組)。drill-in 把某列直屬裝置全設 No Access 時,該節點自動變未勾、該列隨之消失(見 Section 02)。
名稱為 breadcrumb Full path, progressively truncated
列名顯示完整路徑而非只有該層名稱——不同分支常有同名節點(Floor 1、Lobby),而這一列就是實際指派權限的地方,挑錯節點的代價最高。
- 截斷規則、優先序、折疊符畫法一律以 Sites Breadcrumbs 為準,此處只記錄套用範圍與差異。
- 連續被折疊的層合併成單一折疊符,不是每層一個。
- hover breadcrumb 任一處(含各層、分隔符
>、片段之間的空隙、該區域右側留白)→ 顯示完整路徑 tooltip,內容一律是整條路徑;完全沒被縮寫時不顯示。此為 Sites Breadcrumbs 的共用規則(該 deck Section 01),非本對話框的放寬。 - Review Changes 內有兩處刻意不套 breadcrumb(維持只顯示最末層 + hover 看路徑):
Site / Area欄與 device 群組的 sub-header(見 Section 07)。
變更不在列上做視覺標示 No per-row change mark
右面板不對已變更的列做任何顏色標示。要知道動過哪幾列,入口是 N changes 與 Review Changes 頁(見 Section 07)。
裁決(2026-09-10):不做這個標示。 唯一可承載標示的元件是列上的 role select,而平台其他地方的 select 不帶「已變更」這類狀態色;為單一畫面新增一種 select 狀態,代價高於便利,而
N changes與 Review Changes 已在提供這個資訊。曾實作過的方案是把該列 select 底色由
--color-surface-01換成--color-surface-17(key screen9206:136424/12790:36829與原型皆已撤除)。上游 OpenSpecSession change visibility原以 SHALL 要求Changed rows that are visible SHALL be color-highlighted,已依此裁決改寫;D5亦由「colour + Review Changes 兩層」改為只有 Review Changes 一層。
dropdown 的角色選項 Role options
| filter | 選項 |
|---|---|
| Camera | Viewer / Supervisor / 各 Custom role(N 個,01 / 02 / 03… 僅示意) |
| Other devices | Viewer / Supervisor |
站點層沒有 No Access——移除存取靠 sidemenu uncheck(見 Section 02);No Access 只在裝置層(drill-in)可選。只有 Camera 有 Custom role。
已持有 custom role 的非 Camera 裝置(舊資料)保留原角色,選項不變;聚合規則見下表 Multiple roles(Patrick、Amy,2026-10-15;決策紀錄 §3)。
三種特殊顯示 Aggregate display
| 顯示值 | 條件 | 附帶規則 |
|---|---|---|
| Select role(placeholder,灰字) | 該節點直屬該類型裝置全為 No Access(典型:未勾 → 勾選後還沒選角色) | 站點層 dropdown 永不顯示「No Access」字樣 |
| Multiple roles | 該節點直屬該類型裝置角色不一致,含「部分 No Access + 部分有角色」的混合;或直屬 Other devices 有任何一台持有 custom role(舊資料,即使全部是同一個) | 選一個具體值會把該節點直屬裝置壓平成新角色,不遞迴子層 area,記 1 筆 diff |
—(em-dash,disabled) |
該節點直屬沒有該類型裝置(null 聚合,別於 No Access 與 Multiple roles) |
dropdown 停用不可開,chevron 仍可 drill-in;批次列同樣停用。hover 停用的 dropdown 顯示原因:Camera Tab 為 No cameras here.、Other devices Tab 為 No other devices here.(與 drill-in 的 Area header 共用同一份實作,見 Section 05) |
批次列刻意不做這個 tooltip——它停用的原因是「目前顯示的所有列都沒有該類型裝置」,語意與單一節點不同,要描述它得引進「selected sites」這個新文案概念;發生率也低。刻意取捨,不是遺漏。
Empty 狀態 Empty
sidemenu 全未勾時,右面板換成空狀態:「No Device Access」 + 「Select sites from the side menu to manage device access.」。
Tab 列仍保留——只有 Tab 列以下被取代,插圖與文字垂直居中;Tab 是面板的持續控制項,不是內容。此時 sidemenu 計數列顯示 0 selected(見 Section 02)。
驗收條件 QA · Given-When-Then
- Given 開啟站點層 dropdown,Then 選項為 Viewer / Supervisor( + Camera 才有 Custom role),沒有 No Access。
- Given 某節點直屬裝置角色不一致(含部分 No Access 的混合),Then 顯示 Multiple roles;選具體值只壓平該節點直屬裝置,不動子層 area。
- Given
Other devicesTab,某節點直屬裝置有任何一台持有 custom role(即使全部是同一個),Then 該列顯示 Multiple roles,選項仍只有 Viewer / Supervisor。 - Given 剛勾選、尚未配角色的節點,Then 該列 dropdown 顯示「Select role」placeholder,永不顯示「No Access」。
- Given 某節點沒有該類型裝置,Then dropdown 為
—disabled 但仍可 drill-in;hover 顯示No cameras here.(Camera Tab)或No other devices here.(Other devices Tab)。 - Given hover 批次列的停用 dropdown,Then 不顯示 tooltip(刻意不做)。
- Given 切換裝置類型 Tab,Then 每列 dropdown 依新類型重算 enabled / disabled 與聚合角色,顯示哪些列不變。
- Given hover
Other devicesTab,Then 顯示Other devices include NVR, Bridge, Network Speaker, PoE Switch, and VSS. - Given sidemenu 沒有任何勾選,Then 右面板顯示空狀態,但裝置類型 Tab 列仍在、仍可切換。
- Given 某列節點位於深層路徑,Then 列名顯示完整路徑 breadcrumb 並依寬度漸進式截斷;hover 任一處都顯示完整路徑。
- Given 某列路徑完整顯示得下(沒有任何縮寫),Then hover 不顯示 tooltip。
- Given 用批次列選角色,Then 套用到顯示中所有列的直屬該類型裝置(各記 1 筆),不跳 confirm。
- Given 任一列已變更,Then 該列不出現任何顏色標示,變更僅反映在
N changes與 Review Changes。
TODO / 未解問題
- 「Multiple roles」作為系統保留字、不可被設為 Custom role name 的規則,RD 尚未實作;實作後補回描述與驗收條件。
All sites 批次
點 sidemenu「All sites」的 checkbox(未勾或半選 → 勾選,標準三態循環)→ 右面板換成單一批次列,一次套用一個角色到全組織該類型裝置。進入本身不動任何角色。
進入批次 mode Enter
- 進入 = 點 All sites checkbox 從未勾或半選 → 勾選:sidemenu 全變勾選、右面板換成單一批次列(列名
Apply to all sites)。不改任何角色——「覆蓋全 org」的守門在套用時,不在入口。 - 右面板沒有標題文字:批次 mode 的識別由三件事表達——sidemenu
All sites列有選中底色、計數列顯示全 org 數字、批次列名為Apply to all sites(主 mode 為Apply to all selected)。 - dropdown 顯示全 org 該類型裝置的聚合角色(全同→該角色、不一致→Multiple roles、全為 No Access→「Select role」placeholder、沒裝置→
—disabled)。
每個狀態的點擊結果只有一種。沿革見
manage-device-access-decision-history.md第 2 節。
套用角色必須 confirm Apply needs confirmation
會覆蓋整個組織的該類型裝置,所以選角色時先跳 confirm(bounded 的主 mode 批次列不需要):
| 元素 | 內容 |
|---|---|
| 標題 | Apply role to all Cameras?(Camera)/ 對應 Other 類型的標題 |
| 內容 | 說明此舉覆蓋全 org 該類型裝置、取代先前 per-site / per-device 設定;並提示送出前可逐筆檢視調整 |
| 主鈕 | Apply to all |
| 次鈕 | Cancel |
- 確認 → 套用到全 org 該類型裝置,記 1 筆 diff(
All sites → 角色)。 - 取消 → 回 All sites mode、不套用。
- Empty(該類型全是 No Access)或該類型無裝置 → 不跳 confirm;後者不記 diff。
取消 All sites:一律先確認 Uncheck always confirms
點 All sites checkbox 勾選 → 未勾時,只要組織內還有任何裝置保有存取,一律跳 Reset access for all sites?。
| 元素 | 內容 |
|---|---|
| 標題 | Reset access for all sites? |
| 內容第 1 句 | Every device in the organization will be set to No Access. This clears all per-site and per-device settings. |
| 內容第 2 句(只在已有變更時出現) | This also discards your N unsaved changes.——N 為 1 時省略數字(your unsaved change),N 為 0 時整句不顯示;數字與標題列的變更計數同一個值 |
| 內容第 3 句 | You can review the reset before saving. |
| 主鈕 | Reset access |
| 次鈕 | Cancel |
- 確認後的結果可以送出:全 org 設為 No Access 並記進變更清單——至多 2 筆(Camera / Other 各一),依已儲存狀態判斷該類型是否記帳(初始即全 No Access 記 0 筆)。
Site / Area欄顯示All sites,且為唯讀(見 Section 07)。本次 session 先前記的每一筆都被這次重設取代而清算。 - 不跳確認也不提示的例外:當下全組織已無任何存取 → 沒東西可撤,直接清掉勾選、回主 mode。
- 取消:什麼都不動——維持勾選、留在批次 mode。
- 重設不觸發 link-confirm:整個組織都被清空,不存在「範圍外還有連動裝置」(見 Section 06)。
- 從 All sites mode 進 Review Changes 再 back → 回 All sites mode。
這個 confirm 是 Section 01「破壞性編輯不用 modal 擋」的例外。 它第一步會清算整份變更清單,而它寫入的兩筆在 Review Changes 是唯讀的——「變更清單會兜住」的前提在此不成立,事後唯一退路是 Cancel 放棄整個 session。與「進入批次 mode 不需確認」不矛盾:進入完全不動角色。
批次 mode 中點站點列不是特例。 點任一站點的 checkbox 就是照常 toggle(照常走移除記帳與 link-confirm 守門);selection 因此不再是全選,批次 mode 隨之自動退出。
三態循環 Three-state cycle
All sites 列是整個 sidemenu 唯一的三態 checkbox(站點樹節點為兩態,見 Section 02)——它是批次 mode 的開關 + 全組織存取狀態的摘要(半選 = 組織內部分裝置保有存取),不是樹的選取元件。循環為 半選 → 勾選 → 未勾:半選與未勾點下去都是進批次 mode,勾選點下去是取消。
半選 是子項狀態的摘要、不是可被選到的目標值(Site 本身不持有權限),所以點 半選 與點 未勾 同樣往「全給」走。
規範層未規定點半選後往哪走,選「全給」的四個理由見
manage-device-access-decision-history.md第 1 節。
驗收條件 QA · Given-When-Then
- Given 點 All sites checkbox 未勾 → 勾選,Then 全 site 變勾選、右面板換成單一
Apply to all sites列,且不改任何角色、不記任何變更。 - Given All sites 為半選(has-value 現況)時點它,Then 直接進入批次 mode,不跳任何 modal。
- Given 批次 mode 中,Then 右面板標題列沒有標題文字,只有裝置類型 Tab 與變更計數;sidemenu
All sites列有選中底色、計數列顯示全 org 數字、批次列名為Apply to all sites。 - Given 在 All sites 選角色且該類型有非 No Access 裝置,Then 跳「Apply role to all …?」;確認後全 org 套用並記 1 筆「All sites → 角色」。
- Given 點 All sites checkbox 勾選 → 未勾 且組織內仍有裝置保有存取,Then 一律跳「Reset access for all sites?」——不論本次 session 是否套過角色。
- Given 已記錄 3 筆變更時觸發該 confirm,Then 內容出現「會一併捨棄已記錄的 3 筆變更」那一句,數字與標題列的變更計數相同;尚無變更時該句不出現。
- Given 確認 Reset access,Then 全 org 設為 No Access 並在變更清單記至多 2 筆(
All sites,Camera / Other 各一)、Save changes可用;先前記的變更被清算。 - Given 已儲存狀態原本就毫無存取時點勾選 → 未勾,Then 不跳 confirm,直接清掉勾選回主 mode。
- Given 在 Reset confirm 按 Cancel,Then 什麼都不變——維持勾選、留在批次 mode。
- Given 批次 mode 中點任一站點的 checkbox,Then 該節點照常 toggle(照常走移除記帳與 link-confirm 守門),並因 selection 不再是全選而自動退出批次 mode。
- Given 確認 Reset access 之後,再把某個 site 的角色設成它原本已儲存的角色,Then 變更清單仍會列出這一筆(否則清單會讀成該 site 也變成 No Access)。
- Given 從 All sites mode 進 Review Changes 再 back,Then 回到 All sites mode。
Drill-in 單一裝置
點右面板某列的 chevron > → 整個 body 換成該節點的單一裝置清單,可逐台設定角色。drill-in 期間 footer(Save / Cancel)隱藏,變更累計回主 mode 統一送出。
Header(40px) Header
- 左:back
<回主 mode(drill-in 只能從主 mode 的列 chevron 進入;All sites 批次的單一列沒有 chevron)。 - 中:標題
Configure device access for "<節點名稱>",引號內是該節點自己的名稱(不含路徑)。過長時只截斷引號內的名稱,前綴不縮;hover 出完整名稱。 - 右:240×32「Search devices」搜尋框(300ms debounce;此清單 1 個字即觸發——與 Filter Combo / sidemenu 的 ≥2 字不同,以實作為準)。
路徑放在下方 Area header 列而非標題,避免同一畫面印兩次。
Area header 列(60px) Area header
- 左:完整路徑 breadcrumb,規則、視覺、tooltip 與主 mode 列名完全相同(共用同一個實作,見 Section 03)。差別只有可用寬度:drill-in 收掉 sidemenu、單欄佔滿 body,該列由 716px 變約 1036px,又沒有 chevron 欄(省下 32px 按鈕 + 16px 間距),breadcrumb 可用寬度約由 420px 變 790px。同一節點因此常在 drill-in 完整顯示、在主 mode 已折疊——寬度驅動,不是兩套規則。
- 右:200×32 dropdown,顯示該節點直屬該類型裝置的聚合角色。
- 改 dropdown = 套用到該節點直屬該類型的全部裝置(不遞迴子層;不受裝置搜尋過濾影響)。
- 節點無該類型裝置時 dropdown 為
—disabled,hover 出與主 mode 相同的說明:No cameras here./No other devices here.(共用同一份實作,見 Section 03)。
裝置列(60px) Device row
| 元件 | 顯示條件 | 說明 |
|---|---|---|
| 裝置 icon | 永遠 | 依裝置類型(見下) |
| 裝置名稱 | 永遠 | — |
N tag |
Camera 連動到任一 NVR(不限同節點——只檢查有無連動的 NVR,不比對是否同一 site / area,以實作為準) | 直角小標;hover 顯示連動的 NVR 名 |
PREM tag |
Camera 且實體屬性為 premium | outline 圓角小標(4px)+ info icon;點 info icon 出說明(支援 Attribute search / Re-search) |
B tag |
Network Speaker / PoE Switch 連動到 Bridge | 直角小標;hover 顯示連動的 Bridge 名 |
Auto-granted tag |
NVR / Bridge 因連動被自動賦權(來源 = auto) | 藍底 pill;hover 顯示觸發它的來源裝置(見 Section 06) |
| Dropdown | 永遠 | 200×32,選項見下 |
裝置類型 icon(9 類 + 容錯):Camera(default / fisheye / PTZ / MA 多sensor)、NVR、Bridge、Network Speaker、PoE Switch、VSS,以及 Unknown 容錯。
dropdown 選項(裝置層才有 No Access) Options
| 裝置類別 | 選項 |
|---|---|
| Camera 系 | No Access / Viewer / Supervisor / 各 Custom role(N 個,01 / 02 / 03… 僅示意) |
| Other 系 | No Access / Viewer / Supervisor |
Custom role 只能套到 Camera 系。No Access 只出現在裝置列——同頁的 Area header 與主 mode 站點層都沒有。
Other 系裝置已持有 custom role(舊資料):dropdown 顯示該 custom role,選項仍為 No Access / Viewer / Supervisor;改成其他值後無法再選回(Patrick,2026-10-15;決策紀錄 §3)。節點聚合見 Section 03「三種特殊顯示」。
空狀態 Empty states
drill-in 可進入任何節點(即使沒裝置),依情況顯示對應空狀態(共用 64×64 插圖):
| 情境 | 標題 | 說明 |
|---|---|---|
| 該節點任何類型都沒裝置 | No Devices in This Site or Area |
Go back to pick another site, or add devices to this one first. |
| 有 Other 但沒 Camera(filter = Camera) | No Cameras Here |
Switch the device type filter to see what's on this site. |
| 有 Camera 但沒 Other(filter = Other devices) | No Devices Here |
Switch the device type filter to see what's on this site. |
| 搜尋無結果 | No Matches Found |
文案與版面見 Tree View §04 |
drill-in 空節點不算 diff。
drill-in 沒有裝置類型 Tab(只在主 mode 標題列;drill-in header 只有 back / 標題 / 搜尋)。空狀態文案講的「切換類型」是要先返回主 mode 再切——在 drill-in 內切換類型等於換掉觀察對象,會讓 back 的返回目標不明確。OpenSpec 要求在 drill-in 空狀態也能直接切,以本節為準。
全清會即時反映到 sidemenu Clearing all devices
把該節點的直屬裝置全部設為 No Access → sidemenu 該節點 checkbox 自動變未勾、返回主 mode 後該列消失;恢復任一裝置則自動變回勾選。這是顯示層即時推導,不產生額外 diff——完整規則見 Section 02「checkbox 即時反映裝置狀態」,此處不重列。
大量裝置的載入 Infinite scroll
裝置多(60+)的節點分批載入:第一批 50 筆,捲近底部自動載下一批 50 筆;切 filter / 改搜尋 / 進不同節點都重置回第一批。載入時末端顯示置中 48×48 loading(無文字)。
驗收條件 QA · Given-When-Then
- Given 點某列 chevron
>,Then 進入該節點的裝置清單,footer 隱藏。 - Given 改一台裝置的角色,Then 只有該台變更並記 1 筆 diff。
- Given 裝置層 dropdown,Then 有 No Access 選項(站點層與 Area header 沒有);Custom role 只出現在 Camera 系。
- Given Other 系裝置持有 custom role(舊資料),Then dropdown 顯示該 custom role,選項為 No Access / Viewer / Supervisor;改成其他值後選項中沒有 custom role。
- Given 進入沒有該類型裝置的節點,Then 顯示對應空狀態,且不記 diff。
- Given 在 drill-in 的空狀態,Then 沒有裝置類型 Tab 可切;要換類型必須先返回主 mode(見上方註記)。
- Given 進入任一節點,Then 標題為
Configure device access for "<節點名稱>"(引號內不含路徑),路徑顯示在下方的 Area header 列。 - Given 該節點位於深層路徑,Then Area header 的 breadcrumb 規則與主 mode 列名一致;hover 任一處顯示完整路徑。
- Given 該節點沒有該類型裝置,Then Area header 的 dropdown 為
—disabled 且 hover 顯示No cameras here./No other devices here. - Given 節點裝置超過一批,Then 捲動載入下一批 50 筆,末端顯示 loading。
連動與確認
有些裝置彼此連動:Camera ↔ NVR、Network Speaker / PoE Switch ↔ Bridge,改一邊會牽動另一邊。原則是正向自動、反向確認,共三種狀況。
三種狀況出現的裝置名與角色皆為顯示名。權威:OpenSpec
manage-device-access-role · Devices and roles are named by their display labels。
正向自動賦權(不打斷) Forward auto-grant
- 把 Camera(或 Network Speaker / PoE Switch)從 No Access 改成其他角色時,若連動的 parent(NVR / Bridge)當下是 No Access 且來源非手動 → parent 自動變 Viewer、來源標
auto、顯示Auto-grantedtag(見 Section 05)。 - 全部連動的 child 都回 No Access → auto 的 parent 也跟著回 No Access。
- 使用者手動改 parent → 來源變 manual,脫離 auto。
- 正向不當下打斷;自動賦權在背景累積,送出前才統一確認(狀況三)。
狀況一:操作當下的撤銷確認 Link-confirm · device trigger
drill-in 把某裝置改成 No Access、且會連帶讓 linked 裝置失去存取時,當下立刻跳確認。
| 觸發 | 連動對象 |
|---|---|
| NVR → No Access | 連動的 Cameras |
| Bridge → No Access | 連動的 Network Speaker / PoE Switch |
| Camera → No Access(且它是某 NVR 最後一個保有存取的來源) | 該 NVR(來源非 manual) |
| Network Speaker / PoE Switch → No Access(同上邏輯) | 該 Bridge(來源非 manual) |
- 標題:
Remove access from "<裝置名>"?;內文:Setting "<裝置>" to No Access also removes access from its linked <類型>:,逐列列出裝置名(類型)+舊角色 → No Access;hover 出完整路徑。 - CTA:
Remove access(主)/Cancel(次)。 - 確認 → 觸發裝置與所列 linked 裝置都設 No Access,各記 1 筆 diff;取消 → 全 revert、無 diff。
狀況二:移除整個 site 的連動確認 Link-confirm · site trigger
從 sidemenu uncheck 一個 site / area,若其 subtree 連動到其他站點的裝置且會一併失去存取 → 跳同一個 link-confirm,但為 site 觸發版本。
- 標題:
Remove access from "<site 名>"?;內文:Removing access from "<site>" also removes access from these linked devices in other sites:,逐列列出跨站受影響裝置 + 路徑。 - CTA:
Remove access/Cancel。行為同狀況一(確認一併撤銷並記 diff;取消不動)。
All sites 的重設不走這條路。 Reset access for all sites?(見 Section 04)不會再跳 link-confirm——它把整個組織設為 No Access,不存在「範圍外還有連動裝置」。uncheck 單一 site / area 則相反:範圍有邊界,邊界外的連動裝置正是要提醒的事。
狀況三:送出前的正向連動彙總 Confirm Summary at save
按「Save changes」時,若這次 session 有因正向連動而自動賦權(來源從初始變成 auto)的 NVR / Bridge,先跳彙總 modal 統一確認。
| 元素 | 內容 |
|---|---|
| 標題 | Confirm Device Access Changes |
| 區塊 | The following will update automatically based on linked devices:(1 筆與多筆共用同一句,刻意不做單複數分支) |
| 每列 | 裝置名(NVR / Bridge) + No Access → 角色 + Triggered by: 觸發來源裝置;hover 出路徑 |
| CTA | Confirm changes(主)/ Cancel(次) |
反向撤銷已在狀況一 / 二當下確認過,不在此重複列出。
邊界:VSS Out of scope
此版不實作 VSS 連動警示——現有情境尚未出現需要它的商業條件;VSS 在 drill-in 只當一般裝置,不觸發 link tag 或 link-confirm。條件符合時再規劃。
驗收條件 QA · Given-When-Then
- Given 把某 Camera 從 No Access 改為有角色、連動 NVR 當下 No Access 且非手動,Then NVR 自動變 Viewer、標 Auto-granted,不當下跳 modal。
- (狀況一)Given drill-in 把裝置改為 No Access 且牽動 linked 裝置,Then 跳「Remove access from "<裝置>"?」列出受影響裝置;確認一併 No Access 並各記 diff,取消全 revert。
- (狀況二)Given uncheck 的 site subtree 連動到外部站點裝置,Then 跳「Remove access from "
"?」列出跨站受影響裝置。 - Given 確認 All sites 的「Reset access for all sites?」,Then 不跳 link-confirm(整個組織都被清空,沒有範圍外的連動裝置)。
- Given Confirm Summary 只有 1 台自動賦權裝置,Then 標題與多筆時完全相同,不出現任何單複數差異。
- (狀況三)Given 正向賦權產生 auto NVR / Bridge,Then 按 Save 跳「Confirm Device Access Changes」列出這些自動更新,CTA 為
Confirm changes;反向撤銷不重列。 - Given VSS 參與連動,Then 此版不顯示警示(一般裝置處理)。
連動裝置不保證同站。 實測:把
East Wing的Camera-001設為 Viewer 後,被自動賦權的 NVR 不在該站點底下(切到 Other devices Tab 進同一節點,底下的 NVR 全是 No Access)。Auto-granted因此會出現在另一個節點的裝置列上,要找它得從 Confirm Summary 反查位置。與 Section 05Ntag 規則一致(只檢查有無連動的 NVR,不比對是否同節點)。
Review Changes
點右面板標題列的「N changes」→ 整個 body 換成本次 session 的變更清單。每一筆變更(含移除)都列出來,送出前一覽無遺。
清單上方另有一行提示:「When a site and the devices inside it are set to different roles, the site row will appear as Multiple roles after saving.」(說明站點與其內部裝置角色不一致時,送出後該站點列會呈現 Multiple roles。)
版面 Layout
- Header(40px):back
<(回進入前的 mode) + 標題「N changes」(單數「1 change」)。 - Footer 顯示(Save changes / Cancel)——與 drill-in 不同,Review 沿用主 mode 的 footer。
兩段排序 Two sections
清單分兩段,各段內依變更加入順序:
- Site / Area 等級變更——4 欄表格:
Site / Area|Device type|Old role|New role。 - Device 等級變更——依「裝置所屬節點」群組,每群一個 sub-header(40px) + 該節點下的裝置列。
同節點的裝置變更會聚在同一個 sub-header 下,即使操作時在不同節點間穿插。
兩段的列不同欄。 裝置列 3 欄:合併欄(類型圖示 + 裝置名) | Old role | New role,無 Device type 欄——型別由圖示帶,節點由 sub-header 帶。
欄位與呈現 Columns
下表為 Site / Area 列的 4 欄。
| 欄位 | 規則 |
|---|---|
Site / Area |
只顯示最末層名稱;有路徑時 hover 出完整 Root > … > Self tooltip(游標變 help)。刻意不採漸進式截斷——此欄與 Device type 共用寬度,放 breadcrumb 會被截到失去意義,而 hover 已提供同樣資訊;這是與主 mode 列名(完整路徑 breadcrumb,見 Section 03)的有意差異。All sites 重設的兩筆在此欄顯示 All sites |
Device type |
Camera 或 Other devices(短標籤,不逐字列舉種類)。刻意不加 hover 說明——Other devices 含哪五種已由主 mode 的 Tab tooltip 說明(見 Section 03),使用者是先在那裡選了類型才走到這裡 |
Old role |
舊角色(灰字 + 刪除線) + 箭頭 icon |
New role |
200×32 dropdown,可直接重選,立即套回對應範圍 |
- device 群組的 sub-header 同上:只顯示最末層 + hover 看路徑(游標變 help),同樣刻意不採 breadcrumb。本節因此有兩處截斷規則的例外。
裝置名用 drill-in 的顯示名;
Old role/New role用No Access/Viewer/Supervisor/ custom role 名稱。 權威:OpenSpecmanage-device-access-role · Devices and roles are named by their display labels(規範整個對話框,含 Section 06 兩個 modal)。
diff 顆粒度 Diff granularity
每筆只在「使用者真正動到的最高層級」記一次:
| 操作 | diff |
|---|---|
| sidemenu uncheck 一個 site(勾選 → 未勾) | 至多 2 筆(Camera / Other 各一筆,僅該類型的 subtree 在已儲存狀態含非 No Access 裝置、且尚無涵蓋此範圍的粗粒度移除時成立;剛勾選未配角色、或已被粗粒度移除涵蓋 → 0 筆);整 subtree cascade 不列,且 subtree 內本次 session 已記的變更一併清算(見 Section 02) |
| 右面板改一列 dropdown | 1 筆(該節點 → 新角色,對應該 filter) |
| drill-in 改一台裝置 | 1 筆(裝置層) |
| All sites 批次改角色 | 1 筆(All sites) |
| All sites 重設(取消 All sites checkbox 並確認) | 至多 2 筆(Camera / Other 各一,Site / Area 欄顯示 All sites);依已儲存狀態判斷該類型是否需要記帳,初始即全 No Access 的類型記 0 筆;先前記的每一筆都被這次重設取代而清算;唯讀(見下) |
| drill-in 全清觸發的 checkbox 自動未勾 | 0 筆(顯示層推導,裝置變更已各自成列,見 Section 02) |
| 自動賦權的 NVR / Bridge(來源 auto) | 0 筆——不進 diff、不出現在 Review;於送出前的「Confirm Device Access Changes」彙總 modal 統一列出(見 Section 06) |
在 Review 內重選 Re-select in review
- 站點列、裝置列、All sites 列的 New role dropdown 可直接改,立即套回原本 scope。裝置列改成 No Access 時與 drill-in 相同,會經過 link-confirm 守門(見 Section 06)。
- uncheck 列(
… → No Access)為唯讀——在 Review 直接重配會遺失被 cascade 撤權的子孫紀錄;要回復請回主 mode 重新勾選該節點再配角色。All sites 重設的兩筆同樣唯讀(它們就是「uncheck 整棵樹」)。 - 改回最初的舊角色 → 該筆自動移除(revert)。「最初」是扣掉範圍更大的變更之後的基準,不是無條件的已儲存值:若已有涵蓋此節點的粗粒度移除(祖先 uncheck 或 All sites 重設),基準即
No Access,把它設成原本已儲存的角色仍算一筆(見下節)。 - Revert 的先天限制:
Old role為「Multiple roles」的筆(初始角色不一致)無法用「改回原值」移除——要取消得用 Cancel 放棄整個 session。
清單必須能重建最終狀態 The list is the contract
從已儲存狀態套用清單每一筆(範圍重疊取最具體者),結果必須等於實際會被送出的狀態。
「最具體」由細到粗:某台裝置 → 該裝置所屬節點 → 該節點與各層祖先的移除 → 整個組織。同範圍不會同時存在兩筆(對節點明確賦值會取代該節點自己的移除;移除 subtree 會清算其內的細粒度筆數)。由此得兩條規則:
- 粗粒度移除之後,對範圍內節點賦權一定另記一筆——即使角色剛好是已儲存原值;該筆
Old role顯示No Access(粗粒度造成的狀態)。若省略,清單會讀成「該節點也變成 No Access」,與實際相反。 - 已被涵蓋的移除不重複記——粗粒度那筆已表達同一個轉換。
清單因此可能出現「粗粒度預設 + 細粒度例外」,例如先一筆 All sites → No Access,後接一筆 某 Area:No Access → Supervisor。排序保證粗粒度那筆在例外之前(見「兩段排序」:各段內依變更加入順序)——不能改成依名稱排,否則讀者會先看到例外、再看到涵蓋它的預設,結論相反。
淨變化為零時清單為空:若最後沒有任何裝置與已儲存狀態不同,清單清空、Save changes 停用,即使過程中記過粗粒度與例外的組合。
Back 行為 Back
進入 Review 時記住前一個 mode(主 / All sites),back 還原到該 mode——不會跳成「主 mode 但 sidemenu 全勾選」的混合狀態。
驗收條件 QA · Given-When-Then
- Given 一筆裝置層變更,Then 該列為合併欄(類型圖示 + 裝置名) +
Old role+New role,無Device type欄。 - Given 同一台裝置出現在 drill-in 與 Review,Then 名字相同,不是 thing name。
- Given 任一筆變更,Then
Old role與New role皆為角色顯示名。 - Given uncheck 一個含 Camera 與 Other 的 site(兩類型初始皆保有存取),Then Review 列 2 筆(Device type 各為 Camera 與 Other),皆
舊角色 → No Access。 - Given 移除某站點存取使它從可見清單消失,Then 該變更仍出現在 Review(
舊角色 → No Access)。 - Given 在 Review 把某筆 New role 改回最初值,Then 該筆自動從清單移除、N changes 遞減。
- Given 一筆 uncheck 列(
… → No Access),Then 其 New role dropdown 為唯讀不可改。 - Given 在 Review 把某台裝置改成 No Access 且會牽動 linked 裝置,Then 跳與 drill-in 相同的 link-confirm modal。
- Given 從 All sites mode 進 Review 再 back,Then 回到 All sites mode。
- Given N changes = 0,Then 不顯示 N changes 入口(不會進到空的 Review)。
- Given 確認 All sites 重設,Then 清單出現至多 2 筆、
Site / Area欄為All sites、Device type 各為Camera與Other devices,且兩筆的 New role dropdown 唯讀。 - Given 已有一筆涵蓋某節點的粗粒度移除,再把該節點設成它原本已儲存的角色,Then 清單仍列出這一筆,且該筆
Old role顯示No Access。 - Given 粗粒度移除已在清單上,再 uncheck 該範圍內的某個節點,Then 不新增重複的一筆。
- Given 從已儲存狀態出發套用清單上每一筆(範圍重疊取最具體者),Then 得到的每個裝置角色與實際將送出的狀態完全一致。
- Given 所有編輯的淨效果為零(沒有任何裝置與已儲存狀態不同),Then 清單為空、
Save changes停用。
送出與取消
Footer 有「Save changes」與「Cancel」。送出只送 diff;有未存變更時關閉會先確認。
Save changes Submit
| 條件 | 行為 |
|---|---|
| 啟用 | 有變更(N changes > 0) |
| 停用 | 沒有變更(N changes = 0),灰色 |
| 點下去(無自動賦權) | 直接送出 diff |
| 點下去(有自動賦權的 NVR / Bridge) | 先跳 Confirm Summary(見 Section 06),確認後才送 |
- 成功 → 關閉對話框、外層 user detail 頁刷新為新值。
- 失敗 → 對話框保持開啟、變更保留,並在 footer 左側顯示訊息。
失敗訊息在 footer Footer message
送出失敗不使用 toast —— Portal 沒有這個元件。承載處是 footer 左側的訊息槽,與 Device Picker §01 的 footer 訊息同模式、同 style:
| 項目 | 規格 |
|---|---|
| 位置 | footer 左側,距左緣 40、寬 696、高 20;footer 本身 1116 × 70,訊息距 footer 上緣 15 |
| 顏色 | --color-text-alert(#FFB029)—— 是警示色不是紅色 |
| 字型 | Segoe UI 14 Regular、line-height 1.3 |
| 與按鈕的關係 | 同一列;Save changes / Cancel 仍在右側(150 × 42,x = 756 / 926),訊息不推移按鈕 |
這是 Portal 的限制,不及於 App。 08 App Sites Related Pages 的異常呈現仍未定案、並把 toast 列為候選,那是 App 端的判斷,不受本節約束。
上游 OpenSpec 已同步。
manage-device-access-role的Submit enablement and submit outcome現在要求成功關閉對話框、失敗保留對話框並在 footer 顯示訊息,不再要求 toast;成功回饋因對話框關閉後無承載處,列為OQ8待 PM 決定。
勾選但沒配角色的節點不會被儲存 Checked without a role
只勾選一個節點、沒有在右面板或 drill-in 指派角色 → 不記任何變更,因此送出時也不會被儲存。重開對話框後該節點會依裝置現況重新推導(沒有任何裝置保有存取 → 未勾),sidemenu 的選取計數也隨之下降。
送出前沒有額外提醒:Confirm Summary 只在有自動賦權時才跳(見 Section 06)。這不造成資料損失——勾選只是「我要管理這幾個節點」,不是授權;計數算的是勾選節點數而非有權限的節點數(見 Section 02)。已知風險:使用者仍可能讀成工作遺失,特別是有些節點配了角色、有些沒配的混合情況。
Cancel / 關閉 / ESC Dismiss
| 情境 | 行為 |
|---|---|
| N changes = 0 | 直接關閉 |
| N changes > 0 | 跳 Discard 確認 |
Discard modal:
| 元素 | 內容 |
|---|---|
| 標題 | Discard changes? |
| 內容 | 提示未存的存取變更將遺失 |
| 主鈕 | Discard(捨棄並關閉) |
| 次鈕 | Keep editing(留在對話框) |
驗收條件 QA · Given-When-Then
- Given 沒有任何變更,Then Save changes 停用。
- Given 有變更並送出成功,Then 關閉對話框且外層頁面刷新為新值。
- Given 送出失敗,Then 對話框保留、變更不遺失,且 footer 左側顯示訊息(
--color-text-alert、Segoe UI 14 Regular)。 - Given 送出失敗,Then 不出現 toast;
Save changes/Cancel位置不因訊息出現而移動。 - Given 有未存變更時按 Cancel / 關閉 / ESC,Then 跳「Discard changes?」;Discard 捨棄關閉、Keep editing 留下。
- Given 送出時 diff 含自動賦權 NVR / Bridge,Then 先跳 Confirm Summary(見 Section 06)。
- Given 只勾選節點、沒有指派任何角色,Then Save changes 停用(沒有變更可送)。
- Given 一部分節點配了角色、一部分只勾選未配,Then 送出後只有配了角色的節點被儲存;重開對話框時未配角色的節點不再勾選、選取計數隨之下降。
TODO / 未解問題
- 失敗訊息文案未定。 key screen 的訊息槽是佔位字
{{Describe the current error state.}}(與 Device Picker §01 同一個佔位),Figma 沒有實際字串。原型的Failed to update access/Please try again or contact support.是原型自己的字串、不是平台文案,不得當規格引用。 - 成功回饋沒有承載元件。 成功會關閉對話框,footer 訊息槽隨之消失,而 Portal 沒有 toast——上游要求的「說明變更何時生效」目前無處可放。可能方向是放在外層 user detail 頁,但那是本節範圍外,需與 PM 確認。