feat: enhance next week badge and fix data persistence (v1.7.3)
This commit is contained in:
4
dist/bookmarklet-payload.js
vendored
4
dist/bookmarklet-payload.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bookmarklet.txt
vendored
2
dist/bookmarklet.txt
vendored
File diff suppressed because one or more lines are too long
2
dist/install.html
vendored
2
dist/install.html
vendored
File diff suppressed because one or more lines are too long
75
dist/kantine-standalone.html
vendored
75
dist/kantine-standalone.html
vendored
@@ -1134,6 +1134,12 @@ body {
|
|||||||
line-height: normal;
|
line-height: normal;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Detailed Badge Colors */
|
||||||
|
.nav-badge.badge-violet { background-color: #8b5cf6; }
|
||||||
|
.nav-badge.badge-green { background-color: var(--success-color); }
|
||||||
|
.nav-badge.badge-red { background-color: var(--error-color); }
|
||||||
|
.nav-badge.badge-blue { background-color: var(--accent-color); }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -1870,8 +1876,23 @@ body {
|
|||||||
await new Promise(r => setTimeout(r, 100));
|
await new Promise(r => setTimeout(r, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Group by ISO week
|
// 3. Group by ISO week (Merge with existing to preserve past days)
|
||||||
const weeksMap = new Map();
|
const weeksMap = new Map();
|
||||||
|
|
||||||
|
// Hydrate from existing cache (preserve past data)
|
||||||
|
if (allWeeks && allWeeks.length > 0) {
|
||||||
|
allWeeks.forEach(w => {
|
||||||
|
const key = `${w.year}-${w.weekNumber}`;
|
||||||
|
try {
|
||||||
|
weeksMap.set(key, {
|
||||||
|
year: w.year,
|
||||||
|
weekNumber: w.weekNumber,
|
||||||
|
days: w.days ? w.days.map(d => ({ ...d, items: d.items ? [...d.items] : [] })) : []
|
||||||
|
});
|
||||||
|
} catch (e) { console.warn('Error hydrating week:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (const day of allDays) {
|
for (const day of allDays) {
|
||||||
const d = new Date(day.date);
|
const d = new Date(day.date);
|
||||||
const weekNum = getISOWeek(d);
|
const weekNum = getISOWeek(d);
|
||||||
@@ -1882,11 +1903,12 @@ body {
|
|||||||
weeksMap.set(key, { year, weekNumber: weekNum, days: [] });
|
weeksMap.set(key, { year, weekNumber: weekNum, days: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const weekObj = weeksMap.get(key);
|
||||||
const weekday = d.toLocaleDateString('en-US', { weekday: 'long' });
|
const weekday = d.toLocaleDateString('en-US', { weekday: 'long' });
|
||||||
const orderCutoffDate = new Date(day.date);
|
const orderCutoffDate = new Date(day.date);
|
||||||
orderCutoffDate.setHours(10, 0, 0, 0);
|
orderCutoffDate.setHours(10, 0, 0, 0);
|
||||||
|
|
||||||
weeksMap.get(key).days.push({
|
const newDayObj = {
|
||||||
date: day.date,
|
date: day.date,
|
||||||
weekday: weekday,
|
weekday: weekday,
|
||||||
orderCutoff: orderCutoffDate.toISOString(),
|
orderCutoff: orderCutoffDate.toISOString(),
|
||||||
@@ -1904,13 +1926,25 @@ body {
|
|||||||
amountTracking: item.amount_tracking !== false
|
amountTracking: item.amount_tracking !== false
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Merge: Overwrite if exists, push if new
|
||||||
|
const existingIndex = weekObj.days.findIndex(existing => existing.date === day.date);
|
||||||
|
if (existingIndex >= 0) {
|
||||||
|
weekObj.days[existingIndex] = newDayObj;
|
||||||
|
} else {
|
||||||
|
weekObj.days.push(newDayObj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort weeks and days
|
||||||
allWeeks = Array.from(weeksMap.values()).sort((a, b) => {
|
allWeeks = Array.from(weeksMap.values()).sort((a, b) => {
|
||||||
if (a.year !== b.year) return a.year - b.year;
|
if (a.year !== b.year) return a.year - b.year;
|
||||||
return a.weekNumber - b.weekNumber;
|
return a.weekNumber - b.weekNumber;
|
||||||
});
|
});
|
||||||
|
allWeeks.forEach(w => {
|
||||||
|
if (w.days) w.days.sort((a, b) => a.date.localeCompare(b.date));
|
||||||
|
});
|
||||||
|
|
||||||
// Save to localStorage cache
|
// Save to localStorage cache
|
||||||
saveMenuCache();
|
saveMenuCache();
|
||||||
@@ -1989,12 +2023,25 @@ body {
|
|||||||
const nextWeekData = allWeeks.find(w => w.weekNumber === nextWeek && w.year === nextYear);
|
const nextWeekData = allWeeks.find(w => w.weekNumber === nextWeek && w.year === nextYear);
|
||||||
let totalDataCount = 0;
|
let totalDataCount = 0;
|
||||||
let orderableCount = 0;
|
let orderableCount = 0;
|
||||||
|
let daysWithOrders = 0;
|
||||||
|
let daysWithOrderableAndNoOrder = 0;
|
||||||
|
|
||||||
if (nextWeekData && nextWeekData.days) {
|
if (nextWeekData && nextWeekData.days) {
|
||||||
nextWeekData.days.forEach(day => {
|
nextWeekData.days.forEach(day => {
|
||||||
if (day.items && day.items.length > 0) {
|
if (day.items && day.items.length > 0) {
|
||||||
totalDataCount++;
|
totalDataCount++;
|
||||||
if (day.items.some(item => item.available)) orderableCount++;
|
const isOrderable = day.items.some(item => item.available);
|
||||||
|
if (isOrderable) orderableCount++;
|
||||||
|
|
||||||
|
let hasOrder = false;
|
||||||
|
day.items.forEach(item => {
|
||||||
|
const articleId = item.articleId || parseInt(item.id.split('_')[1]);
|
||||||
|
const key = `${day.date}_${articleId}`;
|
||||||
|
if (orderMap.has(key) && orderMap.get(key).length > 0) hasOrder = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasOrder) daysWithOrders++;
|
||||||
|
if (isOrderable && !hasOrder) daysWithOrderableAndNoOrder++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2006,8 +2053,24 @@ body {
|
|||||||
badge.className = 'nav-badge';
|
badge.className = 'nav-badge';
|
||||||
btnNextWeek.appendChild(badge);
|
btnNextWeek.appendChild(badge);
|
||||||
}
|
}
|
||||||
badge.title = `${orderableCount} Tage bestellbar / ${totalDataCount} Tage mit Menüdaten`;
|
|
||||||
badge.innerHTML = `<span class="orderable">${orderableCount}</span><span class="separator">/</span><span class="total">${totalDataCount}</span>`;
|
// Format: ( Ordered / Orderable / Total )
|
||||||
|
badge.title = `${daysWithOrders} bestellt / ${orderableCount} bestellbar / ${totalDataCount} gesamt`;
|
||||||
|
badge.innerHTML = `<span class="ordered">${daysWithOrders}</span><span class="separator">/</span><span class="orderable">${orderableCount}</span><span class="separator">/</span><span class="total">${totalDataCount}</span>`;
|
||||||
|
|
||||||
|
// Color Logic
|
||||||
|
badge.classList.remove('badge-violet', 'badge-green', 'badge-red', 'badge-blue');
|
||||||
|
|
||||||
|
if (daysWithOrders === totalDataCount && totalDataCount > 0) {
|
||||||
|
badge.classList.add('badge-violet'); // All days ordered
|
||||||
|
} else if (daysWithOrderableAndNoOrder > 0) {
|
||||||
|
badge.classList.add('badge-green'); // Orderable days exist without order
|
||||||
|
} else if (orderableCount === 0) {
|
||||||
|
badge.classList.add('badge-red'); // No orderable days at all
|
||||||
|
} else {
|
||||||
|
badge.classList.add('badge-blue'); // Default / partial state
|
||||||
|
}
|
||||||
|
|
||||||
} else if (badge) {
|
} else if (badge) {
|
||||||
badge.remove();
|
badge.remove();
|
||||||
}
|
}
|
||||||
|
|||||||
69
kantine.js
69
kantine.js
@@ -730,8 +730,23 @@
|
|||||||
await new Promise(r => setTimeout(r, 100));
|
await new Promise(r => setTimeout(r, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Group by ISO week
|
// 3. Group by ISO week (Merge with existing to preserve past days)
|
||||||
const weeksMap = new Map();
|
const weeksMap = new Map();
|
||||||
|
|
||||||
|
// Hydrate from existing cache (preserve past data)
|
||||||
|
if (allWeeks && allWeeks.length > 0) {
|
||||||
|
allWeeks.forEach(w => {
|
||||||
|
const key = `${w.year}-${w.weekNumber}`;
|
||||||
|
try {
|
||||||
|
weeksMap.set(key, {
|
||||||
|
year: w.year,
|
||||||
|
weekNumber: w.weekNumber,
|
||||||
|
days: w.days ? w.days.map(d => ({ ...d, items: d.items ? [...d.items] : [] })) : []
|
||||||
|
});
|
||||||
|
} catch (e) { console.warn('Error hydrating week:', e); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (const day of allDays) {
|
for (const day of allDays) {
|
||||||
const d = new Date(day.date);
|
const d = new Date(day.date);
|
||||||
const weekNum = getISOWeek(d);
|
const weekNum = getISOWeek(d);
|
||||||
@@ -742,11 +757,12 @@
|
|||||||
weeksMap.set(key, { year, weekNumber: weekNum, days: [] });
|
weeksMap.set(key, { year, weekNumber: weekNum, days: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const weekObj = weeksMap.get(key);
|
||||||
const weekday = d.toLocaleDateString('en-US', { weekday: 'long' });
|
const weekday = d.toLocaleDateString('en-US', { weekday: 'long' });
|
||||||
const orderCutoffDate = new Date(day.date);
|
const orderCutoffDate = new Date(day.date);
|
||||||
orderCutoffDate.setHours(10, 0, 0, 0);
|
orderCutoffDate.setHours(10, 0, 0, 0);
|
||||||
|
|
||||||
weeksMap.get(key).days.push({
|
const newDayObj = {
|
||||||
date: day.date,
|
date: day.date,
|
||||||
weekday: weekday,
|
weekday: weekday,
|
||||||
orderCutoff: orderCutoffDate.toISOString(),
|
orderCutoff: orderCutoffDate.toISOString(),
|
||||||
@@ -764,13 +780,25 @@
|
|||||||
amountTracking: item.amount_tracking !== false
|
amountTracking: item.amount_tracking !== false
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Merge: Overwrite if exists, push if new
|
||||||
|
const existingIndex = weekObj.days.findIndex(existing => existing.date === day.date);
|
||||||
|
if (existingIndex >= 0) {
|
||||||
|
weekObj.days[existingIndex] = newDayObj;
|
||||||
|
} else {
|
||||||
|
weekObj.days.push(newDayObj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort weeks and days
|
||||||
allWeeks = Array.from(weeksMap.values()).sort((a, b) => {
|
allWeeks = Array.from(weeksMap.values()).sort((a, b) => {
|
||||||
if (a.year !== b.year) return a.year - b.year;
|
if (a.year !== b.year) return a.year - b.year;
|
||||||
return a.weekNumber - b.weekNumber;
|
return a.weekNumber - b.weekNumber;
|
||||||
});
|
});
|
||||||
|
allWeeks.forEach(w => {
|
||||||
|
if (w.days) w.days.sort((a, b) => a.date.localeCompare(b.date));
|
||||||
|
});
|
||||||
|
|
||||||
// Save to localStorage cache
|
// Save to localStorage cache
|
||||||
saveMenuCache();
|
saveMenuCache();
|
||||||
@@ -849,12 +877,25 @@
|
|||||||
const nextWeekData = allWeeks.find(w => w.weekNumber === nextWeek && w.year === nextYear);
|
const nextWeekData = allWeeks.find(w => w.weekNumber === nextWeek && w.year === nextYear);
|
||||||
let totalDataCount = 0;
|
let totalDataCount = 0;
|
||||||
let orderableCount = 0;
|
let orderableCount = 0;
|
||||||
|
let daysWithOrders = 0;
|
||||||
|
let daysWithOrderableAndNoOrder = 0;
|
||||||
|
|
||||||
if (nextWeekData && nextWeekData.days) {
|
if (nextWeekData && nextWeekData.days) {
|
||||||
nextWeekData.days.forEach(day => {
|
nextWeekData.days.forEach(day => {
|
||||||
if (day.items && day.items.length > 0) {
|
if (day.items && day.items.length > 0) {
|
||||||
totalDataCount++;
|
totalDataCount++;
|
||||||
if (day.items.some(item => item.available)) orderableCount++;
|
const isOrderable = day.items.some(item => item.available);
|
||||||
|
if (isOrderable) orderableCount++;
|
||||||
|
|
||||||
|
let hasOrder = false;
|
||||||
|
day.items.forEach(item => {
|
||||||
|
const articleId = item.articleId || parseInt(item.id.split('_')[1]);
|
||||||
|
const key = `${day.date}_${articleId}`;
|
||||||
|
if (orderMap.has(key) && orderMap.get(key).length > 0) hasOrder = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasOrder) daysWithOrders++;
|
||||||
|
if (isOrderable && !hasOrder) daysWithOrderableAndNoOrder++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -866,8 +907,24 @@
|
|||||||
badge.className = 'nav-badge';
|
badge.className = 'nav-badge';
|
||||||
btnNextWeek.appendChild(badge);
|
btnNextWeek.appendChild(badge);
|
||||||
}
|
}
|
||||||
badge.title = `${orderableCount} Tage bestellbar / ${totalDataCount} Tage mit Menüdaten`;
|
|
||||||
badge.innerHTML = `<span class="orderable">${orderableCount}</span><span class="separator">/</span><span class="total">${totalDataCount}</span>`;
|
// Format: ( Ordered / Orderable / Total )
|
||||||
|
badge.title = `${daysWithOrders} bestellt / ${orderableCount} bestellbar / ${totalDataCount} gesamt`;
|
||||||
|
badge.innerHTML = `<span class="ordered">${daysWithOrders}</span><span class="separator">/</span><span class="orderable">${orderableCount}</span><span class="separator">/</span><span class="total">${totalDataCount}</span>`;
|
||||||
|
|
||||||
|
// Color Logic
|
||||||
|
badge.classList.remove('badge-violet', 'badge-green', 'badge-red', 'badge-blue');
|
||||||
|
|
||||||
|
if (daysWithOrders === totalDataCount && totalDataCount > 0) {
|
||||||
|
badge.classList.add('badge-violet'); // All days ordered
|
||||||
|
} else if (daysWithOrderableAndNoOrder > 0) {
|
||||||
|
badge.classList.add('badge-green'); // Orderable days exist without order
|
||||||
|
} else if (orderableCount === 0) {
|
||||||
|
badge.classList.add('badge-red'); // No orderable days at all
|
||||||
|
} else {
|
||||||
|
badge.classList.add('badge-blue'); // Default / partial state
|
||||||
|
}
|
||||||
|
|
||||||
} else if (badge) {
|
} else if (badge) {
|
||||||
badge.remove();
|
badge.remove();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1123,3 +1123,9 @@ body {
|
|||||||
line-height: normal;
|
line-height: normal;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Detailed Badge Colors */
|
||||||
|
.nav-badge.badge-violet { background-color: #8b5cf6; }
|
||||||
|
.nav-badge.badge-green { background-color: var(--success-color); }
|
||||||
|
.nav-badge.badge-red { background-color: var(--error-color); }
|
||||||
|
.nav-badge.badge-blue { background-color: var(--accent-color); }
|
||||||
|
|||||||
Reference in New Issue
Block a user