Completed create product component
continuous-integration/drone/pr Build is failing

This commit is contained in:
Khwezi Mngoma
2026-05-20 11:59:58 +02:00
parent 51946a1388
commit 8f26d5fbfa
7 changed files with 553 additions and 269 deletions
+122 -94
View File
@@ -1,119 +1,147 @@
@using Microsoft.AspNetCore.Components.Forms
<div class="create-product-container">
<EditForm Model="@ProductModel" OnValidSubmit="HandleValidSubmit" class="form-entry-canvas">
<DataAnnotationsValidator />
<div class="create-product-shell">
<div class="form-scroll-viewport">
<div class="form-section-header">
<span class="panel-title-lbl field-accent-tag">Initialization Sequence</span>
<p class="text-muted">Register a new asset node into the core product catalog matrix.</p>
<div class="book-preview-drawer @(string.IsNullOrEmpty(ActivePreviewUrl) ? "" : "is-open")">
@if (!string.IsNullOrEmpty(ActivePreviewUrl))
{
<button type="button" class="btn-close-preview-floating" title="Collapse Frame" @onclick="ClosePreviewDrawer">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
<div class="drawer-portrait-frame">
<img src="@ActivePreviewUrl" alt="Active Book Design Preview" />
</div>
}
</div>
<div class="form-grid-layout">
<div class="create-product-container">
<EditForm Model="@ProductModel" OnValidSubmit="HandleValidSubmit" class="form-entry-canvas">
<DataAnnotationsValidator />
<div class="form-column">
<div class="form-scroll-viewport">
<div class="form-section-header">
<span class="field-accent-tag">PRODUCT MASTER LEDGER</span>
<p>Provision catalog items metadata, book cover assets, and supplementary chapter telemetry designs.</p>
</div>
<div class="form-text-inputs-section">
<div class="console-field-group">
<label class="console-field-label">Product Name</label>
<InputText @bind-Value="ProductModel.Name" placeholder="e.g., Quantum Link Core Subsystem" class="console-input" />
<label class="console-field-label">Book Title</label>
<InputText @bind-Value="ProductModel.Name" class="console-input" placeholder="e.g., Neuromancer" />
<ValidationMessage For="@(() => ProductModel.Name)" style="color: #ff5722; font-size: 0.75rem;" />
</div>
<div class="console-field-group">
<label class="console-field-label">Telemetry Short Summary</label>
<InputText @bind-Value="ProductModel.Summary" placeholder="Brief technical summary tooltip description..." class="console-input" />
<label class="console-field-label">Short Summary</label>
<InputText @bind-Value="ProductModel.Summary" class="console-input" placeholder="Brief catchphrase description metadata line..." />
<ValidationMessage For="@(() => ProductModel.Summary)" style="color: #ff5722; font-size: 0.75rem;" />
</div>
<div class="console-field-group">
<label class="console-field-label">Deep System Description</label>
<InputTextArea @bind-Value="ProductModel.Description" rows="5" placeholder="Provide raw configuration guidelines, catalog notes, or full logistical parameters..." class="console-textarea" />
<label class="console-field-label">Base Ledger Price (ZAR)</label>
<InputNumber @bind-Value="ProductModel.Price" class="console-input" placeholder="0.00" />
<ValidationMessage For="@(() => ProductModel.Price)" style="color: #ff5722; font-size: 0.75rem;" />
</div>
<div class="console-field-group">
<label class="console-field-label">Full Catalog Description</label>
<InputTextArea @bind-Value="ProductModel.Description" class="console-textarea" rows="4" placeholder="Enter extended markdown contents..." />
<ValidationMessage For="@(() => ProductModel.Description)" style="color: #ff5722; font-size: 0.75rem;" />
</div>
</div>
<div class="form-column">
<div class="console-field-group">
<label class="console-field-label">Primary Engine Asset (Image URL)</label>
<div class="input-asset-addon">
<InputText @bind-Value="ProductModel.ImageUrl" placeholder="https://assets.litecharms.internal/nodes/primary.png" class="console-input asset-path-input" />
<div class="asset-preview-stub">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2" /><circle cx="8.5" cy="8.5" r="1.5" /><polyline points="21 15 16 10 5 21" /></svg>
<div class="form-media-deck-section">
<div class="form-section-header" style="margin-bottom: 1rem; padding-bottom: 0.5rem;">
<p style="text-transform: uppercase; font-weight: 600; color: #cbd5e1; font-size: 0.8rem; letter-spacing: 0.05em;">Media Assets Node Array</p>
</div>
<div class="media-deck-row">
<div class="console-field-group">
<label class="console-field-label">Primary Cover</label>
<div class="book-cover-dropzone">
<InputFile OnChange="HandleMainImageUpload" accept=".png,.jpg,.jpeg,.webp" class="hidden-file-input" id="main-image-file" />
@if (string.IsNullOrEmpty(ProductModel.ImageUrl))
{
/* Clicking anywhere inside this label launches the file system picker */
<label for="main-image-file" class="dropzone-interactive-layer">
<div class="empty-slot-blueprint">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
<span style="font-size: 0.7rem; font-family: monospace; color: #475569; margin-top: 0.5rem;">UPLOAD COVER</span>
</div>
</label>
}
else
{
<div class="dropzone-active-preview">
<img src="@ProductModel.ImageUrl" alt="Main Book Cover" />
<div class="image-actions-overlay">
<button type="button" class="btn-micro-action" title="Preview Image" @onclick="() => SetPreviewActive(ProductModel.ImageUrl)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
</button>
<button type="button" class="btn-micro-action danger" title="Remove Asset" @onclick="ClearMainImage">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
</div>
}
</div>
</div>
</div>
<div class="console-field-group spacing-top-modifier">
<label class="console-field-label">
Telemetry Media Thumbnails <span class="text-mono node-dim-label">(Max 5 Slots)</span>
</label>
<div class="console-field-group">
<label class="console-field-label">Additional Media Slots</label>
<div class="thumbnail-deck-grid">
@for (int i = 0; i < 5; i++)
{
var index = i;
<div class="thumbnail-slot-node @(HasAssetAt(index) ? "populated" : "empty")">
@if (HasAssetAt(index))
{
<img src="@ProductModel.Thumbnails[index]" alt="Slot @(index + 1)" />
<div class="thumbnail-deck-grid">
@for (int i = 0; i < 5; i++)
{
var index = i;
<div class="thumbnail-slot-node @(HasAssetAt(index) ? "populated" : "empty")">
@if (HasAssetAt(index))
{
<img src="@ProductModel.Thumbnails[index]" alt="Slot @(index + 1)" />
<button type="button" class="btn-clear-slot" @onclick="() => RemoveThumbnailAt(index)">✕</button>
}
else
{
<div class="empty-slot-blueprint">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
<span class="text-mono">0@(index + 1)</span>
</div>
}
</div>
}
<div class="image-actions-overlay">
<button type="button" class="btn-micro-action" title="Preview Image" @onclick="() => SetPreviewActive(ProductModel.Thumbnails[index])">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
</button>
<button type="button" class="btn-micro-action danger" title="Remove Asset" @onclick="() => RemoveThumbnailAt(index)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
}
else
{
/* Clean hidden execution context matched back to label action surfaces */
<InputFile OnChange="@(e => HandleThumbnailUpload(e, index))" accept=".png,.jpg,.jpeg,.webp" class="hidden-file-input" id="@($"thumb-file-{index}")" />
<label for="@($"thumb-file-{index}")" class="empty-slot-blueprint">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
<span style="font-size: 0.65rem; font-family: monospace; margin-top: 0.25rem;">0@(index + 1)</span>
</label>
}
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="form-action-footer">
<button type="button" class="btn-console-flat">Abort Registers</button>
<button type="submit" class="btn-apply-filters">Commit Node to Ledger</button>
</div>
</EditForm>
</div>
@code {
private Product ProductModel { get; set; } = new();
protected override void OnInitialized()
{
ProductModel.Active = true;
ProductModel.Thumbnails ??= new string[0];
}
private bool HasAssetAt(int index) =>
ProductModel.Thumbnails != null && index < ProductModel.Thumbnails.Length && !string.IsNullOrWhiteSpace(ProductModel.Thumbnails[index]);
private void RemoveThumbnailAt(int index)
{
if (ProductModel.Thumbnails == null) return;
var list = ProductModel.Thumbnails.ToList();
if (index < list.Count)
{
list.RemoveAt(index);
ProductModel.Thumbnails = list.ToArray();
}
}
private void HandleValidSubmit()
{
// Save operation business logic goes here
}
public class Product
{
public Guid Id { get; set; } = Guid.NewGuid();
public string? Name { get; set; }
public string? Summary { get; set; }
public string? Description { get; set; }
public string? ImageUrl { get; set; }
public string[]? Thumbnails { get; set; }
public bool Active { get; set; }
}
}
<div class="form-action-footer">
<button type="submit" class="btn-apply-filters">Commit Record Ledger</button>
</div>
</EditForm>
</div>
</div>