feat: Complete Phase 2 - Products responsive mobile cards

Added comprehensive mobile card layout for Products/Index, completing Phase 2 responsive design.

**Products Mobile View Features:**
- Horizontal layout with 80x80px product image on left
- Product name, category badge, price, and status on right
- Full description (100 chars) below header
- 2-column grid for Stock and Weight info
- Conditional badges for multi-buys and variants
- Full-width "View Details & Edit" button

**Mobile UX Highlights:**
- Larger product images (80px vs 50px desktop thumbnail)
- Price prominently displayed in green (fs-5)
- Stock status color-coded (success/warning)
- Variations clearly shown with icon badges
- Touch-friendly full-width action button

**Technical Implementation:**
- d-flex for image + info horizontal layout
- flex-grow-1 for responsive info section
- row g-2 for 2-column grid with gutters
- Conditional rendering for variations badges
- ARIA labels for accessibility

**Phase 2 Now Complete:**
 Categories - Simple cards with description
 Users - Minimal cards with user info
 ShippingRates - 2x2 grid for rate details
 VariantCollections - Cards with JSON preview
 Products - Rich cards with images and variations
 Orders - Mobile cards (already implemented)

All main Index views now mobile-optimized!

🚀 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
SysAdmin 2025-11-13 19:17:05 +00:00
parent 21a1078d64
commit a272373246

View File

@ -30,7 +30,8 @@
<div class="card-body">
@if (Model.Any())
{
<div class="table-responsive">
<!-- Desktop Table View (hidden on mobile) -->
<div class="table-responsive d-none d-lg-block">
<table class="table table-striped">
<thead>
<tr>
@ -118,6 +119,95 @@
</tbody>
</table>
</div>
<!-- Mobile Card View (visible on mobile only) -->
<div class="d-lg-none">
@foreach (var product in Model)
{
<div class="card mb-3">
<div class="card-body">
<div class="d-flex mb-3">
@if (product.Photos.Any())
{
<img src="@product.Photos.First().FilePath" alt="@product.Photos.First().AltText"
class="img-thumbnail me-3" style="width: 80px; height: 80px; object-fit: cover;">
}
else
{
<div class="bg-light d-flex align-items-center justify-content-center me-3" style="width: 80px; height: 80px; border-radius: var(--radius-md);">
<i class="fas fa-image fa-2x text-muted"></i>
</div>
}
<div class="flex-grow-1">
<h5 class="card-title mb-1">@product.Name</h5>
<span class="badge bg-secondary mb-2">@product.CategoryName</span>
<div class="d-flex align-items-center justify-content-between">
<strong class="text-success fs-5">£@product.Price</strong>
@if (product.IsActive)
{
<span class="badge bg-success">Active</span>
}
else
{
<span class="badge bg-danger">Inactive</span>
}
</div>
</div>
</div>
<p class="card-text text-muted small mb-2">
@product.Description.Substring(0, Math.Min(100, product.Description.Length))@(product.Description.Length > 100 ? "..." : "")
</p>
<div class="row g-2 mb-3">
<div class="col-6">
<div class="p-2 bg-light rounded">
<small class="text-muted d-block">Stock</small>
@if (product.StockQuantity > 0)
{
<strong class="text-success">@product.StockQuantity units</strong>
}
else
{
<strong class="text-warning">Out of stock</strong>
}
</div>
</div>
<div class="col-6">
<div class="p-2 bg-light rounded">
<small class="text-muted d-block">Weight</small>
<strong>@product.Weight @product.WeightUnit.ToString().ToLower()</strong>
</div>
</div>
</div>
@if (product.MultiBuys.Any() || product.Variants.Any())
{
<div class="mb-3">
@if (product.MultiBuys.Any())
{
<span class="badge bg-info me-1">
<i class="fas fa-shopping-basket" aria-hidden="true"></i> @product.MultiBuys.Count() multi-buys
</span>
}
@if (product.Variants.Any())
{
<span class="badge bg-success">
<i class="fas fa-swatchbook" aria-hidden="true"></i> @product.Variants.Count() variants
</span>
}
</div>
}
<div class="d-grid">
<a href="@Url.Action("Edit", new { id = product.Id })" class="btn btn-outline-primary" aria-label="View details for @product.Name">
<i class="fas fa-info-circle" aria-hidden="true"></i> View Details & Edit
</a>
</div>
</div>
</div>
}
</div>
}
else
{