Server-driven building-clear preview on AssignRacksToSite
- Dominant language
- Go
- Stars
- 55
- Forks
- 16
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 87
Description
## Context
PR #489 adds a client-side detection + confirm dialog for the cross-site building clear in `AssignRacksToSite`: when an operator picks "Add to site" on a rack list selection and any selected rack's current building belongs to a different site, the client opens a "Move racks between sites?" dialog before dispatching.
That detection lives entirely in the browser, using `allBuildings` (already loaded for the building filter chip) cross-referenced against each rack's `building_id`. It works, but it's structurally asymmetric with the `AssignDevicesToBuilding` flow shipped in the same PR — that one is server-driven: the server returns `PerDeviceBuildingConflict[]` on a no-force call and the client retries with `force_clear_conflicting_rack_membership=true`.
The asymmetry isn't load-bearing today (worst case is a missed or stale dialog; server still cascades correctly), but server-driven is the right shape for consistency and for closing the stale-`allBuildings` window where one operator's cached state could mislabel another operator's concurrent building move.
## Proposed change
Add `optional bool force_clear_building` to `AssignRacksToSiteRequest` and a `repeated RackBuildingClearPreview previews` field to the response. When `!force` and any rack would have its `building_id` cleared (current building lives at a different site than the target), the server returns the preview list with zero writes. Client opens the same dialog (now driven by the server's response), then retries with `force=true`.
Same shape as the `AssignDevicesToSite` / `AssignDevicesToBuilding` conflict pattern.
## Sketch of work
- `proto/sites/v1/sites.proto` — new field + new message.
- `server/internal/domain/sites/{service,models}.go` — Phase A captures per-rack `(rack_id, building_id)` instead of just incrementing `clearedCount`; on no-force + any preview, return early with zero writes.
- `server/internal/handlers/sites/{handler,translate}.go` — pass `ForceClearBuilding`, translate previews to proto.
- `client/src/protoFleet/api/sites.ts` — add `forceClearBuilding` prop; surface previews via the existing `onError` channel.
- `client/.../pages/RacksPage.tsx` — delete `summarizeBuildingClearance` + the inline state machine; replace with the no-force → preview → force-retry flow (resolve building labels client-side from `buildingNameById`).
- Tests: update existing `AssignRacksToSite` service + handler tests to expect the new force flag; add a preview-path test.
Estimate: ~9 hand-edited files, ~200 LOC net, ~30–45 min including test updates and proto regen.
## Open question
Once Option A lands here, the same pattern probably wants to apply to `AssignBuildingsToSite` (silently re-stamps every rack + device under the moved buildings; no preview). Worth deciding in one place whether every silent cascade across a site boundary should be gated by a confirm dialog.
Contributor guide
Research direction
Start with the existing AssignDevicesToSite and AssignDevicesToBuilding conflict flows, then read proto/sites/v1/sites.proto and the listed server service, model, handler, and translation files. Trace the current AssignRacksToSite client flow in client/src/protoFleet/api/sites.ts and RacksPage.tsx, and run the existing service and handler tests. Done means the no-force path previews without writes, the client confirms and retries with force, and the updated tests pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, react, typescript
- Domain
- api, backend-api-design, full-stack
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100