Paint each board row in its own notification type's colour

This commit is contained in:
2026-08-07 11:06:04 -04:00
parent 7c5b1bc74e
commit 344d252264

View File

@@ -45,13 +45,17 @@
<!-- Carousel - one card at a time, rotating -->
<section v-else-if="group.displaystyle === 'carousel'" class="recognition-section">
<div class="section-title recognition">{{ group.typename }}</div>
<div
class="section-title recognition"
:style="{ backgroundColor: getTypeColor(group.typecolor), color: textOn(group.typecolor) }"
>{{ group.typename }}</div>
<div class="recognition-carousel">
<div
v-for="(rec, idx) in group.items"
:key="`${rec.notificationid}-${rec.employeesso}`"
class="recognition-card"
:class="{ active: idx === carouselIndex(group) }"
:style="{ '--accent': getTypeColor(rec.typecolor) }"
>
<!-- A type that names no employee is a message, not a person:
no photo frame, no empty name line. -->
@@ -82,7 +86,10 @@
<!-- Grid - a row of tiles, cycling a page at a time -->
<section v-else class="recert-section">
<div class="section-title recert-title">
<div
class="section-title recert-title"
:style="{ backgroundColor: getTypeColor(group.typecolor), color: textOn(group.typecolor) }"
>
<span>{{ group.typename }} ({{ group.items.length }})</span>
<span v-if="gridRangeLabel(group)" class="recert-range">{{ gridRangeLabel(group) }}</span>
</div>
@@ -99,6 +106,7 @@
:key="`grid-${rec.notificationid}-${rec.employeesso}`"
class="recert-tile"
:class="{ 'message-tile': !hasEmployee(rec) }"
:style="{ '--accent': getTypeColor(rec.typecolor) }"
>
<template v-if="hasEmployee(rec)">
<img
@@ -265,6 +273,10 @@ const styledGroups = computed(() => {
key,
typename: heading,
displaystyle: n.displaystyle,
// Heading colour comes from the first type in the row. A shared
// category is one heading, so it can only carry one colour; the
// individual cards below still wear their own type's colour.
typecolor: n.typecolor,
items: []
})
}
@@ -453,6 +465,19 @@ function getTypeColor(typecolor) {
return aliases[typecolor] || typecolor || '#14abef'
}
// Text colour for a heading painted an arbitrary type colour. Gold needs dark
// text, navy needs white, and a site picks its own hexes - so this is computed
// rather than a per-colour rule.
function textOn(backgroundcolor) {
const hex = getTypeColor(backgroundcolor).replace('#', '')
if (hex.length < 6) return '#fff'
const r = parseInt(hex.slice(0, 2), 16)
const g = parseInt(hex.slice(2, 4), 16)
const b = parseInt(hex.slice(4, 6), 16)
// Rec. 709 luma, the usual quick contrast test.
return (0.2126 * r + 0.7152 * g + 0.0722 * b) > 150 ? '#231b00' : '#fff'
}
function getSectionClass(notifications) {
// Use danger color if any active incidents
const hasIncident = notifications.some(n => n.typecolor === 'danger' && !n.resolved)
@@ -602,6 +627,7 @@ function handlePhotoError(e) {
}
.section-title.recognition {
/* Fallback only - the real colour is the type's, set inline. */
background: #ffc107;
color: #3a2e00;
}
@@ -612,6 +638,7 @@ function handlePhotoError(e) {
margin-bottom: 25px;
}
.section-title.recert-title {
/* Fallback only - the real colour is the type's, set inline. */
background: #0d6efd;
color: #fff;
display: flex;
@@ -638,8 +665,8 @@ function handlePhotoError(e) {
gap: 14px;
}
.recert-tile {
background: linear-gradient(135deg, #1e3a5f 0%, #0d2137 100%);
border: 2px solid #0d6efd;
background: linear-gradient(135deg, #16233b 0%, #0d2137 100%);
border: 2px solid var(--accent, #0d6efd);
border-radius: 10px;
padding: 14px 10px;
display: flex;
@@ -653,7 +680,7 @@ function handlePhotoError(e) {
height: 90px;
border-radius: 50%;
object-fit: cover;
border: 3px solid #0d6efd;
border: 3px solid var(--accent, #0d6efd);
background: #1a1a2e;
}
.recert-photo.ge-logo-fallback {
@@ -713,8 +740,8 @@ function handlePhotoError(e) {
top: 0;
left: 0;
right: 0;
background: linear-gradient(135deg, #4a3a0a 0%, #2a2200 100%);
border: 3px solid #ffc107;
background: linear-gradient(135deg, #2b2718 0%, #1d1a10 100%);
border: 3px solid var(--accent, #ffc107);
border-radius: 12px;
padding: 20px 25px;
display: flex;
@@ -736,7 +763,7 @@ function handlePhotoError(e) {
height: 140px;
border-radius: 50%;
object-fit: cover;
border: 4px solid #ffc107;
border: 4px solid var(--accent, #ffc107);
background: #1a1a2e;
}