Просмотр исходного кода

Merge pull request #108 from Timmoth/#94-markdown-notes

#94 markdown notes
Tim Jones 1 месяц назад
Родитель
Сommit
b6924878fb
42 измененных файлов с 639 добавлено и 133 удалено
  1. 46 0
      RackPeek.Domain/Persistence/Yaml/NotesStringYamlConverter.cs
  2. 13 0
      RackPeek.Domain/Persistence/Yaml/YamlResourceCollection.cs
  3. 7 1
      RackPeek.Domain/Resources/Hardware/AccessPoints/UpdateAccessPointUseCase.cs
  4. 6 2
      RackPeek.Domain/Resources/Hardware/Desktops/UpdateDesktopUseCase.cs
  5. 6 2
      RackPeek.Domain/Resources/Hardware/Firewalls/UpdateFirewallUseCase.cs
  6. 6 2
      RackPeek.Domain/Resources/Hardware/Laptops/UpdateLaptopUseCase.cs
  7. 6 2
      RackPeek.Domain/Resources/Hardware/Routers/UpdateRouterUseCase.cs
  8. 6 2
      RackPeek.Domain/Resources/Hardware/Servers/UpdateServerUseCase.cs
  9. 6 2
      RackPeek.Domain/Resources/Hardware/Switches/UpdateSwitchUseCase.cs
  10. 6 2
      RackPeek.Domain/Resources/Hardware/UpsUnits/UpdateUpsUseCase.cs
  11. 1 0
      RackPeek.Domain/Resources/Resource.cs
  12. 6 1
      RackPeek.Domain/Resources/Services/UseCases/UpdateServiceUseCase.cs
  13. 7 1
      RackPeek.Domain/Resources/SystemResources/UseCases/UpdateSystemUseCase.cs
  14. 35 0
      RackPeek.Web.Viewer/wwwroot/app.css
  15. 2 0
      RackPeek.Web/Components/App.razor
  16. 9 5
      RackPeek.Web/Components/Pages/Home.razor
  17. 24 27
      RackPeek.Web/wwwroot/app.css
  18. 0 1
      RackPeek.Web/wwwroot/console.js
  19. 30 8
      Shared.Rcl/AccessPoints/AccessPointCardComponent.razor
  20. 52 0
      Shared.Rcl/Components/MarkdownEditor.razor
  21. 49 0
      Shared.Rcl/Components/MarkdownViewer.razor
  22. 57 2
      Shared.Rcl/Desktops/DesktopCardComponent.razor
  23. 23 2
      Shared.Rcl/Firewalls/FirewallCardComponent.razor
  24. 58 2
      Shared.Rcl/Laptops/LaptopCardComponent.razor
  25. 23 2
      Shared.Rcl/Routers/RouterCardComponent.razor
  26. 57 1
      Shared.Rcl/Servers/ServerCardComponent.razor
  27. 17 1
      Shared.Rcl/Services/ServiceCardComponent.razor
  28. 2 1
      Shared.Rcl/Services/ServiceDetailsPage.razor
  29. 3 1
      Shared.Rcl/Services/ServiceEditModel.cs
  30. 2 1
      Shared.Rcl/Services/ServicesListComponent.razor
  31. 1 0
      Shared.Rcl/Shared.Rcl.csproj
  32. 23 2
      Shared.Rcl/Switches/SwitchCardComponent.razor
  33. 19 1
      Shared.Rcl/Systems/SystemCardComponent.razor
  34. 3 1
      Shared.Rcl/Systems/SystemEditModel.cs
  35. 3 1
      Shared.Rcl/Systems/SystemsDetailsPage.razor
  36. 2 1
      Shared.Rcl/Systems/SystemsListComponent.razor
  37. 22 2
      Shared.Rcl/Ups/UpsCardComponent.razor
  38. 0 3
      Tests/EndToEnd/AccessPointE2ETests.cs
  39. 1 19
      Tests/EndToEnd/ServiceYamlE2ETests.cs
  40. 0 6
      Tests/EndToEnd/SwitchYamlE2ETests.cs
  41. 0 23
      Tests/EndToEnd/SystemYamlE2ETests.cs
  42. 0 3
      Tests/EndToEnd/UpsYamlE2ETests.cs

+ 46 - 0
RackPeek.Domain/Persistence/Yaml/NotesStringYamlConverter.cs

@@ -0,0 +1,46 @@
+using YamlDotNet.Core;
+using YamlDotNet.Core.Events;
+using YamlDotNet.Serialization;
+
+public sealed class NotesStringYamlConverter : IYamlTypeConverter
+{
+    public bool Accepts(Type type) => type == typeof(string);
+    public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
+    {
+        var scalar = parser.Consume<Scalar>();
+        return scalar.Value;
+    }
+
+    public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
+    {
+        if (value is null)
+        {
+            emitter.Emit(new Scalar(
+                AnchorName.Empty,
+                TagName.Empty,
+                "",
+                ScalarStyle.Plain,
+                true,
+                true));
+            return;
+        }
+
+        var s = (string)value;
+
+        if (s.Contains('\n'))
+        {
+            // Literal block style (|)
+            emitter.Emit(new Scalar(
+                AnchorName.Empty,
+                TagName.Empty,
+                s,
+                ScalarStyle.Literal,
+                true,
+                false));
+        }
+        else
+        {
+            emitter.Emit(new Scalar(s));
+        }
+    }
+}

+ 13 - 0
RackPeek.Domain/Persistence/Yaml/YamlResourceCollection.cs

@@ -83,6 +83,12 @@ public sealed class YamlResourceCollection(
 
             var serializer = new SerializerBuilder()
                 .WithNamingConvention(CamelCaseNamingConvention.Instance)
+                .WithTypeConverter(new StorageSizeYamlConverter())
+                .WithTypeConverter(new NotesStringYamlConverter())
+                .ConfigureDefaultValuesHandling(
+                    DefaultValuesHandling.OmitNull |
+                    DefaultValuesHandling.OmitEmptyCollections
+                )
                 .Build();
 
             var payload = new OrderedDictionary
@@ -110,6 +116,8 @@ public sealed class YamlResourceCollection(
             .WithNamingConvention(CamelCaseNamingConvention.Instance)
             .WithCaseInsensitivePropertyMatching()
             .WithTypeConverter(new StorageSizeYamlConverter())
+            .WithTypeConverter(new NotesStringYamlConverter())
+            
             .WithTypeDiscriminatingNodeDeserializer(options =>
             {
                 options.AddKeyValueTypeDiscriminator<Resource>("kind", new Dictionary<string, Type>
@@ -163,6 +171,11 @@ public sealed class YamlResourceCollection(
 
         var serializer = new SerializerBuilder()
             .WithNamingConvention(CamelCaseNamingConvention.Instance)
+            .WithTypeConverter(new NotesStringYamlConverter())
+            .ConfigureDefaultValuesHandling(
+                DefaultValuesHandling.OmitNull |
+                DefaultValuesHandling.OmitEmptyCollections
+            )
             .Build();
 
         var yaml = serializer.Serialize(resource);

+ 7 - 1
RackPeek.Domain/Resources/Hardware/AccessPoints/UpdateAccessPointUseCase.cs

@@ -8,7 +8,8 @@ public class UpdateAccessPointUseCase(IHardwareRepository repository) : IUseCase
     public async Task ExecuteAsync(
         string name,
         string? model = null,
-        double? speed = null
+        double? speed = null,
+        string? notes = null
     )
     {
         // ToDo validate / normalize all inputs
@@ -31,6 +32,11 @@ public class UpdateAccessPointUseCase(IHardwareRepository repository) : IUseCase
             ap.Speed = speed.Value;
         }
 
+        if (notes != null)
+        {
+            ap.Notes = notes;
+        }
+
         await repository.UpdateAsync(ap);
     }
 }

+ 6 - 2
RackPeek.Domain/Resources/Hardware/Desktops/UpdateDesktopUseCase.cs

@@ -9,7 +9,8 @@ public class UpdateDesktopUseCase(IHardwareRepository repository) : IUseCase
         string name,
         string? model = null,
         int? ramGb = null,
-        int? ramMts = null
+        int? ramMts = null,
+        string? notes = null
     )
     {
         // ToDo validate / normalize all inputs
@@ -37,7 +38,10 @@ public class UpdateDesktopUseCase(IHardwareRepository repository) : IUseCase
             desktop.Ram ??= new Ram();
             desktop.Ram.Mts = ramMts.Value;
         }
-
+        if (notes != null)
+        {
+            desktop.Notes = notes;
+        }
         await repository.UpdateAsync(desktop);
     }
 }

+ 6 - 2
RackPeek.Domain/Resources/Hardware/Firewalls/UpdateFirewallUseCase.cs

@@ -9,7 +9,8 @@ public class UpdateFirewallUseCase(IHardwareRepository repository) : IUseCase
         string name,
         string? model = null,
         bool? managed = null,
-        bool? poe = null
+        bool? poe = null,
+        string? notes = null
     )
     {
         // ToDo validate / normalize all inputs
@@ -29,7 +30,10 @@ public class UpdateFirewallUseCase(IHardwareRepository repository) : IUseCase
 
         if (poe.HasValue)
             firewallResource.Poe = poe.Value;
-
+        if (notes != null)
+        {
+            firewallResource.Notes = notes;
+        }
         await repository.UpdateAsync(firewallResource);
     }
 }

+ 6 - 2
RackPeek.Domain/Resources/Hardware/Laptops/UpdateLaptopUseCase.cs

@@ -9,7 +9,8 @@ public class UpdateLaptopUseCase(IHardwareRepository repository) : IUseCase
         string name,
         string? model = null,
         int? ramGb = null,
-        int? ramMts = null
+        int? ramMts = null,
+        string? notes = null
     )
     {
         // ToDo validate / normalize all inputs
@@ -37,7 +38,10 @@ public class UpdateLaptopUseCase(IHardwareRepository repository) : IUseCase
             laptop.Ram ??= new Ram();
             laptop.Ram.Mts = ramMts.Value;
         }
-
+        if (notes != null)
+        {
+            laptop.Notes = notes;
+        }
         await repository.UpdateAsync(laptop);
     }
 }

+ 6 - 2
RackPeek.Domain/Resources/Hardware/Routers/UpdateRouterUseCase.cs

@@ -9,7 +9,8 @@ public class UpdateRouterUseCase(IHardwareRepository repository) : IUseCase
         string name,
         string? model = null,
         bool? managed = null,
-        bool? poe = null
+        bool? poe = null,
+        string? notes = null
     )
     {
         // ToDo pass in properties as inputs, construct the entity in the usecase
@@ -30,7 +31,10 @@ public class UpdateRouterUseCase(IHardwareRepository repository) : IUseCase
 
         if (poe.HasValue)
             routerResource.Poe = poe.Value;
-
+        if (notes != null)
+        {
+            routerResource.Notes = notes;
+        }
         await repository.UpdateAsync(routerResource);
     }
 }

+ 6 - 2
RackPeek.Domain/Resources/Hardware/Servers/UpdateServerUseCase.cs

@@ -9,7 +9,8 @@ public class UpdateServerUseCase(IHardwareRepository repository) : IUseCase
         string name,
         int? ramGb = null,
         int? ramMts = null,
-        bool? ipmi = null
+        bool? ipmi = null,
+        string? notes = null
     )
     {
         // ToDo pass in properties as inputs, construct the entity in the usecase, ensure optional inputs are nullable
@@ -38,7 +39,10 @@ public class UpdateServerUseCase(IHardwareRepository repository) : IUseCase
 
         // ---- IPMI ----
         if (ipmi.HasValue) server.Ipmi = ipmi.Value;
-
+        if (notes != null)
+        {
+            server.Notes = notes;
+        }
         await repository.UpdateAsync(server);
     }
 }

+ 6 - 2
RackPeek.Domain/Resources/Hardware/Switches/UpdateSwitchUseCase.cs

@@ -9,7 +9,8 @@ public class UpdateSwitchUseCase(IHardwareRepository repository) : IUseCase
         string name,
         string? model = null,
         bool? managed = null,
-        bool? poe = null
+        bool? poe = null,
+        string? notes = null
     )
     {
         // ToDo pass in properties as inputs, construct the entity in the usecase, ensure optional inputs are nullable
@@ -30,7 +31,10 @@ public class UpdateSwitchUseCase(IHardwareRepository repository) : IUseCase
 
         if (poe.HasValue)
             switchResource.Poe = poe.Value;
-
+        if (notes != null)
+        {
+            switchResource.Notes = notes;
+        }
         await repository.UpdateAsync(switchResource);
     }
 }

+ 6 - 2
RackPeek.Domain/Resources/Hardware/UpsUnits/UpdateUpsUseCase.cs

@@ -8,7 +8,8 @@ public class UpdateUpsUseCase(IHardwareRepository repository) : IUseCase
     public async Task ExecuteAsync(
         string name,
         string? model = null,
-        int? va = null
+        int? va = null,
+        string? notes = null
     )
     {
         // ToDo pass in properties as inputs, construct the entity in the usecase, ensure optional inputs are nullable
@@ -26,7 +27,10 @@ public class UpdateUpsUseCase(IHardwareRepository repository) : IUseCase
 
         if (va.HasValue)
             ups.Va = va.Value;
-
+        if (notes != null)
+        {
+            ups.Notes = notes;
+        }
         await repository.UpdateAsync(ups);
     }
 }

+ 1 - 0
RackPeek.Domain/Resources/Resource.cs

@@ -22,6 +22,7 @@ public abstract class Resource
     public required string Name { get; set; }
 
     public Dictionary<string, string>? Tags { get; set; }
+    public string? Notes { get; set; }
 
     public static string KindToPlural(string kind)
     {

+ 6 - 1
RackPeek.Domain/Resources/Services/UseCases/UpdateServiceUseCase.cs

@@ -11,7 +11,8 @@ public class UpdateServiceUseCase(IServiceRepository repository, ISystemReposito
         int? port = null,
         string? protocol = null,
         string? url = null,
-        string? runsOn = null
+        string? runsOn = null,
+        string? notes = null
     )
     {
         // ToDo pass in properties as inputs, construct the entity in the usecase, ensure optional inputs are nullable
@@ -53,6 +54,10 @@ public class UpdateServiceUseCase(IServiceRepository repository, ISystemReposito
             var parentSystem = await systemRepo.GetByNameAsync(runsOn);
             if (parentSystem == null) throw new NotFoundException($"Parent system '{runsOn}' not found.");
             service.RunsOn = runsOn;
+        }        
+        if (notes != null)
+        {
+            service.Notes = notes;
         }
 
         await repository.UpdateAsync(service);

+ 7 - 1
RackPeek.Domain/Resources/SystemResources/UseCases/UpdateSystemUseCase.cs

@@ -11,7 +11,8 @@ public class UpdateSystemUseCase(ISystemRepository repository, IHardwareReposito
         string? os = null,
         int? cores = null,
         int? ram = null,
-        string? runsOn = null
+        string? runsOn = null,
+        string? notes = null
     )
     {
         // ToDo pass in properties as inputs, construct the entity in the usecase, ensure optional inputs are nullable
@@ -41,6 +42,11 @@ public class UpdateSystemUseCase(ISystemRepository repository, IHardwareReposito
         if (ram.HasValue)
             system.Ram = ram.Value;
 
+        if (notes != null)
+        {
+            system.Notes = notes;
+        }
+        
         if (!string.IsNullOrWhiteSpace(runsOn))
         {
             ThrowIfInvalid.ResourceName(runsOn);

+ 35 - 0
RackPeek.Web.Viewer/wwwroot/app.css

@@ -0,0 +1,35 @@
+.markdown h1 { font-size: 1.5rem; font-weight: 700; margin: 0.6em 0; }
+.markdown h2 { font-size: 1.3rem; font-weight: 600; margin: 0.5em 0; }
+.markdown h3 { font-size: 1.1rem; font-weight: 600; margin: 0.4em 0; }
+
+.markdown p { margin: 0.4em 0; }
+
+.markdown ul { list-style: disc; margin-left: 1.2rem; }
+.markdown ol { list-style: decimal; margin-left: 1.2rem; }
+
+.markdown li { margin: 0.2em 0; }
+
+.markdown code {
+    background: #27272a;
+    padding: 2px 5px;
+    border-radius: 4px;
+    font-size: 0.9em;
+}
+
+.markdown pre {
+    background: #18181b;
+    padding: 12px;
+    border-radius: 8px;
+    overflow-x: auto;
+}
+
+.markdown blockquote {
+    border-left: 3px solid #52525b;
+    padding-left: 10px;
+    color: #a1a1aa;
+    margin: 0.6em 0;
+}
+
+.markdown > :first-child {
+    margin-top: 0;
+}

+ 2 - 0
RackPeek.Web/Components/App.razor

@@ -10,6 +10,8 @@
     <HeadOutlet @rendermode="InteractiveServer"/>
     <script src="console.js"></script>
     <script src="tailwind.js"></script>
+    <link href="app.css" rel="stylesheet" />
+
 </head>
 
 <body class="bg-black">

+ 9 - 5
RackPeek.Web/Components/Pages/Home.razor

@@ -76,7 +76,7 @@
             </div>
 
 
-            <!-- Systems -->
+           <!-- Systems -->
             <div>
                 <div class="text-xs text-zinc-500 uppercase tracking-wider mb-3">
                     Systems
@@ -94,8 +94,10 @@
                                 <li class="text-zinc-400">Types</li>
                                 @foreach (var (type, count) in _system.SystemsByType.OrderByDescending(x => x.Value))
                                 {
-                                    <li class="text-zinc-500">
-                                        └─ @type (@count)
+                                    <li class="text-zinc-500 hover:text-emerald-300">
+                                        <NavLink href="@($"systems/list?type={type}")" class="block">
+                                            └─ @type (@count)
+                                        </NavLink>
                                     </li>
                                 }
                             </ul>
@@ -107,8 +109,10 @@
                                 <li class="text-zinc-400">Operating Systems</li>
                                 @foreach (var (os, count) in _system.SystemsByOs.OrderByDescending(x => x.Value))
                                 {
-                                    <li class="text-zinc-500">
-                                        └─ @os (@count)
+                                    <li class="text-zinc-500 hover:text-emerald-300">
+                                        <NavLink href="@($"systems/list?os={os}")" class="block">
+                                            └─ @os (@count)
+                                        </NavLink>
                                     </li>
                                 }
                             </ul>

+ 24 - 27
RackPeek.Web/wwwroot/app.css

@@ -1,38 +1,35 @@
-h1:focus {
-    outline: none;
-}
+.markdown h1 { font-size: 1.5rem; font-weight: 700; margin: 0.6em 0; }
+.markdown h2 { font-size: 1.3rem; font-weight: 600; margin: 0.5em 0; }
+.markdown h3 { font-size: 1.1rem; font-weight: 600; margin: 0.4em 0; }
 
-.valid.modified:not([type=checkbox]) {
-    outline: 1px solid #26b050;
-}
+.markdown p { margin: 0.4em 0; }
 
-.invalid {
-    outline: 1px solid #e50000;
-}
+.markdown ul { list-style: disc; margin-left: 1.2rem; }
+.markdown ol { list-style: decimal; margin-left: 1.2rem; }
 
-.validation-message {
-    color: #e50000;
-}
+.markdown li { margin: 0.2em 0; }
 
-.blazor-error-boundary {
-    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
-    padding: 1rem 1rem 1rem 3.7rem;
-    color: white;
+.markdown code {
+    background: #27272a;
+    padding: 2px 5px;
+    border-radius: 4px;
+    font-size: 0.9em;
 }
 
-.blazor-error-boundary::after {
-    content: "An error has occurred."
+.markdown pre {
+    background: #18181b;
+    padding: 12px;
+    border-radius: 8px;
+    overflow-x: auto;
 }
 
-.darker-border-checkbox.form-check-input {
-    border-color: #929292;
+.markdown blockquote {
+    border-left: 3px solid #52525b;
+    padding-left: 10px;
+    color: #a1a1aa;
+    margin: 0.6em 0;
 }
 
-.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
-    color: var(--bs-secondary-color);
-    text-align: end;
+.markdown > :first-child {
+    margin-top: 0;
 }
-
-.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
-    text-align: start;
-}

+ 0 - 1
RackPeek.Web/wwwroot/console.js

@@ -6,4 +6,3 @@ window.consoleHasSelection = () => {
     const sel = window.getSelection();
     return sel && sel.toString().length > 0;
 };
-

+ 30 - 8
Shared.Rcl/AccessPoints/AccessPointCardComponent.razor

@@ -64,8 +64,8 @@
             @if (_isEditing)
             {
                 <input class="w-full px-3 py-2 rounded-md
-                              bg-zinc-800 text-zinc-100
-                              border border-zinc-600"
+                          bg-zinc-800 text-zinc-100
+                          border border-zinc-600"
                        @bind="_edit.Model" />
             }
             else if (!string.IsNullOrWhiteSpace(AccessPoint.Model))
@@ -83,8 +83,8 @@
                 <input type="number"
                        step="0.1"
                        class="w-full px-3 py-2 rounded-md
-                              bg-zinc-800 text-zinc-100
-                              border border-zinc-600"
+                          bg-zinc-800 text-zinc-100
+                          border border-zinc-600"
                        @bind="_edit.Speed" />
             }
             else if (AccessPoint.Speed is not null)
@@ -95,7 +95,25 @@
             }
         </div>
 
+        <div class="md:col-span-2">
+            <div class="text-zinc-400 mb-1">Notes</div>
+
+            @if (_isEditing)
+            {
+                <MarkdownEditor
+                    @bind-Value="_edit.Notes"
+                    ShowActionButtons="false" />
+            }
+            else
+            {
+                <MarkdownViewer
+                    Value="@AccessPoint.Notes"
+                    ShowEditButton="false" />
+            }
+        </div>
+
     </div>
+
 </div>
 
 <ConfirmModal
@@ -145,7 +163,6 @@
         _edit = AccessPointEditModel.From(AccessPoint);
         _isEditing = true;
     }
-
     async Task Save()
     {
         _isEditing = false;
@@ -153,11 +170,13 @@
         await UpdateUseCase.ExecuteAsync(
             AccessPoint.Name,
             _edit.Model,
-            _edit.Speed);
+            _edit.Speed,
+            _edit.Notes);
 
-        // update local view model
         AccessPoint.Model = _edit.Model;
         AccessPoint.Speed = _edit.Speed;
+        AccessPoint.Notes = _edit.Notes; 
+        
     }
 
     void Cancel()
@@ -206,14 +225,17 @@
     {
         public string? Model { get; set; }
         public double? Speed { get; set; }
+        public string? Notes { get; set; }
 
         public static AccessPointEditModel From(AccessPoint ap)
         {
             return new AccessPointEditModel
             {
                 Model = ap.Model,
-                Speed = ap.Speed
+                Speed = ap.Speed,
+                Notes = ap.Notes
             };
         }
     }
+
 }

+ 52 - 0
Shared.Rcl/Components/MarkdownEditor.razor

@@ -0,0 +1,52 @@
+<div class="border border-zinc-800 rounded p-4 bg-zinc-900">
+
+    @if (ShowActionButtons)
+    {
+        <div class="flex justify-end gap-3 mb-2 text-xs">
+            <button class="text-emerald-400 hover:text-emerald-300 transition"
+                    @onclick="HandleSave">
+                Save
+            </button>
+
+            <button class="text-zinc-500 hover:text-zinc-300 transition"
+                    @onclick="HandleCancel">
+                Cancel
+            </button>
+        </div>
+    }
+
+    <textarea
+        class="w-full h-64 bg-zinc-950 text-zinc-200 border border-zinc-700 rounded p-3 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-emerald-500"
+        value="@Value"
+        @oninput="HandleInput">
+    </textarea>
+
+</div>
+
+@code {
+    [Parameter] public string? Value { get; set; }
+    [Parameter] public EventCallback<string?> ValueChanged { get; set; }
+
+    [Parameter] public bool ShowActionButtons { get; set; } = true;
+
+    [Parameter] public EventCallback OnSave { get; set; }
+    [Parameter] public EventCallback OnCancel { get; set; }
+
+    async Task HandleInput(ChangeEventArgs e)
+    {
+        Value = e.Value?.ToString();
+        await ValueChanged.InvokeAsync(Value);
+    }
+
+    async Task HandleSave()
+    {
+        if (OnSave.HasDelegate)
+            await OnSave.InvokeAsync();
+    }
+
+    async Task HandleCancel()
+    {
+        if (OnCancel.HasDelegate)
+            await OnCancel.InvokeAsync();
+    }
+}

+ 49 - 0
Shared.Rcl/Components/MarkdownViewer.razor

@@ -0,0 +1,49 @@
+@using Markdig
+
+<div class="relative border border-zinc-800 rounded p-4 bg-zinc-900">
+
+    @if (ShowEditButton)
+    {
+        <div class="absolute top-2 right-2 z-10">
+            <button class="text-xs text-blue-400 hover:text-blue-300 transition"
+                    @onclick="HandleEdit">
+                @(string.IsNullOrWhiteSpace(Value) ? "Add" : "Edit")
+            </button>
+        </div>
+    }
+
+    @if (string.IsNullOrWhiteSpace(Value))
+    {
+        <div class="text-sm text-zinc-500 italic">
+            No notes
+        </div>
+    }
+    else
+    {
+        <div class="markdown text-sm">
+            @((MarkupString)_html)
+        </div>
+    }
+
+</div>
+
+@code {
+    [Parameter] public string? Value { get; set; }
+    [Parameter] public bool ShowEditButton { get; set; }
+    [Parameter] public EventCallback OnEdit { get; set; }
+
+    private string _html = string.Empty;
+
+    private static readonly MarkdownPipeline Pipeline =
+        new MarkdownPipelineBuilder()
+            .UseAdvancedExtensions()
+            .Build();
+
+    protected override void OnParametersSet()
+    {
+        _html = Markdown.ToHtml(Value ?? string.Empty, Pipeline);
+    }
+
+    private Task HandleEdit()
+        => OnEdit.HasDelegate ? OnEdit.InvokeAsync() : Task.CompletedTask;
+}

+ 57 - 2
Shared.Rcl/Desktops/DesktopCardComponent.razor

@@ -33,7 +33,6 @@
     <div class="flex justify-between items-center mb-3">
         <div class="text-zinc-100 hover:text-emerald-300">
             <NavLink href="@($"resources/hardware/{Desktop.Name}")" class="block">
-
                 @Desktop.Name
             </NavLink>
         </div>
@@ -218,6 +217,27 @@
         </div>
 
     </div>
+    
+    <div class="md:col-span-2">
+        <div class="text-zinc-400 mb-1">Notes</div>
+
+        @if (!_editingNotes)
+        {
+            <MarkdownViewer
+                Value="@Desktop.Notes"
+                ShowEditButton="true"
+                OnEdit="BeginNotesEdit" />
+        }
+        else
+        {
+            <MarkdownEditor
+                @bind-Value="_notesDraft"
+                ShowActionButtons="true"
+                OnSave="SaveNotes"
+                OnCancel="CancelNotesEdit" />
+        }
+    </div>
+    
 </div>
 <CpuModal
     IsOpen="@_cpuModalOpen"
@@ -557,4 +577,39 @@
         Nav.NavigateTo($"resources/hardware/{newName}");
     }
 
-}
+}
+
+@code
+{
+    bool _editingNotes;
+    string? _notesDraft;
+
+    void BeginNotesEdit()
+    {
+        _editingNotes = true;
+        _notesDraft = Desktop.Notes; // draft buffer
+    }
+
+    void CancelNotesEdit()
+    {
+        _editingNotes = false;
+        _notesDraft = null; // discard
+    }
+
+    async Task SaveNotes()
+    {
+        _editingNotes = false;
+
+        await UpdateDesktopUseCase.ExecuteAsync(
+            Desktop.Name,
+            Desktop.Model,
+            Desktop.Ram?.Size,
+            Desktop.Ram?.Mts,
+            _notesDraft);
+
+        Desktop = await GetDesktopUseCase.ExecuteAsync(Desktop.Name);
+        _notesDraft = null;
+    }
+
+    
+}

+ 23 - 2
Shared.Rcl/Firewalls/FirewallCardComponent.razor

@@ -151,6 +151,24 @@
                 }
             }
         </div>
+        
+        <div class="md:col-span-2">
+            <div class="text-zinc-400 mb-1">Notes</div>
+
+            @if (_isEditing)
+            {
+                <MarkdownEditor
+                    @bind-Value="_edit.Notes"
+                    ShowActionButtons="false" />
+            }
+            else
+            {
+                <MarkdownViewer
+                    Value="@Firewall.Notes"
+                    ShowEditButton="false" />
+            }
+        </div>
+        
     </div>
 </div>
 
@@ -207,7 +225,8 @@
             Firewall.Name,
             _edit.Model,
             _edit.Managed,
-            _edit.Poe);
+            _edit.Poe,
+            _edit.Notes);
 
         Firewall = await GetFirewallUseCase.ExecuteAsync(Firewall.Name);
     }
@@ -279,6 +298,7 @@
         public string? Model { get; set; }
         public bool? Managed { get; set; }
         public bool? Poe { get; set; }
+        public string? Notes { get; set; }
 
         public static FirewallEditModel From(Firewall firewall)
         {
@@ -286,7 +306,8 @@
             {
                 Model = firewall.Model,
                 Managed = firewall.Managed,
-                Poe = firewall.Poe
+                Poe = firewall.Poe,
+                Notes = firewall.Notes
             };
         }
     }

+ 58 - 2
Shared.Rcl/Laptops/LaptopCardComponent.razor

@@ -182,6 +182,26 @@
         </div>
 
     </div>
+    <div class="md:col-span-2">
+        <div class="text-zinc-400 mb-1">Notes</div>
+
+        @if (!_editingNotes)
+        {
+            <MarkdownViewer
+                Value="@Laptop.Notes"
+                ShowEditButton="true"
+                OnEdit="BeginNotesEdit" />
+        }
+        else
+        {
+            <MarkdownEditor
+                @bind-Value="_notesDraft"
+                ShowActionButtons="true"
+                OnSave="SaveNotes"
+                OnCancel="CancelNotesEdit" />
+        }
+    </div>
+    
 </div>
 <CpuModal
     IsOpen="@_cpuModalOpen"
@@ -253,7 +273,7 @@
     private async Task HandleRamSubmit(Ram value)
     {
         _isRamModalOpen = false;
-        await UpdateLaptopUseCase.ExecuteAsync(Laptop.Name, Laptop.Model, value.Size, value.Mts);
+        await UpdateLaptopUseCase.ExecuteAsync(Laptop.Name, Laptop.Model, value.Size, value.Mts, Laptop.Notes);
         Laptop = await GetLaptopUseCase.ExecuteAsync(Laptop.Name);
     }
 
@@ -461,4 +481,40 @@
         Nav.NavigateTo($"resources/hardware/{newName}");
     }
 
-}
+}
+
+
+@code
+{
+    bool _editingNotes;
+    string? _notesDraft;
+
+    void BeginNotesEdit()
+    {
+        _editingNotes = true;
+        _notesDraft = Laptop.Notes; // draft buffer
+    }
+
+    void CancelNotesEdit()
+    {
+        _editingNotes = false;
+        _notesDraft = null; // discard
+    }
+
+    async Task SaveNotes()
+    {
+        _editingNotes = false;
+
+        await UpdateLaptopUseCase.ExecuteAsync(
+            Laptop.Name,
+            Laptop.Model,
+            Laptop.Ram?.Size,
+            Laptop.Ram?.Mts,
+            _notesDraft);
+
+        Laptop = await GetLaptopUseCase.ExecuteAsync(Laptop.Name);
+        _notesDraft = null;
+    }
+
+    
+}

+ 23 - 2
Shared.Rcl/Routers/RouterCardComponent.razor

@@ -148,6 +148,24 @@
                 }
             }
         </div>
+        
+        <div class="md:col-span-2">
+            <div class="text-zinc-400 mb-1">Notes</div>
+
+            @if (_isEditing)
+            {
+                <MarkdownEditor
+                    @bind-Value="_edit.Notes"
+                    ShowActionButtons="false" />
+            }
+            else
+            {
+                <MarkdownViewer
+                    Value="@Router.Notes"
+                    ShowEditButton="false" />
+            }
+        </div>
+        
     </div>
 </div>
 
@@ -204,7 +222,8 @@
             Router.Name,
             _edit.Model,
             _edit.Managed,
-            _edit.Poe);
+            _edit.Poe,
+            _edit.Notes);
 
         Router = await GetRouterUseCase.ExecuteAsync(Router.Name);
     }
@@ -276,6 +295,7 @@
         public string? Model { get; set; }
         public bool? Managed { get; set; }
         public bool? Poe { get; set; }
+        public string? Notes { get; set; }
 
         public static RouterEditModel From(Router Router)
         {
@@ -283,7 +303,8 @@
             {
                 Model = Router.Model,
                 Managed = Router.Managed,
-                Poe = Router.Poe
+                Poe = Router.Poe,
+                Notes = Router.Notes
             };
         }
     }

+ 57 - 1
Shared.Rcl/Servers/ServerCardComponent.razor

@@ -225,6 +225,26 @@
 
 
     </div>
+    <div class="md:col-span-2">
+        <div class="text-zinc-400 mb-1">Notes</div>
+
+        @if (!_editingNotes)
+        {
+            <MarkdownViewer
+                Value="@Server.Notes"
+                ShowEditButton="true"
+                OnEdit="BeginNotesEdit" />
+        }
+        else
+        {
+            <MarkdownEditor
+                @bind-Value="_notesDraft"
+                ShowActionButtons="true"
+                OnSave="SaveNotes"
+                OnCancel="CancelNotesEdit" />
+        }
+    </div>
+    
 </div>
 
 <CpuModal
@@ -566,4 +586,40 @@
         Nav.NavigateTo($"resources/hardware/{newName}");
     }
 
-}
+}
+
+
+@code
+{
+    bool _editingNotes;
+    string? _notesDraft;
+
+    void BeginNotesEdit()
+    {
+        _editingNotes = true;
+        _notesDraft = Server.Notes; // draft buffer
+    }
+
+    void CancelNotesEdit()
+    {
+        _editingNotes = false;
+        _notesDraft = null; // discard
+    }
+
+    async Task SaveNotes()
+    {
+        _editingNotes = false;
+
+        await UpdateServerUseCase.ExecuteAsync(
+            Server.Name,
+            Server.Ram?.Size,
+            Server.Ram?.Mts,
+            Server.Ipmi,
+            _notesDraft);
+
+        Server = await GetServerUseCase.ExecuteAsync(Server.Name);
+        _notesDraft = null;
+    }
+
+    
+}

+ 17 - 1
Shared.Rcl/Services/ServiceCardComponent.razor

@@ -174,7 +174,22 @@
                 </NavLink>
             }
         </div>
+        <div class="md:col-span-2">
+            <div class="text-zinc-400 mb-1">Notes</div>
 
+            @if (_isEditing)
+            {
+                <MarkdownEditor
+                    @bind-Value="_edit.Notes"
+                    ShowActionButtons="false" />
+            }
+            else
+            {
+                <MarkdownViewer
+                    Value="@Service.Notes"
+                    ShowEditButton="false" />
+            }
+        </div>
     </div>
 </div>
 
@@ -269,7 +284,8 @@
             Service.Network?.Port,
             Service.Network?.Protocol,
             Service.Network?.Url,
-            name);
+            name,
+            Service.Notes);
         Service = await GetServiceUseCase.ExecuteAsync(Service.Name);
         _edit = ServiceEditModel.From(Service);
     }

+ 2 - 1
Shared.Rcl/Services/ServiceDetailsPage.razor

@@ -52,7 +52,8 @@
             edit.Port,
             edit.Protocol,
             edit.Url,
-            edit.RunsOn
+            edit.RunsOn,
+                edit.Notes
         );
     }
 

+ 3 - 1
Shared.Rcl/Services/ServiceEditModel.cs

@@ -10,6 +10,7 @@ public sealed class ServiceEditModel
     public string? Protocol { get; set; }
     public string? Url { get; set; }
     public string? RunsOn { get; set; }
+    public string? Notes { get; set; }
 
     public static ServiceEditModel From(Service s)
     {
@@ -20,7 +21,8 @@ public sealed class ServiceEditModel
             Port = s.Network?.Port,
             Protocol = s.Network?.Protocol,
             Url = s.Network?.Url,
-            RunsOn = s.RunsOn
+            RunsOn = s.RunsOn,
+            Notes = s.Notes
         };
     }
 }

+ 2 - 1
Shared.Rcl/Services/ServicesListComponent.razor

@@ -47,7 +47,8 @@
             edit.Port,
             edit.Protocol,
             edit.Url,
-            edit.RunsOn
+            edit.RunsOn,
+            edit.Notes
         );
     }
 

+ 1 - 0
Shared.Rcl/Shared.Rcl.csproj

@@ -14,6 +14,7 @@
     </ItemGroup>
 
     <ItemGroup>
+        <PackageReference Include="Markdig" Version="0.45.0" />
         <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.0"/>
         <PackageReference Include="Spectre.Console.Cli" Version="0.53.1" />
         <PackageReference Include="Spectre.Console.Testing" Version="0.54.0" />

+ 23 - 2
Shared.Rcl/Switches/SwitchCardComponent.razor

@@ -149,6 +149,24 @@
                 }
             }
         </div>
+        
+        <div class="md:col-span-2">
+            <div class="text-zinc-400 mb-1">Notes</div>
+
+            @if (_isEditing)
+            {
+                <MarkdownEditor
+                    @bind-Value="_edit.Notes"
+                    ShowActionButtons="false" />
+            }
+            else
+            {
+                <MarkdownViewer
+                    Value="@Switch.Notes"
+                    ShowEditButton="false" />
+            }
+        </div>
+        
     </div>
 </div>
 
@@ -206,7 +224,8 @@
             Switch.Name,
             _edit.Model,
             _edit.Managed,
-            _edit.Poe);
+            _edit.Poe,
+            _edit.Notes);
 
         Switch = await GetSwitchUseCase.ExecuteAsync(Switch.Name);
     }
@@ -278,6 +297,7 @@
         public string? Model { get; set; }
         public bool? Managed { get; set; }
         public bool? Poe { get; set; }
+        public string? Notes { get; set; }
 
         public static SwitchEditModel From(Switch Switch)
         {
@@ -285,7 +305,8 @@
             {
                 Model = Switch.Model,
                 Managed = Switch.Managed,
-                Poe = Switch.Poe
+                Poe = Switch.Poe,
+                Notes = Switch.Notes
             };
         }
     }

+ 19 - 1
Shared.Rcl/Systems/SystemCardComponent.razor

@@ -220,6 +220,23 @@
             }
         </div>
 
+        <div class="md:col-span-2">
+            <div class="text-zinc-400 mb-1">Notes</div>
+
+            @if (_isEditing)
+            {
+                <MarkdownEditor
+                    @bind-Value="_edit.Notes"
+                    ShowActionButtons="false" />
+            }
+            else
+            {
+                <MarkdownViewer
+                    Value="@System.Notes"
+                    ShowEditButton="false" />
+            }
+        </div>
+        
     </div>
 </div>
 
@@ -294,7 +311,8 @@
             System.Os,
             System.Cores,
             System.Ram,
-            name);
+            name,
+            System.Notes);
         System = await GetSystemUseCase.ExecuteAsync(System.Name);
         _edit = SystemEditModel.From(System);
     }

+ 3 - 1
Shared.Rcl/Systems/SystemEditModel.cs

@@ -10,6 +10,7 @@ public sealed class SystemEditModel
     public int? Cores { get; set; }
     public int? Ram { get; set; }
     public string? RunsOn { get; set; }
+    public string? Notes { get; set; }
 
     public static SystemEditModel From(SystemResource system)
     {
@@ -20,7 +21,8 @@ public sealed class SystemEditModel
             Os = system.Os,
             Cores = system.Cores,
             Ram = system.Ram,
-            RunsOn = system.RunsOn
+            RunsOn = system.RunsOn,
+            Notes = system.Notes
         };
     }
 }

+ 3 - 1
Shared.Rcl/Systems/SystemsDetailsPage.razor

@@ -2,6 +2,7 @@
 @using RackPeek.Domain.Resources.Hardware
 @using RackPeek.Domain.Resources.SystemResources
 @using RackPeek.Domain.Resources.SystemResources.UseCases
+@using Shared.Rcl.Services
 @inject ISystemRepository SystemRepository
 @inject UpdateSystemUseCase UpdateSystemUseCase
 @inject GetSystemServiceTreeUseCase GetSystemServiceTreeUseCase
@@ -74,7 +75,8 @@
             edit.Os,
             edit.Cores,
             edit.Ram,
-            edit.RunsOn
+            edit.RunsOn,
+                edit.Notes
         );
     }
 

+ 2 - 1
Shared.Rcl/Systems/SystemsListComponent.razor

@@ -74,7 +74,8 @@
             edit.Os,
             edit.Cores,
             edit.Ram,
-            edit.RunsOn
+            edit.RunsOn,
+                edit.Notes
         );
 
         await Reload();

+ 22 - 2
Shared.Rcl/Ups/UpsCardComponent.razor

@@ -101,6 +101,23 @@
                 <div class="text-zinc-300">@Ups.Va VA</div>
             }
         </div>
+        
+        <div class="md:col-span-2">
+            <div class="text-zinc-400 mb-1">Notes</div>
+
+            @if (_isEditing)
+            {
+                <MarkdownEditor
+                    @bind-Value="_edit.Notes"
+                    ShowActionButtons="false" />
+            }
+            else
+            {
+                <MarkdownViewer
+                    Value="@Ups.Notes"
+                    ShowEditButton="false" />
+            }
+        </div>
     </div>
 </div>
 
@@ -153,7 +170,8 @@
         await UpdateUpsUseCase.ExecuteAsync(
             Ups.Name,
             _edit.Model,
-            _edit.Va);
+            _edit.Va,
+            _edit.Notes);
 
         Ups = await GetUpsUseCase.ExecuteAsync(Ups.Name);
     }
@@ -182,13 +200,15 @@
     {
         public string? Model { get; set; }
         public int? Va { get; set; }
+        public string? Notes { get; set; }
 
         public static UpsEditModel From(Ups ups)
         {
             return new UpsEditModel
             {
                 Model = ups.Model,
-                Va = ups.Va
+                Va = ups.Va,
+                Notes = ups.Notes
             };
         }
     }

+ 0 - 3
Tests/EndToEnd/AccessPointE2ETests.cs

@@ -48,7 +48,6 @@ public class AccessPointYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper ou
                        model: Unifi-U6-Lite
                        speed: 1
                        name: ap01
-                       tags: 
 
                      """, yaml);
 
@@ -69,12 +68,10 @@ public class AccessPointYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper ou
                        model: Unifi-U6-Lite
                        speed: 1
                        name: ap01
-                       tags: 
                      - kind: AccessPoint
                        model: Aruba-AP-515
                        speed: 2.5
                        name: ap02
-                       tags: 
 
                      """, yaml);
 

+ 1 - 19
Tests/EndToEnd/ServiceYamlE2ETests.cs

@@ -31,10 +31,7 @@ public class ServiceYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper output
         Assert.Equal("""
                      resources:
                      - kind: Service
-                       network: 
-                       runsOn: 
                        name: immich
-                       tags: 
 
                      """, yaml);
         (output, yaml) = await ExecuteAsync("systems", "add", "vm01");
@@ -56,16 +53,8 @@ public class ServiceYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper output
                          url: http://timmoth.lan:80
                        runsOn: vm01
                        name: immich
-                       tags: 
                      - kind: System
-                       type: 
-                       os: 
-                       cores: 
-                       ram: 
-                       drives: 
-                       runsOn: 
                        name: vm01
-                       tags: 
 
                      """, yaml);
 
@@ -79,14 +68,7 @@ public class ServiceYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper output
         Assert.Equal("""
                      resources:
                      - kind: System
-                       type: 
-                       os: 
-                       cores: 
-                       ram: 
-                       drives: 
-                       runsOn: 
                        name: vm01
-                       tags: 
 
                      """, yaml);
 
@@ -141,7 +123,7 @@ public class ServiceYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper output
 
                      """, output);
 
-        // Report systems
+        // Report systemså
         (output, yaml) = await ExecuteAsync("services", "summary");
         Assert.Equal("""
                      ╭────────┬───────────────┬──────┬──────────┬──────────────────────┬────────────╮

+ 0 - 6
Tests/EndToEnd/SwitchYamlE2ETests.cs

@@ -43,9 +43,7 @@ public class SwitchYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputH
                        model: Netgear GS108
                        managed: true
                        poe: true
-                       ports: 
                        name: sw01
-                       tags: 
 
                      """, yaml);
 
@@ -63,16 +61,12 @@ public class SwitchYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputH
                        model: Netgear GS108
                        managed: true
                        poe: true
-                       ports: 
                        name: sw01
-                       tags: 
                      - kind: Switch
                        model: TP-Link TL-SG108E
                        managed: false
                        poe: false
-                       ports: 
                        name: sw02
-                       tags: 
 
                      """, yaml);
 

+ 0 - 23
Tests/EndToEnd/SystemYamlE2ETests.cs

@@ -35,23 +35,9 @@ public class SystemYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputH
         Assert.Equal("""
                      resources:
                      - kind: Server
-                       cpus: 
-                       ram: 
-                       drives: 
-                       nics: 
-                       gpus: 
-                       ipmi: 
                        name: hypervisor01
-                       tags: 
                      - kind: System
-                       type: 
-                       os: 
-                       cores: 
-                       ram: 
-                       drives: 
-                       runsOn: 
                        name: host01
-                       tags: 
 
                      """, yaml);
 
@@ -70,23 +56,14 @@ public class SystemYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputH
         Assert.Equal("""
                      resources:
                      - kind: Server
-                       cpus: 
-                       ram: 
-                       drives: 
-                       nics: 
-                       gpus: 
-                       ipmi: 
                        name: hypervisor01
-                       tags: 
                      - kind: System
                        type: baremetal
                        os: ubuntu-22.04
                        cores: 4
                        ram: 8192
-                       drives: 
                        runsOn: hypervisor01
                        name: host01
-                       tags: 
 
                      """, yaml);
 

+ 0 - 3
Tests/EndToEnd/UpsYamlE2ETests.cs

@@ -48,7 +48,6 @@ public class UpsYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputHelp
                        model: APC Smart-UPS 1500
                        va: 1500
                        name: ups01
-                       tags: 
 
                      """, yaml);
 
@@ -70,12 +69,10 @@ public class UpsYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputHelp
                        model: APC Smart-UPS 1500
                        va: 1500
                        name: ups01
-                       tags: 
                      - kind: Ups
                        model: CyberPower CP1500PFCLCD
                        va: 1500
                        name: ups02
-                       tags: 
 
                      """, yaml);