littleshop/LittleShop/Areas/Admin/Views/Users/Index.cshtml
SysAdmin 28dce2223d feat: Phase 2 - Mobile-first responsive card views
Implemented responsive mobile card layouts for all main Index views, providing superior mobile UX while maintaining desktop table views.

**Responsive Design Pattern:**
- Desktop (≥992px): Table layout with all data columns
- Mobile (<992px): Card-based layout optimized for touch interaction
- Breakpoint: Bootstrap's lg breakpoint for optimal viewing experience

**Views Converted:**

1. **Categories/Index.cshtml:**
   - Mobile cards with name, description, product count, status
   - Full-width action buttons for easy touch interaction
   - Clear visual hierarchy with icons and badges

2. **Users/Index.cshtml:**
   - Simplified mobile cards showing username, created date, status
   - Conditional delete button (protected admin account)
   - Clean, minimal design for quick user management

3. **ShippingRates/Index.cshtml:**
   - 2x2 grid layout for shipping rate data (country, price, weight, delivery)
   - Visual separation with light background boxes
   - All critical information displayed in scannable format

4. **VariantCollections/Index.cshtml:**
   - Properties JSON displayed in scrollable code block
   - Created/Updated dates in compact format
   - Clear deactivation action for variant collections

**Mobile UX Enhancements:**
-  44px minimum touch targets (Bootstrap .btn default)
-  Full-width buttons with .d-grid gap-2 for easy tapping
-  Proper spacing with mb-3 between cards
-  Clear visual hierarchy with card-title and badges
-  Descriptive button text (not just icons) on mobile
-  Responsive icons and status indicators
-  Word-break handling for long JSON strings

**Technical Implementation:**
- Used Bootstrap's d-none d-lg-block for desktop tables
- Used d-lg-none for mobile card views
- No JavaScript required - pure CSS responsive design
- Maintains all functionality from desktop view
- Zero data loss in mobile transformation

**Accessibility Maintained:**
- All ARIA labels preserved from Phase 1
- Semantic HTML structure in both views
- Proper heading hierarchy maintained
- Keyboard navigation fully functional

🚀 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 18:00:33 +00:00

140 lines
6.4 KiB
Plaintext

@model IEnumerable<LittleShop.DTOs.UserDto>
@{
ViewData["Title"] = "Users";
}
<div class="row mb-4">
<div class="col">
<h1><i class="fas fa-users"></i> Users</h1>
</div>
<div class="col-auto">
<a href="@Url.Action("Create")" class="btn btn-primary">
<i class="fas fa-user-plus"></i> Add User
</a>
</div>
</div>
@if (TempData["SuccessMessage"] != null)
{
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle"></i> @TempData["SuccessMessage"]
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
@if (TempData["ErrorMessage"] != null)
{
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-circle"></i> @TempData["ErrorMessage"]
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
<div class="card">
<div class="card-body">
@if (Model.Any())
{
<!-- Desktop Table View (hidden on mobile) -->
<div class="table-responsive d-none d-lg-block">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Username</th>
<th scope="col">Created</th>
<th scope="col">Status</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in Model)
{
<tr>
<td><strong>@user.Username</strong></td>
<td>@user.CreatedAt.ToString("MMM dd, yyyy")</td>
<td>
@if (user.IsActive)
{
<span class="badge bg-success">Active</span>
}
else
{
<span class="badge bg-danger">Inactive</span>
}
</td>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="User actions">
<a href="@Url.Action("Edit", new { id = user.Id })" class="btn btn-outline-primary" aria-label="Edit @user.Username">
<i class="fas fa-edit" aria-hidden="true"></i><span class="d-none d-sm-inline ms-1">Edit</span>
</a>
@if (user.Username != "admin")
{
<form method="post" action="@Url.Action("Delete", new { id = user.Id })" class="d-inline"
onsubmit="return confirm('Are you sure you want to delete this user?')">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-outline-danger" aria-label="Delete @user.Username">
<i class="fas fa-trash" aria-hidden="true"></i><span class="d-none d-sm-inline ms-1">Delete</span>
</button>
</form>
}
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
<!-- Mobile Card View (visible on mobile only) -->
<div class="d-lg-none">
@foreach (var user in Model)
{
<div class="card mb-3">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="card-title mb-0">
<i class="fas fa-user text-primary"></i> @user.Username
</h5>
@if (user.IsActive)
{
<span class="badge bg-success">Active</span>
}
else
{
<span class="badge bg-danger">Inactive</span>
}
</div>
<p class="text-muted mb-3">
<i class="fas fa-calendar" aria-hidden="true"></i> Created: @user.CreatedAt.ToString("MMM dd, yyyy")
</p>
<div class="d-grid gap-2">
<a href="@Url.Action("Edit", new { id = user.Id })" class="btn btn-outline-primary" aria-label="Edit @user.Username">
<i class="fas fa-edit" aria-hidden="true"></i> Edit User
</a>
@if (user.Username != "admin")
{
<form method="post" action="@Url.Action("Delete", new { id = user.Id })"
onsubmit="return confirm('Are you sure you want to delete this user?')">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-outline-danger w-100" aria-label="Delete @user.Username">
<i class="fas fa-trash" aria-hidden="true"></i> Delete User
</button>
</form>
}
</div>
</div>
</div>
}
</div>
}
else
{
<div class="text-center py-4">
<i class="fas fa-users fa-3x text-muted mb-3"></i>
<p class="text-muted">No users found.</p>
</div>
}
</div>
</div>