source: StockMaster/Views/Sale/Create.cshtml@ dfe03b8

main
Last change on this file since dfe03b8 was dfe03b8, checked in by Ceyda <ceyda.huseini@…>, 3 days ago

Initialize StockMaster project

  • Property mode set to 100644
File size: 10.3 KB
Line 
1@model StockMaster.ViewModels.SaleCreateViewModel
2@{
3 ViewData["Title"] = "New Sale";
4}
5
6
7@section Styles {
8 <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
9 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />
10 <style>
11
12 .select2-container--bootstrap-5 .select2-selection {
13 border-color: #dee2e6;
14 }
15
16 .is-invalid + .select2-container .select2-selection {
17 border-color: #dc3545;
18 }
19 </style>
20}
21
22<div class="row mb-4">
23 <div class="col-12">
24 <h2><i class="fas fa-shopping-cart"></i> Create New Sale</h2>
25 <nav aria-label="breadcrumb">
26 <ol class="breadcrumb">
27 <li class="breadcrumb-item"><a href="/">Home</a></li>
28 <li class="breadcrumb-item"><a href="/Sale/Index">Sales</a></li>
29 <li class="breadcrumb-item active">New Sale</li>
30 </ol>
31 </nav>
32 </div>
33</div>
34
35<form id="saleForm" method="post" action="/Sale/Create">
36 @Html.AntiForgeryToken()
37 <div class="row">
38 <div class="col-md-8">
39 <div class="card mb-3">
40 <div class="card-header">
41 <i class="fas fa-info-circle"></i> Sale Information
42 </div>
43 <div class="card-body">
44 <div class="row">
45 <div class="col-md-6 mb-3">
46 <label class="form-label">Customer</label>
47
48 <select name="CustomerId" id="customerSelect" class="form-select">
49 <option value="">Select customer (optional)</option>
50 @foreach (var customer in ViewBag.Customers)
51 {
52
53 <option value="@customer.CustomerId">@customer.Name (@customer.Email)</option>
54 }
55 </select>
56 </div>
57
58 <div class="col-md-6 mb-3">
59 <label class="form-label">Warehouse *</label>
60 <select name="WarehouseId" id="warehouseSelect" class="form-select" required>
61 <option value="">Select warehouse</option>
62 @foreach (var warehouse in ViewBag.Warehouses)
63 {
64 <option value="@warehouse.WarehouseId">@warehouse.Name</option>
65 }
66 </select>
67 </div>
68 </div>
69 </div>
70 </div>
71
72 <div class="card">
73 <div class="card-header d-flex justify-content-between align-items-center">
74 <span><i class="fas fa-list"></i> Sale Items</span>
75 <button type="button" class="btn btn-sm btn-success" onclick="addItem()">
76 <i class="fas fa-plus"></i> Add Product
77 </button>
78 </div>
79 <div class="card-body">
80 <div id="itemsContainer">
81
82 </div>
83
84 @if ((ViewBag.Products == null || ViewBag.Products.Count == 0))
85 {
86 <div class="alert alert-warning">
87 <i class="fas fa-exclamation-triangle"></i>
88 No products available. Please add products first.
89 </div>
90 }
91 </div>
92 </div>
93 </div>
94
95 <div class="col-md-4">
96 <div class="card">
97 <div class="card-header">
98 <i class="fas fa-calculator"></i> Summary
99 </div>
100 <div class="card-body">
101 <div class="mb-3">
102 <h4>Total: <span id="totalAmount" class="text-primary">0.00 MKD</span></h4>
103 </div>
104 <hr>
105 <div class="d-grid gap-2">
106 <button type="submit" class="btn btn-success btn-lg">
107 <i class="fas fa-check"></i> Complete Sale
108 </button>
109 <a href="/Sale/Index" class="btn btn-secondary">
110 <i class="fas fa-times"></i> Cancel
111 </a>
112 </div>
113 </div>
114 </div>
115 </div>
116 </div>
117</form>
118
119
120<div id="itemTemplate" style="display: none;">
121 <div class="row mb-3 item-row">
122 <div class="col-md-5">
123 <label class="form-label">Product</label>
124
125 <select class="form-select product-select" required>
126 <option value="">Select product</option>
127 @foreach (var product in ViewBag.Products)
128 {
129 <option value="@product.ProductId" data-price="@product.UnitPrice">
130 @product.Name (@product.UnitPrice.ToString("N2") MKD) - SKU: @product.Sku
131 </option>
132 }
133 </select>
134 </div>
135 <div class="col-md-3">
136 <label class="form-label">Quantity</label>
137 <input type="number" class="form-control quantity-input" min="1" value="1" required />
138 </div>
139 <div class="col-md-3">
140 <label class="form-label">Unit Price</label>
141 <input type="number" class="form-control price-input" step="0.01" min="0" required />
142 </div>
143 <div class="col-md-1">
144 <label class="form-label">&nbsp;</label>
145 <button type="button" class="btn btn-danger w-100" onclick="removeItem(this)">
146 <i class="fas fa-times"></i>
147 </button>
148 </div>
149 </div>
150</div>
151
152@section Scripts {
153
154 <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
155
156 <script>
157
158 $(document).ready(function() {
159
160 $('#customerSelect').select2({
161 theme: 'bootstrap-5',
162 width: '100%',
163 placeholder: 'Search for a customer...'
164 });
165
166
167 $('#warehouseSelect').select2({
168 theme: 'bootstrap-5',
169 width: '100%',
170 placeholder: 'Select warehouse'
171 });
172
173
174 addItem();
175 });
176
177 function addItem() {
178 const template = document.getElementById('itemTemplate').innerHTML;
179 const container = document.getElementById('itemsContainer');
180 const itemDiv = document.createElement('div');
181 itemDiv.innerHTML = template;
182 const row = itemDiv.firstElementChild;
183
184 container.appendChild(row);
185
186
187
188 const $productSelect = $(row).find('.product-select');
189 const quantityInput = row.querySelector('.quantity-input');
190 const priceInput = row.querySelector('.price-input');
191
192
193 $productSelect.select2({
194 theme: 'bootstrap-5',
195 width: '100%',
196 placeholder: 'Search product...',
197 dropdownParent: $(row)
198 });
199
200
201 $productSelect.on('select2:select change', function(e) {
202
203 const selectedOption = $(this).find(':selected');
204 const price = selectedOption.attr('data-price');
205 priceInput.value = price || 0;
206 calculateTotal();
207 });
208
209 quantityInput.addEventListener('input', calculateTotal);
210 priceInput.addEventListener('input', calculateTotal);
211
212 updateIndices();
213 }
214
215 function removeItem(button) {
216
217 const row = button.closest('.item-row');
218 $(row).find('.product-select').select2('destroy');
219
220 row.remove();
221 updateIndices();
222 calculateTotal();
223 }
224
225 function updateIndices() {
226 document.querySelectorAll('#itemsContainer .item-row').forEach((row, index) => {
227 const productSelect = row.querySelector('.product-select');
228 const quantityInput = row.querySelector('.quantity-input');
229 const priceInput = row.querySelector('.price-input');
230
231 productSelect.name = `Items[${index}].ProductId`;
232 quantityInput.name = `Items[${index}].Quantity`;
233 priceInput.name = `Items[${index}].UnitPrice`;
234 });
235 }
236
237 function calculateTotal() {
238 let total = 0;
239 document.querySelectorAll('#itemsContainer .item-row').forEach(row => {
240 const quantity = parseFloat(row.querySelector('.quantity-input').value) || 0;
241 const price = parseFloat(row.querySelector('.price-input').value) || 0;
242 total += quantity * price;
243 });
244 document.getElementById('totalAmount').textContent = total.toFixed(2) + ' MKD';
245 }
246
247 document.getElementById('saleForm').addEventListener('submit', function(e) {
248 const itemsCount = document.querySelectorAll('#itemsContainer .item-row').length;
249
250 if (itemsCount === 0) {
251 e.preventDefault();
252 alert('You must add at least one product!');
253 return false;
254 }
255
256 let allValid = true;
257 document.querySelectorAll('#itemsContainer .item-row').forEach(row => {
258 const productSelect = row.querySelector('.product-select');
259 if (!productSelect.value) {
260 allValid = false;
261
262 productSelect.classList.add('is-invalid');
263
264 $(row).find('.select2-selection').addClass('is-invalid-border');
265 } else {
266 productSelect.classList.remove('is-invalid');
267 }
268 });
269
270 if (!allValid) {
271 e.preventDefault();
272 alert('Please select products for all items!');
273 return false;
274 }
275
276 return true;
277 });
278 </script>
279}
Note: See TracBrowser for help on using the repository browser.