Files
shopadmin/ShopAdmin/Components/CreateProduct.razor
T
Khwezi Mngoma 84a50e64bf
continuous-integration/drone/pr Build is passing
Generalised datetime picker into component for reuse
2026-05-20 17:14:11 +02:00

172 lines
11 KiB
Plaintext

@using Microsoft.AspNetCore.Components.Forms
<div class="create-product-shell">
<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="create-product-container">
<EditForm Model="@ProductModel" OnValidSubmit="HandleValidSubmit" class="form-entry-canvas">
<DataAnnotationsValidator />
<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">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">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-row">
<div class="console-field-group">
<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">ISBN Reference</label>
<InputText @bind-Value="ProductModel.Isbn" class="console-input" placeholder="e.g., 978-0393312836" />
<ValidationMessage For="@(() => ProductModel.Isbn)" style="color: #ff5722; font-size: 0.75rem;" />
</div>
</div>
<div class="console-field-row">
<div class="console-field-group">
<label class="console-field-label">Author / Creator</label>
<InputText @bind-Value="ProductModel.Author" class="console-input" placeholder="e.g., William Gibson" />
<ValidationMessage For="@(() => ProductModel.Author)" style="color: #ff5722; font-size: 0.75rem;" />
</div>
<div class="console-field-group">
<label class="console-field-label">Date of Publication</label>
<ConsoleDatePicker @bind-Value="ProductModel.PublishDate" />
</div>
</div>
<div class="console-field-group">
<label class="console-field-label">Copyright Information</label>
<InputText @bind-Value="ProductModel.CopyrightInfo" class="console-input" placeholder="e.g., © 1984 William Gibson. All rights reserved." />
<ValidationMessage For="@(() => ProductModel.CopyrightInfo)" 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-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))
{
<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 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="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
{
<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 class="form-action-footer">
<button type="submit" class="btn-apply-filters">Commit Record Ledger</button>
</div>
</EditForm>
</div>
</div>