riskFactors;
+ }
+
+ // Helper class for internal comparable representation
+ private static class ComparableLot {
+ String lotId;
+ double finalPrice;
+ double conditionScore;
+ int manufacturingYear;
+ int hasProvenance;
+ int daysAgo;
+
+ public ComparableLot(String lotId, double finalPrice, double conditionScore, int manufacturingYear, int hasProvenance, int daysAgo) {
+ this.lotId = lotId;
+ this.finalPrice = finalPrice;
+ this.conditionScore = conditionScore;
+ this.manufacturingYear = manufacturingYear;
+ this.hasProvenance = hasProvenance;
+ this.daysAgo = daysAgo;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/auctiora/WorkflowOrchestrator.java b/src/main/java/auctiora/WorkflowOrchestrator.java
index b9c2c40..87a6783 100644
--- a/src/main/java/auctiora/WorkflowOrchestrator.java
+++ b/src/main/java/auctiora/WorkflowOrchestrator.java
@@ -251,7 +251,7 @@ public class WorkflowOrchestrator {
notifier.sendNotification(message, "Lot Closing Soon", 1);
// Mark as notified
- var updated = new Lot(
+ var updated = Lot.basic(
lot.saleId(), lot.lotId(), lot.title(), lot.description(),
lot.manufacturer(), lot.type(), lot.year(), lot.category(),
lot.currentBid(), lot.currency(), lot.url(),
diff --git a/src/main/resources/META-INF/resources/index.html b/src/main/resources/META-INF/resources/index.html
index dda3d0f..a722870 100644
--- a/src/main/resources/META-INF/resources/index.html
+++ b/src/main/resources/META-INF/resources/index.html
@@ -267,6 +267,63 @@
+
+
+
+
+
+
+
+
+ Sleeper Lots
+
+
High interest, low bids
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Bargains
+
+
Below estimate
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hot Lots
+
+
High competition
+
+
+
+
+
+
+
+
@@ -548,11 +605,17 @@
Title
|
+
+ Watchers
+ |
Current Bid
|
- Closing Time
+ Est. Range
+ |
+
+ Total Cost
|
Time Left
@@ -641,7 +704,10 @@ let dashboardState = {
closingSoon: [],
countryDistribution: {},
categoryDistribution: {},
- trendData: {}
+ trendData: {},
+ sleepers: [],
+ bargains: [],
+ popular: []
},
filters: {
auction: '',
@@ -748,7 +814,8 @@ async function fetchAllData() {
fetchStatistics(),
fetchRateLimitStats(),
fetchClosingSoon(),
- fetchChartData()
+ fetchChartData(),
+ fetchIntelligenceData()
]);
updateLastUpdate();
updateDataAge();
@@ -766,6 +833,38 @@ async function fetchAllData() {
}
}
+// Fetch intelligence data
+async function fetchIntelligenceData() {
+ try {
+ // Fetch sleepers
+ const sleepersRes = await fetch('/api/monitor/intelligence/sleepers');
+ if (sleepersRes.ok) {
+ const sleepersData = await sleepersRes.json();
+ document.getElementById('sleeper-count').textContent = sleepersData.count || 0;
+ dashboardState.data.sleepers = sleepersData.lots || [];
+ }
+
+ // Fetch bargains
+ const bargainsRes = await fetch('/api/monitor/intelligence/bargains');
+ if (bargainsRes.ok) {
+ const bargainsData = await bargainsRes.json();
+ document.getElementById('bargain-count').textContent = bargainsData.count || 0;
+ dashboardState.data.bargains = bargainsData.lots || [];
+ }
+
+ // Fetch popular lots
+ const popularRes = await fetch('/api/monitor/intelligence/popular?level=HIGH');
+ if (popularRes.ok) {
+ const popularData = await popularRes.json();
+ document.getElementById('popular-count').textContent = popularData.count || 0;
+ dashboardState.data.popular = popularData.lots || [];
+ }
+
+ } catch (error) {
+ console.error('Intelligence data fetch error:', error);
+ }
+}
+
// Fetch system status with trends
async function fetchStatus() {
try {
@@ -1179,12 +1278,41 @@ function updateClosingSoonTable(data = null) {
const urgencyIcon = minutesLeft < 10 ? 'fa-exclamation-circle' :
minutesLeft < 20 ? 'fa-exclamation-triangle' : 'fa-clock';
+ // Calculate followers badge
+ const followers = lot.followersCount || 0;
+ const followersBadge = followers > 50 ? 'bg-red-100 text-red-800' :
+ followers > 20 ? 'bg-orange-100 text-orange-800' :
+ followers > 5 ? 'bg-blue-100 text-blue-800' :
+ 'bg-gray-100 text-gray-600';
+
+ // Calculate estimate range
+ const estMin = lot.estimatedMin ? (lot.estimatedMin / 100).toFixed(0) : null;
+ const estMax = lot.estimatedMax ? (lot.estimatedMax / 100).toFixed(0) : null;
+ const estimateDisplay = estMin && estMax ? `β¬${estMin}-${estMax}` : '--';
+
+ // Calculate total cost (including VAT and premium)
+ const currentBid = lot.currentBid || 0;
+ const vat = lot.vat || 0;
+ const premium = lot.buyerPremiumPercentage || 0;
+ const totalCost = currentBid * (1 + (vat/100) + (premium/100));
+ const totalCostDisplay = totalCost > 0 ? `β¬${totalCost.toFixed(2)}` : '--';
+
+ // Bargain indicator
+ const isBargain = estMin && currentBid < parseFloat(estMin);
+ const bargainBadge = isBargain ? 'DEAL' : '';
+
return `
|
| ${lot.lotId || '--'} |
${lot.title || 'N/A'} |
- ${lot.currency || 'EUR'} ${lot.currentBid ? parseFloat(lot.currentBid).toFixed(2) : '0.00'} |
- ${lot.closingTime ? new Date(lot.closingTime).toLocaleString() : 'N/A'} |
+
+
+ ${followers}
+
+ |
+ ${lot.currency || 'EUR'} ${lot.currentBid ? parseFloat(lot.currentBid).toFixed(2) : '0.00'}${bargainBadge} |
+ ${estimateDisplay} |
+ ${totalCostDisplay} |
${minutesLeft} min
@@ -1394,6 +1522,46 @@ window.addEventListener('offline', () => {
showToast('Connection lost - working offline', 'warning');
addLog('Network connection lost', 'warning');
});
+
+// Intelligence widget handlers
+function showSleeperLots() {
+ if (!dashboardState.data.sleepers || dashboardState.data.sleepers.length === 0) {
+ showToast('No sleeper lots found', 'info');
+ return;
+ }
+ dashboardState.data.closingSoon = dashboardState.data.sleepers;
+ applyFilters();
+ showToast(`Showing ${dashboardState.data.sleepers.length} sleeper lots`, 'success');
+ addLog(`Filtered to sleeper lots (high interest, low bids)`);
+ // Scroll to table
+ document.getElementById('closing-soon-table').scrollIntoView({ behavior: 'smooth' });
+}
+
+function showBargainLots() {
+ if (!dashboardState.data.bargains || dashboardState.data.bargains.length === 0) {
+ showToast('No bargain lots found', 'info');
+ return;
+ }
+ dashboardState.data.closingSoon = dashboardState.data.bargains;
+ applyFilters();
+ showToast(`Showing ${dashboardState.data.bargains.length} bargain lots`, 'success');
+ addLog(`Filtered to bargain lots (below estimate)`);
+ // Scroll to table
+ document.getElementById('closing-soon-table').scrollIntoView({ behavior: 'smooth' });
+}
+
+function showPopularLots() {
+ if (!dashboardState.data.popular || dashboardState.data.popular.length === 0) {
+ showToast('No popular lots found', 'info');
+ return;
+ }
+ dashboardState.data.closingSoon = dashboardState.data.popular;
+ applyFilters();
+ showToast(`Showing ${dashboardState.data.popular.length} popular lots`, 'success');
+ addLog(`Filtered to popular lots (high followers)`);
+ // Scroll to table
+ document.getElementById('closing-soon-table').scrollIntoView({ behavior: 'smooth' });
+}
|