a26d6ba317
The Native Alarm Source Overrides card had no bulk affordance — the CSV path shipped CLI-only (instance native-alarm-source import --file), so the UI could only retarget one source at a time inline. Adds a second InputFile on that card, mirroring the attribute importer's UX (hidden input behind a button-styled label, 512 KB cap, success/error alert with the per-line error list, toast). Parsing reuses the SHARED NativeAlarmSourceOverrideCsvParser — the exact parser the CLI uses — and the new pure InstanceConfigure.BuildNativeAlarmSourceCsvImport applies the same batch rules the server enforces in ManagementActor.HandleSetInstanceNativeAlarmSourceOverrides: the source must resolve, must not be template-locked, and may appear at most once; any error rejects the whole file and applies nothing. Semantics match the CLI: merge, not full replace — sources absent from the file keep their existing override, a blank field keeps the inherited value, and an all-blank row clears that source's override (equivalent to the CLI's all-null override row, without leaving a dead row behind). Persistence reuses the inline editor's path: SaveNativeOverride's upsert body is extracted to UpsertNativeOverrideCore (no SaveChangesAsync inside), so the import commits the validated batch in one SaveChangesAsync — no new server method, no duplicated parsing. Tests: InstanceConfigureNativeAlarmCsvImportTests (9) — happy path, blank-field inheritance, all-blank clear, merge semantics, unknown/locked/duplicate source and parser-error rejection, plus structural pins on the InputFile wiring. Docs: Component-CentralUI.md native-alarm-source card gains the import bullet.