Skip to content

Commit 0cee7a5

Browse files
committed
Implement searching and sorting in ListProducts.cshtml
1 parent 5346bd3 commit 0cee7a5

File tree

9 files changed

+214
-68
lines changed

9 files changed

+214
-68
lines changed

InventoryWebApplication/Controllers/ImportController.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55
using InventoryWebApplication.Models;
66
using InventoryWebApplication.Operations;
77
using InventoryWebApplication.Services.Importer;
8-
using JetBrains.Annotations;
98
using Microsoft.AspNetCore.Authorization;
109
using Microsoft.AspNetCore.Http;
1110
using Microsoft.AspNetCore.Mvc;
12-
using Microsoft.EntityFrameworkCore.Metadata.Internal;
13-
using Newtonsoft.Json;
1411

1512
namespace InventoryWebApplication.Controllers
1613
{

InventoryWebApplication/Controllers/ProductsController.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using InventoryWebApplication.Models.Database;
55
using InventoryWebApplication.Operations;
66
using InventoryWebApplication.Services.Database;
7+
using JetBrains.Annotations;
78
using Microsoft.AspNetCore.Authorization;
89
using Microsoft.AspNetCore.Mvc;
910

@@ -21,9 +22,15 @@ public ProductsController(ProductsService productsService)
2122

2223
[HttpGet]
2324
[Authorize(Roles = Role.StockManagerAndAbove)]
24-
public IActionResult ListProducts()
25+
public async Task<IActionResult> ListProducts([FromQuery] [CanBeNull] string q = null)
2526
{
26-
return View();
27+
ProductsListOperation operation = new()
28+
{
29+
Query = q,
30+
Products = await _productsService.SearchProducts(q),
31+
};
32+
33+
return View(operation);
2734
}
2835

2936
[HttpGet]

InventoryWebApplication/Controllers/SettingsController.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using System.Threading.Tasks;
32
using InventoryWebApplication.Models;
43
using InventoryWebApplication.Operations;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using InventoryWebApplication.Models.Database;
2+
using JetBrains.Annotations;
3+
4+
namespace InventoryWebApplication.Operations
5+
{
6+
public class ProductsListOperation
7+
{
8+
[CanBeNull]
9+
public string Query { get; set; }
10+
public Product[] Products { get; set; }
11+
}
12+
}

InventoryWebApplication/Services/Database/ProductsService.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System;
2-
using System.Diagnostics.CodeAnalysis;
2+
using System.Linq;
33
using System.Threading.Tasks;
44
using InventoryWebApplication.DatabaseContexts;
55
using InventoryWebApplication.Models.Database;
6+
using JetBrains.Annotations;
7+
using Microsoft.EntityFrameworkCore;
68
using Microsoft.Extensions.Logging;
79

810
namespace InventoryWebApplication.Services.Database
@@ -23,7 +25,7 @@ public ProductsService(DatabaseContext databaseContext, ILogger<DatabaseService<
2325
/// <param name="product">Product to be updated</param>
2426
/// <param name="shift">Number to add</param>
2527
/// <exception cref="ArgumentException">The result of the operation would be negative</exception>
26-
public async Task ShiftProductQuantity([NotNull] Product product, int shift)
28+
public async Task ShiftProductQuantity([System.Diagnostics.CodeAnalysis.NotNull] Product product, int shift)
2729
{
2830
if (product.AvailableQuantity + shift < 0) throw new ArgumentException("Negative quantity");
2931
product.AvailableQuantity += shift;
@@ -38,5 +40,16 @@ protected override void SetValues(Product target, Product values)
3840
target.Cost = values.Cost;
3941
target.SellPrice = values.SellPrice;
4042
}
41-
}
43+
44+
public async Task<Product[]> SearchProducts([CanBeNull] string query)
45+
{
46+
IQueryable<Product> source = ItemSet;
47+
if (query is not null)
48+
{
49+
query = query.ToLower();
50+
source = ItemSet.Where(o => o.Name.ToLower().Contains(query));
51+
}
52+
return await source.ToArrayAsync();
53+
}
54+
}
4255
}

InventoryWebApplication/Services/Importer/ImporterFactory.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using InventoryWebApplication.Models.Interfaces;
55
using InventoryWebApplication.Services.Database;
66
using Microsoft.Extensions.DependencyInjection;
7-
using InventoryWebApplication.Services.Exporter;
87

98
namespace InventoryWebApplication.Services.Importer
109
{

InventoryWebApplication/Utils/JsUtils.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
21
using System.Collections.Generic;
3-
using System.Text;
42
using Newtonsoft.Json;
53

64
namespace InventoryWebApplication.Utils
Lines changed: 167 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
@using InventoryWebApplication.Utils.Formatting
22
@using InventoryWebApplication.Models.Database
3-
@using InventoryWebApplication.Services.Database
43
@using System.Globalization
5-
@inject DatabaseService<Product> _databaseService
4+
@model InventoryWebApplication.Operations.ProductsListOperation
65

76
@{
87
ViewBag.Title = "Products Management";
@@ -11,13 +10,27 @@
1110
}
1211

1312
<h3>Products</h3>
13+
14+
<div class="display-horizontal">
15+
<input type="text" placeholder="Search" value="@(Model.Query)" id="searchBar"/>
16+
<button class="btn-secondary btn-icon" type="button" data-toggle="modal" data-target="#searchModal"
17+
id="searchButton" onclick="searchClick()">
18+
<span uk-icon="icon: search; ratio: 1.1"></span>
19+
</button>
20+
21+
<button class="btn-secondary btn-icon" type="button" data-toggle="modal" data-target="#searchModal"
22+
id="clearButton" onclick="clearClick()">
23+
<span uk-icon="icon: close; ratio: 1.1"></span>
24+
</button>
25+
</div>
26+
1427
<div class="btn-toolbar">
1528
<a asp-controller="Products" asp-action="AddProductForm">
1629
<button class="btn-secondary">Add</button>
1730
</a>
1831
</div>
1932
<div class="table-wrapper table-items-wrapper">
20-
<table class="table table-bordered">
33+
<table class="table table-bordered" id="productsTable">
2134
<colgroup>
2235
<col span="1" style="width: 6%">
2336
<col span="1" style="width: 25%">
@@ -27,68 +40,166 @@
2740
<col span="1" style="width: 10%">
2841
</colgroup>
2942

30-
<tr>
31-
<th>Id</th>
32-
<th>Name</th>
33-
<th>Desc</th>
34-
<th>Quant.</th>
35-
<th>Cost</th>
36-
<th>Sell Price</th>
43+
<tr id="tableHeaderRow">
44+
<th>
45+
<div class="center-flex">
46+
<span>Id</span>
47+
<span class="ml-auto cur-pointer" onclick="changeOrder(0)"></span>
48+
</div>
49+
</th>
50+
<th>
51+
<div class="center-flex">
52+
<span>Name</span>
53+
<span class="ml-auto cur-pointer" onclick="changeOrder(1)"></span>
54+
</div>
55+
</th>
56+
<th>
57+
<div class="center-flex">
58+
<span>Desc</span>
59+
<span class="ml-auto cur-pointer" onclick="changeOrder(2)"></span>
60+
</div>
61+
</th>
62+
<th>
63+
<div class="center-flex">
64+
<span>Quant.</span>
65+
<span class="ml-auto cur-pointer" onclick="changeOrder(3)"></span>
66+
</div>
67+
</th>
68+
<th>
69+
<div class="center-flex">
70+
<span>Cost</span>
71+
<span class="ml-auto cur-pointer" onclick="changeOrder(4)"></span>
72+
</div>
73+
</th>
74+
<th>
75+
<div class="center-flex">
76+
<span>Sell Price</span>
77+
<span class="ml-auto cur-pointer" onclick="changeOrder(5)"></span>
78+
</div>
79+
</th>
3780
</tr>
3881

39-
@foreach (Product product in _databaseService.GetAll())
40-
{
41-
<tr>
42-
<td>
43-
<a class="normal-text-link" href="/products/edit/@product.Id">
44-
<div>
45-
@product.Id
46-
</div>
47-
</a>
48-
</td>
49-
<td>
50-
<a class="normal-text-link" href="/products/edit/@product.Id">
51-
<div>
52-
@product.Name
53-
</div>
54-
</a>
55-
</td>
56-
<td>
57-
<a class="normal-text-link" href="/products/edit/@product.Id">
58-
<div>
59-
@product.Description.CutAfter(40)
60-
</div>
61-
</a>
62-
</td>
63-
<td>
64-
<a class="normal-text-link" href="/products/edit/@product.Id">
65-
<div>
66-
@product.AvailableQuantity
67-
</div>
68-
</a>
69-
</td>
70-
<td>
71-
<a class="normal-text-link" href="/products/edit/@product.Id">
72-
<div>
73-
@product.Cost.ToString(CultureInfo.InvariantCulture)
74-
</div>
75-
</a>
76-
</td>
77-
<td>
78-
<a class="normal-text-link" href="/products/edit/@product.Id">
79-
<div>
80-
@product.SellPrice.ToString(CultureInfo.InvariantCulture)
81-
</div>
82-
</a>
83-
</td>
84-
</tr>
82+
<tbody id="tableContent">
83+
@foreach (Product product in Model.Products)
84+
{
85+
<tr>
86+
<td>
87+
<a class="normal-text-link" href="/products/edit/@product.Id">
88+
<div>
89+
@product.Id
90+
</div>
91+
</a>
92+
</td>
93+
<td>
94+
<a class="normal-text-link" href="/products/edit/@product.Id">
95+
<div>
96+
@product.Name
97+
</div>
98+
</a>
99+
</td>
100+
<td>
101+
<a class="normal-text-link" href="/products/edit/@product.Id">
102+
<div>
103+
@product.Description.CutAfter(40)
104+
</div>
105+
</a>
106+
</td>
107+
<td>
108+
<a class="normal-text-link" href="/products/edit/@product.Id">
109+
<div>
110+
@product.AvailableQuantity
111+
</div>
112+
</a>
113+
</td>
114+
<td>
115+
<a class="normal-text-link" href="/products/edit/@product.Id">
116+
<div>
117+
@product.Cost.ToString(CultureInfo.InvariantCulture)
118+
</div>
119+
</a>
120+
</td>
121+
<td>
122+
<a class="normal-text-link" href="/products/edit/@product.Id">
123+
<div>
124+
@product.SellPrice.ToString(CultureInfo.InvariantCulture)
125+
</div>
126+
</a>
127+
</td>
128+
</tr>
85129
}
130+
</tbody>
131+
86132
</table>
87133
</div>
88134

89135
@section Scripts
90136
{
91137
<script>
138+
const searchBar = document.getElementById("searchBar");
139+
const searchButton = document.getElementById("searchButton");
140+
const clearButton = document.getElementById("clearButton");
141+
const tableHeaderRow = document.getElementById("tableHeaderRow");
142+
const tableHeaderOrderButtons = Array.from(tableHeaderRow.children).map(o => o.children[0].children[1]);
143+
tableHeaderOrderButtons.forEach(o => defaultIcon(o));
144+
const tableContent = document.getElementById("tableContent");
145+
146+
let currentOrder = 0;
147+
let orderReversed = true;
148+
changeOrder(0);
92149
150+
function searchClick() {
151+
let value = searchBar.value;
152+
if (!value) return;
153+
window.location.assign("/products?q=" + value);
154+
}
155+
156+
function clearClick() {
157+
window.location.assign("/products");
158+
}
159+
160+
function changeOrder(num) {
161+
if (num === currentOrder) {
162+
orderReversed = !orderReversed;
163+
} else {
164+
defaultIcon(tableHeaderOrderButtons[currentOrder]);
165+
currentOrder = num;
166+
}
167+
168+
let button = tableHeaderOrderButtons[num];
169+
170+
button.setAttribute("uk-icon", "ratio: 0.7; icon: " + (orderReversed ? "chevron-down" : "chevron-up"));
171+
button.style.opacity = "100%";
172+
173+
let children = Array.from(tableContent.children);
174+
175+
children.sort((a, b) => {
176+
let result;
177+
178+
let propA = a.children[num].children[0].children[0].innerText;
179+
let propB = b.children[num].children[0].children[0].innerText;
180+
181+
let numA = Number.parseFloat(propA);
182+
if (isNaN(numA)){
183+
result = propA.localeCompare(propB);
184+
} else {
185+
let numB = Number.parseFloat(propB);
186+
result = numA - numB;
187+
}
188+
189+
if (orderReversed) result *= -1;
190+
console.log(propA + " " + propB + " " + result);
191+
return result;
192+
});
193+
194+
for (let i = 0; i < children.length; i++) {
195+
tableContent.removeChild(children[i]);
196+
tableContent.appendChild(children[i]);
197+
}
198+
}
199+
200+
function defaultIcon(element) {
201+
element.setAttribute("uk-icon", "ratio: 0.7; icon: minus");
202+
element.style.opacity = "50%";
203+
}
93204
</script>
94205
}

InventoryWebApplication/wwwroot/css/site.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,14 @@ span.delete-list-item {
255255
span.delete-list-item:hover {
256256
color: darkred;
257257
cursor: pointer;
258+
}
259+
260+
.cur-pointer {
261+
cursor: pointer;
262+
}
263+
264+
.center-flex {
265+
display: flex;
266+
flex-direction: row;
267+
align-items: center;
258268
}

0 commit comments

Comments
 (0)