littleshop/LittleShop/Areas/Admin/Views/VariantCollections/Edit.cshtml
sysadmin 76efba55bd feat: Phase 2.5 - Variant Collections Spreadsheet Editor
Replaces JSON textarea with professional Excel-like spreadsheet interface for managing product variant properties.

Features:
- Handsontable 14.6.1 spreadsheet component
- Property presets (Size, Color, Material, Storage, Custom)
- Inline cell editing with Tab/Enter navigation
- Context menu for add/remove rows and columns
- Keyboard shortcuts (Ctrl+D delete, Ctrl+Enter save, Ctrl+Z undo)
- Mobile touch gestures (swipe to delete rows)
- Automatic JSON serialization on form submit
- Form validation before saving
- Comprehensive user guide documentation

Files Changed:
- LittleShop/package.json: NPM package management setup
- LittleShop/wwwroot/js/variant-editor.js: 400-line spreadsheet editor module
- LittleShop/wwwroot/lib/handsontable/: Handsontable library (Community Edition)
- LittleShop/wwwroot/lib/hammerjs/: Hammer.js touch gesture library
- LittleShop/Areas/Admin/Views/VariantCollections/Edit.cshtml: Spreadsheet UI integration
- VARIANT_COLLECTIONS_USER_GUIDE.md: Complete user guide (18+ pages)

Technical Details:
- Excel-like editing experience (no more manual JSON editing)
- Mobile-first responsive design
- Browser compatibility: Chrome 90+, Firefox 88+, Edge 90+, Safari 14+
- Touch-optimized for mobile administration
- Automatic data validation and error handling
2025-11-13 19:40:06 +00:00

139 lines
6.0 KiB
Plaintext

@model LittleShop.DTOs.UpdateVariantCollectionDto
@{
ViewData["Title"] = "Edit Variant Collection";
var collectionId = ViewData["CollectionId"];
}
<div class="row mb-4">
<div class="col">
<h1><i class="fas fa-edit"></i> Edit Variant Collection</h1>
</div>
<div class="col-auto">
<a href="@Url.Action("Index")" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Back to Collections
</a>
</div>
</div>
<div class="card">
<div class="card-body">
<form method="post" action="@Url.Action("Edit", new { id = collectionId })">
@Html.AntiForgeryToken()
<div asp-validation-summary="All" class="alert alert-danger" role="alert"></div>
<div class="mb-3">
<label for="Name" class="form-label">Collection Name</label>
<input type="text" class="form-control" id="Name" name="Name" value="@Model.Name" maxlength="100" placeholder="e.g., Mens Clothes, Jewelry Sizes">
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="variant-spreadsheet-editor" class="form-label">
<i class="fas fa-table"></i> Properties Spreadsheet
</label>
<!-- Spreadsheet Editor Container -->
<div id="variant-spreadsheet-editor" style="height: 400px; overflow: auto;" class="border rounded"></div>
<!-- Hidden input to store JSON data -->
<input type="hidden" id="PropertiesJson" name="PropertiesJson" value='@Model.PropertiesJson' />
<div class="form-text mt-2">
<strong><i class="fas fa-info-circle"></i> How to use:</strong>
<ul class="mb-1">
<li><strong>Click column headers</strong> to select property type (Size, Color, Material, Storage, or Custom)</li>
<li><strong>Click cells</strong> to edit values directly</li>
<li><strong>Right-click</strong> for menu to add/remove rows and columns</li>
<li><strong>Keyboard shortcuts:</strong> Tab to move right, Enter to move down, Ctrl+D to delete, Ctrl+Enter to save</li>
<li><strong>Mobile:</strong> Swipe rows left/right to delete</li>
</ul>
<small class="text-muted">Changes are automatically saved when you click "Save Changes" below.</small>
</div>
<span asp-validation-for="PropertiesJson" class="text-danger"></span>
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="IsActive" name="IsActive" value="true" @(Model.IsActive == true ? "checked" : "")>
<label class="form-check-label" for="IsActive">
Active
</label>
</div>
<div class="form-text">Inactive collections cannot be selected when editing products</div>
</div>
<div class="border-top pt-3">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Save Changes
</button>
<a href="@Url.Action("Index")" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<!-- Handsontable Spreadsheet Library -->
<link rel="stylesheet" href="~/lib/handsontable/handsontable.full.min.css" />
<script src="~/lib/handsontable/handsontable.full.min.js"></script>
<!-- Hammer.js for Touch Gestures -->
<script src="~/lib/hammerjs/hammer.min.js"></script>
<!-- Variant Editor Module -->
<script src="~/js/variant-editor.js"></script>
<!-- Initialize Variant Editor -->
<script>
(function() {
// Get initial JSON data from hidden input
const propertiesJson = document.getElementById('PropertiesJson').value;
// Initialize the spreadsheet editor
const editor = new VariantEditor('variant-spreadsheet-editor', propertiesJson, {
minRows: 5,
minCols: 2,
maxCols: 8
});
// Before form submission, serialize spreadsheet data to JSON
const form = document.querySelector('form');
form.addEventListener('submit', function(e) {
try {
const jsonString = editor.serializeToJSON();
console.log('Serialized variant properties:', jsonString);
// Validation: Ensure we have valid JSON
const parsed = JSON.parse(jsonString);
if (!Array.isArray(parsed)) {
e.preventDefault();
alert('Invalid variant properties data. Please check your spreadsheet.');
return false;
}
// Check for empty property names
const emptyNames = parsed.filter(p => !p.name || p.name.trim() === '');
if (emptyNames.length > 0) {
e.preventDefault();
alert('All property columns must have names. Please click the column headers to set property types.');
return false;
}
return true;
} catch (error) {
console.error('Failed to serialize variant properties:', error);
e.preventDefault();
alert('Failed to save variant properties: ' + error.message);
return false;
}
});
// Keyboard shortcut hint for users
console.log('Variant Editor loaded. Keyboard shortcuts: Ctrl+D (delete), Ctrl+Enter (save)');
})();
</script>
}