my personal site
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Refactor admin HTML layout and enhance ad card functionality

- Restructured the admin HTML layout for improved usability, including a new button for creating ads and updated status display.
- Removed sidebar status elements and integrated a new ad creation button directly into the main view.
- Updated ad card rendering logic to improve interaction and accessibility, including keyboard navigation support.

+107 -78
+107 -78
workers/gymtracker-ads-api/src/admin-html.ts
··· 15 15 <span class="topbar-title">Gym Tracker</span> 16 16 <span class="topbar-sep">/</span> 17 17 <span class="topbar-sub">ads admin</span> 18 - <div class="status"> 19 - <div class="dot" id="statusDot"></div> 20 - <span class="status-text" id="fetchStatus">—</span> 21 - </div> 22 - </div> 23 - <div class="sidebar"> 24 - <div class="group"> 25 - <div class="group-label">status</div> 26 - <div class="kv"><span class="kv-k">total</span><span class="kv-v" id="sidebarTotal">0</span></div> 27 - <div class="kv"><span class="kv-k">live</span><span class="kv-v" id="sidebarLive">0</span></div> 28 - <div class="kv"><span class="kv-k">scheduled</span><span class="kv-v" id="sidebarScheduled">0</span></div> 29 - <div class="kv"><span class="kv-k">ended</span><span class="kv-v" id="sidebarEnded">0</span></div> 18 + <div class="topbar-right"> 19 + <button type="button" id="refreshBtn" class="topbar-refresh" title="Refresh">↻</button> 20 + <div class="status"> 21 + <div class="dot" id="statusDot"></div> 22 + <span class="status-text" id="fetchStatus">—</span> 23 + </div> 30 24 </div> 31 - <button type="button" id="refreshBtn" class="primary">Refresh</button> 32 - <button type="button" id="newAdBtn" class="primary">New ad</button> 33 25 </div> 34 26 <div class="main"> 35 27 <div class="schedule-page"> 36 28 <div class="schedule-grid"> 37 29 <div class="schedule-ads"> 38 - <div class="main-toolbar" id="mainToolbar" hidden> 30 + <div class="main-toolbar" id="mainToolbar"> 39 31 <input type="text" id="adsSearch" placeholder="Search sponsor or ID" class="toolbar-search"> 40 32 <div class="toolbar-filters"> 41 33 <button type="button" class="status-filter active" data-filter="all">All</button> ··· 50 42 </div> 51 43 <div class="schedule-calendar"> 52 44 <div id="calendarEmpty" class="calendar-empty" hidden> 53 - <p class="calendar-empty-text">Use the sidebar to create one</p> 45 + <p class="calendar-empty-text">Use New ad to create one</p> 54 46 </div> 55 47 <div id="calendarWrap" class="calendar-wrap" hidden> 56 48 <div class="calendar-header"> ··· 364 356 } 365 357 366 358 function renderOverview() { 367 - const c = countByStatus(); 368 - const totalEl = document.getElementById('sidebarTotal'); 369 - const liveEl = document.getElementById('sidebarLive'); 370 - const scheduledEl = document.getElementById('sidebarScheduled'); 371 - const endedEl = document.getElementById('sidebarEnded'); 372 - if (totalEl) totalEl.textContent = String(c.total); 373 - if (liveEl) liveEl.textContent = String(c.live); 374 - if (scheduledEl) scheduledEl.textContent = String(c.scheduled); 375 - if (endedEl) endedEl.textContent = String(c.ended); 359 + updateFilterChips(countByStatus()); 376 360 clearErrorBanner(); 361 + } 362 + 363 + function updateFilterChips(c) { 364 + const labels = { all: 'All', live: 'Live', scheduled: 'Scheduled', paused: 'Paused', ended: 'Ended' }; 365 + const counts = { all: c.total, live: c.live, scheduled: c.scheduled, paused: c.paused, ended: c.ended }; 366 + document.querySelectorAll('.status-filter').forEach(btn => { 367 + const key = btn.dataset.filter || 'all'; 368 + const n = counts[key] || 0; 369 + btn.textContent = n > 1 ? labels[key] + ' ' + n : labels[key]; 370 + }); 377 371 } 378 372 379 373 function formatCompact(n) { ··· 493 487 494 488 function renderAdCards() { 495 489 const adsHeader = document.getElementById('adsHeader'); 496 - const mainToolbar = document.getElementById('mainToolbar'); 497 490 if (adsHeader) adsHeader.hidden = scheduledAds.length === 0; 498 - if (mainToolbar) mainToolbar.hidden = scheduledAds.length === 0; 499 491 adCards.innerHTML = ''; 500 492 493 + const newAdGroup = document.createElement('div'); 494 + newAdGroup.className = 'ad-cards-group ad-cards-group-new'; 495 + const newAdWrap = document.createElement('div'); 496 + newAdWrap.className = 'ad-cards'; 497 + const newAdCardWrap = document.createElement('div'); 498 + newAdCardWrap.className = 'ad-card-wrap'; 499 + const newAdBtn = document.createElement('button'); 500 + newAdBtn.type = 'button'; 501 + newAdBtn.id = 'newAdBtn'; 502 + newAdBtn.className = 'ad-card new-ad-card'; 503 + newAdBtn.innerHTML = '<span class="new-ad-plus">+</span><span class="new-ad-label">New ad</span>'; 504 + newAdBtn.addEventListener('click', goToNewAd); 505 + newAdCardWrap.appendChild(newAdBtn); 506 + newAdWrap.appendChild(newAdCardWrap); 507 + newAdGroup.appendChild(newAdWrap); 508 + adCards.appendChild(newAdGroup); 509 + 501 510 const filtered = filterAdsForDisplay(); 502 511 const groups = { live: [], scheduled: [], paused: [], ended: [] }; 503 512 filtered.forEach(({ ad, i }) => { ··· 524 533 const card = document.createElement('div'); 525 534 card.className = 'ad-card-wrap'; 526 535 const canToggle = s === 'live' || s === 'paused'; 527 - const toggleLabel = s === 'paused' ? 'Resume' : 'Pause'; 528 - card.innerHTML = '<button type="button" class="ad-card' + (selectedIndex === i ? ' selected' : '') + '" data-ad-idx="' + i + '">' + 536 + const stateLabel = s === 'paused' ? 'Paused' : 'Live'; 537 + const stateIcon = s === 'paused' ? '⏸' : '▶'; 538 + const actionIcon = s === 'paused' ? '▶' : '⏸'; 539 + const actionHtml = canToggle ? '<button type="button" class="ad-card-action ad-card-action-' + s + '" data-ad-idx="' + i + '" data-action="toggle" title="' + stateLabel + ' — click to ' + (s === 'paused' ? 'resume' : 'pause') + '" aria-label="' + stateLabel + '"><span class="ad-card-action-icon-wrap"><span class="ad-card-action-icon ad-card-action-icon-state">' + stateIcon + '</span><span class="ad-card-action-icon ad-card-action-icon-action">' + actionIcon + '</span></span></button>' : ''; 540 + const chipHtml = (s === 'live' || s === 'paused') ? '' : '<span class="chip ' + statusClass(s) + '">' + s + '</span>'; 541 + card.innerHTML = '<div role="button" tabindex="0" class="ad-card' + (selectedIndex === i ? ' selected' : '') + '" data-ad-idx="' + i + '">' + 542 + actionHtml + 529 543 '<span class="ad-card-head">' + escapeHtml(ad.sponsor) + ' — ' + escapeHtml(ad.id) + '</span>' + 530 - '<span class="chip ' + statusClass(s) + '">' + s + '</span>' + 544 + chipHtml + 531 545 '<span class="ad-card-dates">' + formatDateRange(ad) + '</span>' + 532 546 (statsLine ? statsLine : '') + 533 547 '<span class="ad-card-tier">' + (ad.tier || 'banner') + '</span>' + 534 - '</button>' + 535 - (canToggle ? '<button type="button" class="ad-card-action" data-ad-idx="' + i + '" data-action="toggle" title="' + toggleLabel + '">' + (s === 'paused' ? '▶' : '⏸') + '</button>' : ''); 536 - card.querySelector('.ad-card').addEventListener('click', () => { 548 + '</div>'; 549 + const cardEl = card.querySelector('.ad-card'); 550 + cardEl.addEventListener('click', (e) => { 551 + if (e.target.closest('.ad-card-action')) return; 537 552 selectAd(i); 538 553 openFormOverlay('Edit: ' + (ad.sponsor || ad.id)); 539 554 }); 555 + cardEl.addEventListener('keydown', (e) => { 556 + if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); cardEl.click(); } 557 + }); 540 558 const actionBtn = card.querySelector('.ad-card-action'); 541 - if (actionBtn) actionBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleAdActive(parseInt(actionBtn.dataset.adIdx, 10)); }); 559 + if (actionBtn) actionBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); toggleAdActive(parseInt(actionBtn.dataset.adIdx, 10)); }); 542 560 cardWrap.appendChild(card); 543 561 }); 544 562 section.appendChild(cardWrap); ··· 1014 1032 document.querySelector('.form-overlay-backdrop').addEventListener('click', closeFormOverlay); 1015 1033 1016 1034 refreshBtn.addEventListener('click', loadSchedule); 1017 - document.getElementById('newAdBtn').addEventListener('click', goToNewAd); 1018 1035 1019 1036 document.getElementById('adsSearch').addEventListener('input', (e) => { 1020 1037 adsSearchQuery = (e.target.value || '').trim(); ··· 1151 1168 } 1152 1169 }); 1153 1170 1171 + updateFilterChips(countByStatus()); 1154 1172 loadSchedule(); 1155 1173 </script> 1156 1174 ··· 1176 1194 1177 1195 .dashboard-wrap { 1178 1196 display: grid; 1179 - grid-template-columns: minmax(0, 240px) 1fr; 1180 1197 grid-template-rows: 40px 1fr; 1181 1198 height: 100vh; 1182 1199 min-height: 0; ··· 1196 1213 .topbar-title { font-size: 13px; font-weight: 500; white-space: nowrap; } 1197 1214 .topbar-sep { color: var(--border); flex-shrink: 0; } 1198 1215 .topbar-sub { color: var(--muted); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } 1199 - .status { display: flex; align-items: center; gap: 6px; margin-left: auto; flex-shrink: 0; } 1200 - .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--border); flex-shrink: 0; } 1216 + .topbar-right { display: flex; align-items: center; gap: 10px; margin-left: auto; flex-shrink: 0; } 1217 + .topbar-refresh { 1218 + padding: 4px 8px; font-size: 14px; line-height: 1; 1219 + border: 1px solid var(--border); background: transparent; color: var(--muted); 1220 + cursor: pointer; 1221 + } 1222 + .topbar-refresh:hover { color: var(--accent); border-color: var(--accent); } 1223 + .status { display: flex; align-items: center; gap: 6px; flex-shrink: 0; } 1224 + .dot { width: 6px; height: 6px; background: var(--border); flex-shrink: 0; } 1201 1225 .dot.on { background: var(--green); } 1202 1226 .dot.err { background: var(--red); } 1203 1227 .status-text { color: var(--muted); font-size: 11px; } 1204 1228 .status-text.status-ok { color: var(--green); } 1205 1229 .status-text.status-err { color: var(--red); } 1206 1230 1207 - .sidebar { 1208 - border-right: 1px solid var(--border); 1209 - overflow-y: auto; 1210 - overflow-x: hidden; 1211 - padding: 12px 16px; 1212 - display: flex; 1213 - flex-direction: column; 1214 - gap: 12px; 1215 - background: var(--surface); 1216 - min-width: 0; 1217 - } 1218 - .sidebar button { width: 100%; min-height: 36px; } 1219 1231 .kv { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; } 1220 1232 .kv-k { color: var(--muted); white-space: nowrap; } 1221 1233 .kv-v { color: var(--accent); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; } ··· 1238 1250 .overview-status-banner.operational { border-left: 4px solid var(--green); } 1239 1251 .overview-status-banner.loading { border-left: 4px solid var(--muted); } 1240 1252 .overview-status-banner.error { border-left: 4px solid var(--red); } 1241 - .status-banner-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } 1253 + .status-banner-dot { width: 8px; height: 8px; flex-shrink: 0; } 1242 1254 .status-banner-dot.ok { background: var(--green); } 1243 1255 .status-banner-dot.pending { background: var(--muted); } 1244 1256 .status-banner-dot.err { background: var(--red); } ··· 1248 1260 padding: 7px 12px; 1249 1261 font-size: 12px; 1250 1262 font-family: var(--font); 1251 - border-radius: 4px; 1252 1263 border: 1px solid var(--accent); 1253 1264 color: var(--accent); 1254 1265 background: transparent; ··· 1414 1425 .save-status.status-err { color: var(--red); } 1415 1426 1416 1427 #adCards { display: flex; flex-direction: column; gap: 4px; } 1417 - .ad-cards { display: flex; flex-wrap: wrap; gap: 8px; min-width: 0; } 1428 + .ad-cards { 1429 + display: grid; 1430 + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); 1431 + gap: 10px; 1432 + min-width: 0; 1433 + } 1418 1434 .ad-cards-group { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; } 1419 1435 .main-toolbar { 1420 1436 display: flex; align-items: center; gap: 12px; flex-wrap: wrap; 1421 1437 margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid var(--border); 1422 1438 } 1423 - .toolbar-search { width: 180px; max-width: 100%; margin: 0; } 1439 + .toolbar-search { flex: 1; min-width: 180px; max-width: 360px; margin: 0; } 1424 1440 .toolbar-filters { display: flex; flex-wrap: wrap; gap: 4px; } 1441 + .ad-cards-group-new .ad-card-wrap { max-width: 160px; } 1442 + .new-ad-card { justify-content: center; align-items: center; text-align: center; color: var(--muted); min-height: 80px; padding-right: 12px; } 1443 + .new-ad-card:hover { border-color: var(--accent); color: var(--accent); background: rgba(20,184,166,0.08); } 1444 + .new-ad-plus { font-size: 20px; line-height: 1; } 1445 + .new-ad-label { font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.05em; } 1425 1446 .ads-status-filters { display: flex; flex-wrap: wrap; gap: 4px; } 1426 1447 .status-filter { padding: 4px 8px; font-size: 10px; } 1427 1448 .status-filter.active { border-color: var(--accent); color: var(--accent); } 1428 1449 .ad-cards-group:last-child { margin-bottom: 0; } 1429 1450 .ad-cards-group-label { font-size: 10px; color: var(--muted); letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 2px; } 1430 - .ad-card-wrap { display: flex; align-items: flex-start; gap: 4px; width: 100%; } 1431 - .ad-card-wrap .ad-card { flex: 1; min-width: 0; } 1432 - .ad-card-action { flex-shrink: 0; width: 28px; height: 28px; min-width: 28px; padding: 0; font-size: 11px; display: flex; align-items: center; justify-content: center; } 1451 + .ad-card-wrap { position: relative; min-width: 0; height: 100%; } 1452 + .ad-card-wrap .ad-card { position: relative; min-width: 0; min-height: 80px; width: 100%; height: 100%; } 1453 + .ad-card-action { 1454 + position: absolute; top: 8px; right: 8px; 1455 + padding: 6px 10px; font-size: 12px; 1456 + display: flex; align-items: center; gap: 6px; 1457 + z-index: 1; flex-shrink: 0; 1458 + } 1459 + .ad-card-action-icon-wrap { position: relative; display: inline-flex; width: 14px; height: 14px; align-items: center; justify-content: center; } 1460 + .ad-card-action-icon { font-size: 14px; line-height: 1; transition: opacity 0.15s; } 1461 + .ad-card-action-icon-action { position: absolute; opacity: 0; } 1462 + .ad-card-action:hover .ad-card-action-icon-state { opacity: 0; } 1463 + .ad-card-action:hover .ad-card-action-icon-action { opacity: 1; } 1464 + .ad-card-action-live { color: var(--green); background: rgba(34,197,94,0.15); border-color: rgba(34,197,94,0.4); } 1465 + .ad-card-action-paused { color: var(--yellow); background: rgba(234,179,8,0.15); border-color: rgba(234,179,8,0.4); } 1466 + .ad-card-action-live:hover, 1467 + .ad-card-action-live:focus-visible { 1468 + background: rgba(234,179,8,0.2) !important; border-color: var(--yellow) !important; color: var(--yellow) !important; 1469 + } 1470 + .ad-card-action-paused:hover, 1471 + .ad-card-action-paused:focus-visible { 1472 + background: rgba(34,197,94,0.2) !important; border-color: var(--green) !important; color: var(--green) !important; 1473 + } 1433 1474 .ad-card { 1434 1475 display: flex; flex-direction: column; align-items: flex-start; gap: 4px; 1435 - padding: 10px 12px; border: 1px solid var(--border); background: var(--surface); 1476 + padding: 10px 12px; padding-right: 56px; border: 1px solid var(--border); background: var(--surface); 1436 1477 color: var(--text); font: inherit; text-align: left; cursor: pointer; 1437 - transition: border-color 0.15s, background 0.15s; 1478 + transition: border-color 0.15s, background 0.15s; box-sizing: border-box; 1438 1479 } 1439 1480 .ad-card:hover { border-color: var(--text); } 1440 1481 .ad-card.selected { border-color: var(--accent); background: rgba(20,184,166,0.08); } ··· 1442 1483 .ad-card-dates { font-size: 10px; color: var(--muted); } 1443 1484 .ad-card-stats { font-size: 10px; color: var(--green); display: block; margin-top: 2px; } 1444 1485 .ad-card-tier { font-size: 9px; color: var(--muted); text-transform: uppercase; } 1445 - .ad-kpi-box { margin-bottom: 16px; padding: 10px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; } 1486 + .ad-kpi-box { margin-bottom: 16px; padding: 10px 12px; background: var(--bg); border: 1px solid var(--border); } 1446 1487 .ad-kpi-box .group-label { margin-bottom: 8px; } 1447 1488 .kpi-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px 16px; } 1448 1489 .kpi-item { display: flex; flex-direction: column; gap: 2px; } 1449 1490 .kpi-label { font-size: 9px; color: var(--muted); text-transform: uppercase; } 1450 1491 .kpi-value { font-size: 13px; font-weight: 500; color: var(--green); } 1451 - .chip { font-size: 10px; font-weight: 500; padding: 2px 6px; border: 1px solid transparent; border-radius: 3px; line-height: 1.5; } 1492 + .chip { font-size: 10px; font-weight: 500; padding: 2px 6px; border: 1px solid transparent; line-height: 1.5; } 1452 1493 .chip-live { color: var(--green); background: rgba(34,197,94,0.15); border-color: rgba(34,197,94,0.3); } 1453 1494 .chip-scheduled { color: var(--accent); background: rgba(20,184,166,0.15); border-color: rgba(20,184,166,0.3); } 1454 1495 .chip-ended { color: var(--muted); background: rgba(115,115,115,0.1); opacity: 0.9; } ··· 1475 1516 .cal-cell.has-conflict { outline: 1px dashed var(--yellow); outline-offset: -1px; } 1476 1517 .cal-cell-num { font-size: 12px; color: var(--text); margin-bottom: 4px; } 1477 1518 .cal-cell-ads { display: flex; flex-wrap: wrap; gap: 2px; align-content: flex-start; overflow: hidden; } 1478 - .cal-ad-pill { font-size: 10px; padding: 5px 8px; min-height: 28px; border: none; border-radius: 3px; cursor: pointer; font-family: var(--font); text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } 1519 + .cal-ad-pill { font-size: 10px; padding: 5px 8px; min-height: 28px; border: none; cursor: pointer; font-family: var(--font); text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } 1479 1520 .cal-ad-pill.chip-live { background: rgba(34,197,94,0.2); color: var(--green); } 1480 1521 .cal-ad-pill.chip-scheduled { background: rgba(20,184,166,0.2); color: var(--accent); } 1481 1522 .cal-ad-pill.chip-ended { background: rgba(102,102,102,0.2); color: var(--muted); } ··· 1483 1524 .cal-ad-pill:hover { opacity: 0.9; } 1484 1525 .cal-ad-pill.selected { outline: 1px solid var(--text); outline-offset: 1px; } 1485 1526 .cal-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); font-size: 10px; color: var(--muted); } 1486 - .cal-legend-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-right: 4px; vertical-align: middle; } 1527 + .cal-legend-dot { display: inline-block; width: 6px; height: 6px; margin-right: 4px; vertical-align: middle; } 1487 1528 .cal-legend-dot.live { background: var(--green); } 1488 1529 .cal-legend-dot.scheduled { background: var(--accent); } 1489 1530 .cal-legend-dot.ended { background: var(--muted); } ··· 1504 1545 .preview.preview-feature .preview-copy-block { padding: 14px 16px; display: flex; flex-direction: column; gap: 12px; width: 100%; } 1505 1546 .preview-sponsor-line { display: flex; align-items: center; gap: 8px; } 1506 1547 .preview-sponsor { font-size: 13px; color: var(--muted); } 1507 - .preview-logo { width: 24px; height: 24px; object-fit: contain; border-radius: 6px; flex-shrink: 0; } 1548 + .preview-logo { width: 24px; height: 24px; object-fit: contain; flex-shrink: 0; } 1508 1549 .preview-copy { display: flex; flex-direction: column; gap: 10px; width: 100%; } 1509 1550 .preview-headline { font-size: 17px; font-weight: 600; line-height: 1.3; } 1510 1551 .preview-subline { font-size: 14px; color: var(--muted); line-height: 1.4; } ··· 1512 1553 display: flex; align-items: center; justify-content: center; gap: 6px; 1513 1554 width: 100%; padding: 10px 0; 1514 1555 color: var(--cta-orange); background: rgba(232, 119, 34, 0.12); 1515 - border-radius: 8px; font-size: 14px; font-weight: 500; cursor: default; border: none; 1556 + font-size: 14px; font-weight: 500; cursor: default; border: none; 1516 1557 } 1517 1558 .preview-cta-arrow { font-size: 12px; opacity: 0.9; } 1518 1559 .preview-img-wrap { width: 100%; overflow: hidden; position: relative; background: var(--border); } ··· 1534 1575 #start_at[readonly], #end_at[readonly] { cursor: pointer; } 1535 1576 1536 1577 @media (min-width: 900px) { 1537 - .dashboard-wrap { grid-template-columns: 260px 1fr; } 1538 1578 .schedule-grid { grid-template-columns: 1fr 420px; } 1539 1579 } 1540 1580 @media (min-width: 1200px) { 1541 - .dashboard-wrap { grid-template-columns: 280px 1fr; } 1542 1581 .schedule-grid { grid-template-columns: 1fr 480px; } 1543 1582 .cal-cell { height: 80px; min-height: 80px; } 1544 1583 .cal-ad-pill { font-size: 11px; min-height: 30px; } ··· 1553 1592 .cal-ad-pill { font-size: 8px; padding: 0 3px; } 1554 1593 } 1555 1594 @media (max-width: 640px) { 1556 - .dashboard-wrap { grid-template-columns: 1fr; grid-template-rows: 40px auto 1fr; } 1557 - .sidebar { 1558 - border-right: none; 1559 - border-bottom: 1px solid var(--border); 1560 - flex-direction: column; 1561 - gap: 10px; 1562 - padding: 10px 12px; 1563 - } 1564 - .sidebar .group { min-width: 0; } 1565 - .sidebar .kv { font-size: 11px; } 1566 - .sidebar button { width: 100%; } 1595 + .dashboard-wrap { grid-template-rows: 40px 1fr; } 1567 1596 .main { padding: 12px 16px; } 1568 1597 .overview-status-banner { padding: 10px 12px; margin-bottom: 12px; } 1569 1598 .status-banner-text { font-size: 12px; }