| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- @page "/yaml/import"
- @using RackPeek.Domain.Api
- @using RackPeek.Domain.Persistence
- <PageTitle>Yaml Import</PageTitle>
- @inject UpsertInventoryUseCase ImportUseCase
- <div class="border border-zinc-800 rounded p-4 bg-zinc-900 mt-6">
- <div class="text-zinc-100 mb-3">
- Import YAML
- </div>
- <!-- Mode selector -->
- <div class="mb-4 flex gap-6 text-xs">
- <label class="flex items-start gap-2 cursor-pointer">
- <input type="radio"
- name="mergeMode"
- checked="@(_mode == MergeMode.Merge)"
- @onchange="() => OnModeChanged(MergeMode.Merge)"/>
- <div>
- <div class="text-emerald-400">Merge</div>
- <div class="text-zinc-500">
- Update matching resources. Properties not specified remain unchanged.
- </div>
- </div>
- </label>
- <label class="flex items-start gap-2 cursor-pointer">
- <input type="radio"
- name="mergeMode"
- checked="@(_mode == MergeMode.Replace)"
- @onchange="() => OnModeChanged(MergeMode.Replace)"/>
- <div>
- <div class="text-red-400">Replace</div>
- <div class="text-zinc-500">
- Completely replace matching resources.
- </div>
- </div>
- </label>
- </div>
- <!-- YAML Input -->
- <textarea class="w-full input font-mono text-xs mb-3"
- style="min-height: 18rem"
- placeholder="Paste YAML here..."
- @bind="_inputYaml"
- @bind:event="oninput"
- @bind:after="ComputeDiff">
- </textarea>
- @if (!string.IsNullOrEmpty(_validationError))
- {
- <div class="text-red-400 text-xs mb-3">
- @_validationError
- </div>
- }
- <!-- Apply -->
- <div class="flex gap-3 text-xs mb-4">
- <button class="text-amber-400 hover:text-amber-300 disabled:opacity-40"
- disabled="@(!_isValid)"
- @onclick="Apply">
- Apply
- </button>
- </div>
- <!-- Summary + Diff -->
- @if (_isValid)
- {
- <div class="mt-4 text-xs">
- <div class="mb-2 text-zinc-400">
- Import Summary
- </div>
- @if (!_added.Any() && !_updated.Any() && !_replaced.Any())
- {
- <div class="text-zinc-500 italic mt-2">
- No changes detected.
- </div>
- }
- @if (_added.Any())
- {
- <div class="text-emerald-400 mb-2">
- + @_added.Count added
- </div>
- @foreach (var name in _added)
- {
- <div class="ml-4 text-zinc-300 mb-4">
- <div class="font-bold">+ @name</div>
- <pre class="bg-zinc-800 p-2 rounded text-xs overflow-x-auto">
- @_newYaml[name]
- </pre>
- </div>
- }
- }
- @if (_updated.Any())
- {
- <div class="text-amber-400 mt-4 mb-2">
- ~ @_updated.Count updated
- </div>
- @foreach (var name in _updated)
- {
- <div class="ml-4 mb-6">
- <div class="font-bold text-zinc-300 mb-2">~ @name</div>
- <div class="grid grid-cols-2 gap-4">
- <div>
- <div class="text-zinc-500 mb-1">Current</div>
- <pre class="bg-zinc-800 p-2 rounded text-xs overflow-x-auto">
- @_oldYaml[name]
- </pre>
- </div>
- <div>
- <div class="text-emerald-500 mb-1">Incoming (Merged)</div>
- <pre class="bg-zinc-800 p-2 rounded text-xs overflow-x-auto">
- @_newYaml[name]
- </pre>
- </div>
- </div>
- </div>
- }
- }
- @if (_replaced.Any())
- {
- <div class="text-red-400 mt-4 mb-2">
- ! @_replaced.Count replaced
- </div>
- @foreach (var name in _replaced)
- {
- <div class="ml-4 mb-6">
- <div class="font-bold text-zinc-300 mb-2">! @name</div>
- <div class="grid grid-cols-2 gap-4">
- <div>
- <div class="text-zinc-500 mb-1">Current</div>
- <pre class="bg-zinc-800 p-2 rounded text-xs overflow-x-auto">
- @_oldYaml[name]
- </pre>
- </div>
- <div>
- <div class="text-red-400 mb-1">Incoming (Replacement)</div>
- <pre class="bg-zinc-800 p-2 rounded text-xs overflow-x-auto">
- @_newYaml[name]
- </pre>
- </div>
- </div>
- </div>
- }
- }
- </div>
- }
- </div>
- @code {
- private List<string> _added = new();
- private List<string> _updated = new();
- private List<string> _replaced = new();
- private Dictionary<string, string> _oldYaml = new(StringComparer.OrdinalIgnoreCase);
- private Dictionary<string, string> _newYaml = new(StringComparer.OrdinalIgnoreCase);
- private string _inputYaml = "";
- private string? _validationError;
- private bool _isValid;
- private MergeMode _mode = MergeMode.Merge;
- async Task OnModeChanged(MergeMode mode)
- {
- _mode = mode;
- await ComputeDiff();
- }
- async Task ComputeDiff()
- {
- _validationError = null;
- _isValid = false;
- _added.Clear();
- _updated.Clear();
- _replaced.Clear();
- _oldYaml.Clear();
- _newYaml.Clear();
- if (string.IsNullOrWhiteSpace(_inputYaml))
- return;
- try
- {
- var result = await ImportUseCase.ExecuteAsync(new ImportYamlRequest
- {
- Yaml = _inputYaml,
- Mode = _mode,
- DryRun = true
- });
- _added = result.Added;
- _updated = result.Updated;
- _replaced = result.Replaced;
- _oldYaml = result.OldYaml;
- _newYaml = result.NewYaml;
- _isValid = true;
- }
- catch (Exception ex)
- {
- _validationError = $"YAML invalid: {ex.Message}";
- }
- }
- async Task Apply()
- {
- if (!_isValid)
- return;
- try
- {
- await ImportUseCase.ExecuteAsync(new ImportYamlRequest
- {
- Yaml = _inputYaml,
- Mode = _mode,
- DryRun = false
- });
- _inputYaml = "";
- _isValid = false;
- _added.Clear();
- _updated.Clear();
- _replaced.Clear();
- }
- catch (Exception ex)
- {
- _validationError = $"Apply failed: {ex.Message}";
- }
- }
- }
|