some more fancy shit to make it auto update without having to press enter
This commit is contained in:
@@ -4,6 +4,7 @@ import org.springframework.stereotype.Controller;
|
|||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -11,10 +12,15 @@ import java.util.ArrayList;
|
|||||||
public class WebServer {
|
public class WebServer {
|
||||||
|
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String index(@RequestParam(value = "fuzzySearch", required = false) String fuzzySearch, Model model) {
|
public String index() {
|
||||||
// Call your BonusManager or service to get items
|
|
||||||
ArrayList<BonusItem> items = BonusManager.getBonusItems(fuzzySearch);
|
|
||||||
model.addAttribute("items", items);
|
|
||||||
return "index";
|
return "index";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/api/fuzzy")
|
||||||
|
@ResponseBody
|
||||||
|
public ArrayList<BonusItem> index(@RequestParam(value = "q", required = true) String fuzzySearch, Model model){
|
||||||
|
// Call your BonusManager or service to get items
|
||||||
|
ArrayList<BonusItem> items = BonusManager.getBonusItems(fuzzySearch);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,25 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
form {
|
.search-container {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
input[type="text"] {
|
input[type="text"] {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
border: 2px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
}
|
||||||
|
input[type="text"]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #4CAF50;
|
||||||
|
}
|
||||||
|
.search-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
}
|
}
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -50,14 +62,44 @@
|
|||||||
a:hover {
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
.no-results {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.score-badge {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
.score-badge.medium {
|
||||||
|
background-color: #ff9800;
|
||||||
|
}
|
||||||
|
.score-badge.low {
|
||||||
|
background-color: #f44336;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Appie Bonus Items</h1>
|
<h1>Appie Bonus Items</h1>
|
||||||
|
|
||||||
<form action="">
|
<div class="search-container">
|
||||||
<input type="text" name="fuzzySearch" placeholder="Search items...">
|
<input
|
||||||
</form>
|
type="text"
|
||||||
|
id="fuzzySearch"
|
||||||
|
placeholder="Search items... (real-time)"
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
<div class="search-info">
|
||||||
|
<span id="resultCount">Showing all items</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -70,7 +112,7 @@
|
|||||||
<th>More Info</th>
|
<th>More Info</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="tableBody">
|
||||||
<tr th:each="item : ${items}">
|
<tr th:each="item : ${items}">
|
||||||
<td th:text="${item.title}">Item Name</td>
|
<td th:text="${item.title}">Item Name</td>
|
||||||
<td th:text="${item.description}">Description</td>
|
<td th:text="${item.description}">Description</td>
|
||||||
@@ -85,5 +127,93 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const searchInput = document.getElementById('fuzzySearch');
|
||||||
|
const tableBody = document.getElementById('tableBody');
|
||||||
|
const resultCount = document.getElementById('resultCount');
|
||||||
|
let debounceTimer;
|
||||||
|
|
||||||
|
searchInput.addEventListener('input', (e) => {
|
||||||
|
clearTimeout(debounceTimer);
|
||||||
|
const query = e.target.value.trim();
|
||||||
|
|
||||||
|
// Debounce: wait 300ms before searching
|
||||||
|
debounceTimer = setTimeout(() => {
|
||||||
|
if (query.length === 0) {
|
||||||
|
// Reset to all items
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
performFuzzySearch(query);
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function performFuzzySearch(query) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/fuzzy?q=${encodeURIComponent(query)}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('Search failed:', response.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await response.json();
|
||||||
|
renderResults(results, query);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error performing search:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderResults(results, query) {
|
||||||
|
if (results.length === 0) {
|
||||||
|
tableBody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="no-results">
|
||||||
|
No items found matching "<strong>${escapeHtml(query)}</strong>"
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
resultCount.textContent = `0 results for "${query}"`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableBody.innerHTML = results.map(item => `
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
${escapeHtml(item.title)}
|
||||||
|
</td>
|
||||||
|
<td>${escapeHtml(item.description)}</td>
|
||||||
|
<td>${escapeHtml(item.bonusText)}</td>
|
||||||
|
<td>${escapeHtml(item.category)}</td>
|
||||||
|
<td>
|
||||||
|
<img src="${escapeHtml(item.imageURL)}" alt="${escapeHtml(item.title)}" width="200" height="200">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="${escapeHtml(item.moreInfoURL)}" target="_blank">here</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
resultCount.textContent = `${results.length} result${results.length !== 1 ? 's' : ''} for "${query}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScoreBadgeClass(score) {
|
||||||
|
if (score >= 90) return ''; // Green (default)
|
||||||
|
if (score >= 70) return 'medium'; // Orange
|
||||||
|
return 'low'; // Red
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
const map = {
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
'"': '"',
|
||||||
|
"'": '''
|
||||||
|
};
|
||||||
|
return text.replace(/[&<>"']/g, m => map[m]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user