some more fancy shit to make it auto update without having to press enter

This commit is contained in:
Valentijn van der Jagt
2025-12-29 23:26:14 +01:00
parent 5b765f9a7d
commit a0b2ffa8c4
2 changed files with 146 additions and 10 deletions

View File

@@ -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;
}
} }

View File

@@ -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 = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}
</script>
</body> </body>
</html> </html>