feat: add game icons and campaign timing to inventory cards

- Display game box art icons (52x70px) next to game names in campaign headers
- Show contextual timing information below campaign status:
  * Active campaigns display end time
  * Upcoming campaigns display start time
  * Expired campaigns display end time
- Use locale-aware date formatting via JavaScript toLocaleString()
- Utilize existing translation keys (starts/ends) with {time} placeholders
- Add flexbox layout for icon alignment and styling for timing text

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
github-actions[bot]
2025-11-08 22:42:15 +11:00
parent 080dc4aa13
commit 3181f2367b
4 changed files with 46 additions and 4 deletions

View File

@@ -811,11 +811,38 @@ function renderInventory() {
? `<button class="link-account-btn" onclick="window.open('${campaign.link_url}', '_blank')">Link Account</button>`
: '';
// Add game icon if available
let gameIconHtml = '';
if (campaign.game_box_art_url) {
const iconUrl = campaign.game_box_art_url.replace('{width}', '52').replace('{height}', '70');
gameIconHtml = `<img src="${iconUrl}" alt="${campaign.game_name}" class="game-icon" onerror="this.style.display='none'">`;
}
// Format campaign timing based on status
let timingHtml = '';
if (campaign.active && campaign.ends_at) {
const endDate = new Date(campaign.ends_at);
const formattedDate = endDate.toLocaleString();
const endsLabel = t.gui?.inventory?.ends || 'Ends: {time}';
timingHtml = `<div class="campaign-timing">${endsLabel.replace('{time}', formattedDate)}</div>`;
} else if (campaign.upcoming && campaign.starts_at) {
const startDate = new Date(campaign.starts_at);
const formattedDate = startDate.toLocaleString();
const startsLabel = t.gui?.inventory?.starts || 'Starts: {time}';
timingHtml = `<div class="campaign-timing">${startsLabel.replace('{time}', formattedDate)}</div>`;
} else if (campaign.expired && campaign.ends_at) {
const endDate = new Date(campaign.ends_at);
const formattedDate = endDate.toLocaleString();
const endsLabel = t.gui?.inventory?.ends || 'Ends: {time}';
timingHtml = `<div class="campaign-timing">${endsLabel.replace('{time}', formattedDate)}</div>`;
}
const claimedCountText = t.gui?.inventory?.claimed_drops || 'claimed';
card.innerHTML = `
<div class="campaign-header">
<div class="campaign-game">
${campaign.game_name}
${gameIconHtml}
<span class="campaign-game-name">${campaign.game_name}</span>
${linkStatusBadgeHtml}
</div>
${campaignNameHtml}
@@ -825,6 +852,7 @@ function renderInventory() {
<span>${statusText}</span>
<span>${campaign.claimed_drops} / ${campaign.total_drops} ${claimedCountText}</span>
</div>
${timingHtml}
<div class="campaign-drops">
${dropsHtml}
</div>

View File

@@ -618,10 +618,19 @@ header h1 {
}
.campaign-game {
display: flex;
align-items: center;
gap: 12px;
font-weight: bold;
font-size: 16px;
}
.campaign-game .game-icon {
width: 52px;
height: 70px;
flex-shrink: 0;
}
.campaign-name {
font-size: 14px;
color: var(--text-secondary);
@@ -712,6 +721,13 @@ header h1 {
justify-content: space-between;
}
.campaign-timing {
padding: 5px 15px 10px;
font-size: 11px;
color: var(--text-secondary);
font-style: italic;
}
.campaign-drops {
padding: 15px;
}