{event.title}
+{event.description}
+From 8264483c84426246dc046c8ed1521439d362b213 Mon Sep 17 00:00:00 2001 From: kristin-7 <46657300+KH07@users.noreply.github.com> Date: Tue, 5 Aug 2025 22:16:45 -0700 Subject: [PATCH 01/52] add the promotion table page --- .../QuestionnaireDashboard/PromotionTable.css | 102 ++++++++++++++++ .../QuestionnaireDashboard/PromotionTable.jsx | 109 ++++++++++++++++++ yarn.lock | 22 ++-- 3 files changed, 222 insertions(+), 11 deletions(-) create mode 100644 src/components/QuestionnaireDashboard/PromotionTable.css create mode 100644 src/components/QuestionnaireDashboard/PromotionTable.jsx diff --git a/src/components/QuestionnaireDashboard/PromotionTable.css b/src/components/QuestionnaireDashboard/PromotionTable.css new file mode 100644 index 0000000000..76520fa2c8 --- /dev/null +++ b/src/components/QuestionnaireDashboard/PromotionTable.css @@ -0,0 +1,102 @@ +.container { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + background-color: #ffffff; + border: 1px solid #e1e4e8; + border-radius: 6px; + max-width: 1200px; + margin: 20px auto; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 24px; + border-bottom: 1px solid #e1e4e8; +} + +.header h1 { + font-size: 24px; + font-weight: 600; + margin: 0; +} + +.header .actions { + display: flex; + gap: 8px; +} + +.btn { + border: 1px solid #1b1f2326; + padding: 5px 16px; + font-size: 14px; + font-weight: 500; + border-radius: 6px; + cursor: pointer; +} + +.btn-primary { + background-color: #2c974b; + color: #ffffff; + border-color: #2c974b; +} + +.btn-secondary { + background-color: #f6f8fa; + color: #24292e; +} + +.actions .btn { + margin-right: 100px; +} + +.actions .btn:last-child { + margin-right: 0; +} + +.promotion-table { + width: 100%; + border-collapse: collapse; +} + +.promotion-table th, +.promotion-table td { + padding: 12px 16px; + text-align: left; + font-size: 14px; + border-bottom: 1px solid #e1e4e8; +} + +.promotion-table thead th { + background-color: #f6f8fa; + font-weight: 600; +} + +.promotion-table tbody tr:last-child td { + border-bottom: none; +} + +.section-header td { + background-color: #f6f8fa; + font-weight: bold; + color: #586069; +} + +.status-met { + color: #22863a; +} + +.status-not-met { + color: #cb2431; +} + +.status-icon { + font-weight: bold; + margin-right: 8px; +} + +.promote-checkbox { + width: 20px; + height: 20px; + cursor: pointer; +} diff --git a/src/components/QuestionnaireDashboard/PromotionTable.jsx b/src/components/QuestionnaireDashboard/PromotionTable.jsx new file mode 100644 index 0000000000..a5af389d57 --- /dev/null +++ b/src/components/QuestionnaireDashboard/PromotionTable.jsx @@ -0,0 +1,109 @@ +import { useEffect, useState } from 'react'; + +const names = ['Alice', 'Bob', 'Charlie']; +const dummyMembers = Array.from({ length: 45 }, (_, i) => ({ + id: i + 1, + reviewer: names[i % names.length], + hasMetWeekly: i % 2 === 0, // Simulating weekly requirement met + requiredPRs: 5, + totalReviews: Math.floor(Math.random() * 10), + remainingWeeks: Math.max(0, 4 - Math.floor(Math.random() * 4)), // Random remaining weeks between 0 and 4 + promote: i % 3 === 0, // Randomly decide if they should be promoted + isNew: i < 15, // First 15 are new members +})); + +function PromotionTable() { + const [eligibilityData, setEligibilityData] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + // Simulating an API call with the dummy data + setLoading(true); + const timer = setTimeout(() => { + setEligibilityData(dummyMembers); + setLoading(false); + }, 500); // Simulate network delay + + return () => clearTimeout(timer); // Cleanup timer on unmount + }, []); + + const newMembers = eligibilityData.filter(u => u.isNew); + const existingMembers = eligibilityData.filter(u => !u.isNew); + + if (loading) return
| Existing member/ New member | +Reviewer | +Weekly Requirements | +Required PRs | +Total Reviews | +Remaining Weeks | +Promote? | +
|---|---|---|---|---|---|---|
| New Members | +||||||
| + | {user.reviewer} | ++ {user.hasMetWeekly ? '✓' : '✗'} + {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} + | +{user.requiredPRs} | +{user.totalReviews} | +{user.remainingWeeks} | ++ + | +
| Existing Members | +||||||
| + | {user.reviewer} | ++ {user.hasMetWeekly ? '✓' : '✗'} + {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} + | +{user.requiredPRs} | +{user.totalReviews} | +{user.remainingWeeks} | ++ + | +
| Existing member/ New member | -Reviewer | -Weekly Requirements | -Required PRs | -Total Reviews | -Remaining Weeks | -Promote? | -|||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| New Members | -|||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - | {user.reviewer} | -- {user.hasMetWeekly ? '✓' : '✗'} - {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} - | -{user.requiredPRs} | -{user.totalReviews} | -{user.remainingWeeks} | -- - | +|||||||||||||||||||||||||||||||||||||||||||||||||
| Existing member/ New member | +Reviewer | +Weekly Requirements | +Required PRs | +Total Reviews | +Remaining Weeks | +Promote? |
|---|---|---|---|---|---|---|
| New Members | +||||||
| + | {user.reviewer} | ++ {user.hasMetWeekly ? '✓' : '✗'} + {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} + | +{user.requiredPRs} | +{user.totalReviews} | +{user.remainingWeeks} | ++ + | +
| Existing Members | -||||||
| - | {user.reviewer} | -- {user.hasMetWeekly ? '✓' : '✗'} - {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} - | -{user.requiredPRs} | -{user.totalReviews} | -{user.remainingWeeks} | -- - | + {/* --- Existing Members Section --- */} +
| Existing Members | ||||||
| Existing member/ New member | @@ -61,7 +74,7 @@ function PromotionTable() {||||||
|---|---|---|---|---|---|---|
| New Members | ||||||
| Existing Members | ||||||
| Existing member/ New member | +Existing/New | Reviewer | Weekly Requirements | Required PRs | @@ -74,7 +65,7 @@ function PromotionTable() {||
|---|---|---|---|---|---|---|
| New Members | ||||||
| Existing Members | ||||||
| Existing/New | @@ -65,15 +63,15 @@ function PromotionTable() {||||||
|---|---|---|---|---|---|---|
| New Members | ||||||
| {user.reviewer} | -- {user.hasMetWeekly ? '✓' : '✗'} + | + {user.hasMetWeekly ? '✓' : '✗'} {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} | {user.requiredPRs} | @@ -81,7 +79,7 @@ function PromotionTable() {{user.remainingWeeks} | @@ -90,15 +88,15 @@ function PromotionTable() { ))} {/* --- Existing Members Section --- */} - | |
| Existing Members | ||||||
| {user.reviewer} | -- {user.hasMetWeekly ? '✓' : '✗'} + | + {user.hasMetWeekly ? '✓' : '✗'} {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} | {user.requiredPRs} | @@ -106,7 +104,7 @@ function PromotionTable() {{user.remainingWeeks} | diff --git a/src/components/QuestionnaireDashboard/PromotionTable.module.css b/src/components/QuestionnaireDashboard/PromotionTable.module.css new file mode 100644 index 0000000000..579af45076 --- /dev/null +++ b/src/components/QuestionnaireDashboard/PromotionTable.module.css @@ -0,0 +1,209 @@ +.container { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + background-color: #ffffff; + border: 1px solid #e1e4e8; + border-radius: 6px; + width: 100%; + padding: 0; + margin: 0; + color: #24292e; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 24px; + border-bottom: 1px solid #e1e4e8; +} + +.header h1 { + font-size: 24px; + font-weight: 600; + margin: 0; +} + +.header .actions { + display: flex; + gap: 8px; +} + +.btn { + padding: 5px 16px; + font-size: 14px; + font-weight: 500; + border-radius: 6px; + cursor: pointer; + appearance: none; + -webkit-appearance: none; + border: 1px solid transparent; +} + +[data-theme="light"] .btnPrimary { + background-color: #2c974b; + color: #ffffff; + border-color: #2c974b; +} + +[data-theme="light"] .btnSecondary { + background-color: #0d6efd; + color: #ffffff; + border-color: #0d6efd; +} + +.actions .btn { + margin-right: 100px; +} + +.actions .btn:last-child { + margin-right: 0; +} + +.promotionTableWrapper { + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + +.promotionTable { + min-width: 700px; + width: 100%; + border-collapse: collapse; +} + +.promotionTable th, +.promotionTable td { + padding: 12px 16px; + text-align: left; + font-size: 14px; + border-bottom: 1px solid #e1e4e8; +} + +.promotionTable thead th { + background-color: #f6f8fa; + font-weight: 600; +} + +.promotionTable tbody tr:last-child td { + border-bottom: none; +} + +.sectionHeader td { + background-color: #f6f8fa; + font-weight: bold; + color: #586069; +} + +.statusMet { + color: #22863a; +} + +.statusNotMet { + color: #cb2431; +} + +.statusIcon { + font-weight: bold; + margin-right: 8px; +} + +.promoteCheckbox { + width: 20px; + height: 20px; + cursor: pointer; +} + +/* Dark Mode Support */ +[data-theme="dark"] { + background-color: #1e2329; + color: #f0f0f0; + border-color: #383e44; +} + +[data-theme="dark"] .container { + background-color: #1e2329; + border-color: #383e44; + color: #f0f0f0; +} + +[data-theme="dark"] .header { + background-color: #24292f; + border-bottom: 1px solid #383e44; +} + +[data-theme="dark"] .header h1 { + color: #f0f0f0; +} + +[data-theme="dark"] .btnPrimary { + background-color: #2c974b; + color: #ffffff; + border-color: #2c974b; +} + +[data-theme="dark"] .btnSecondary { + background-color: #0d6efd; + color: #ffffff; + border-color: #0d6efd; +} + +[data-theme="dark"] .promotionTable { + width: 100%; + background-color: #2d333b; +} + +[data-theme="dark"] .promotionTable thead th { + background-color: #24292f; + border-color: #383e44; + color: #f0f0f0; +} + +[data-theme="dark"] .promotionTable th, +[data-theme="dark"] .promotionTable td { + border-bottom: 1px solid #383e44; +} + +[data-theme="dark"] .sectionHeader td { + background-color: #24292f; + color: #f0f0f0; +} + +[data-theme="dark"] .promotionTable tbody tr.odd-row { + background-color: #24292f; +} + +[data-theme="dark"] .promotionTable tbody tr:hover { + background-color: #363b43; + cursor: pointer; +} + +[data-theme="dark"] .promotionTable tbody tr:hover td { + color: #f0f0f0; +} + +[data-theme="dark"] .statusMet { + color: #53d06e; +} + +[data-theme="dark"] .statusNotMet { + color: #fa7970; +} + +@media (max-width: 768px) { + .header { + flex-direction: column; + align-items: flex-start; + padding: 16px; + } + + .header .actions { + flex-direction: column; + width: 100%; + margin-top: 16px; + } + + .actions .btn { + width: 100%; + margin-right: 0; + margin-bottom: 8px; + } +} \ No newline at end of file From 1ee09b76163d4a774f0f9f7eb706f4785db29ca7 Mon Sep 17 00:00:00 2001 From: kristin-7 <46657300+KH07@users.noreply.github.com> Date: Thu, 30 Oct 2025 22:34:05 -0700 Subject: [PATCH 16/52] convert .css to module.css --- .../QuestionnaireDashboard/PromotionTable.css | 202 ------------------ 1 file changed, 202 deletions(-) delete mode 100644 src/components/QuestionnaireDashboard/PromotionTable.css diff --git a/src/components/QuestionnaireDashboard/PromotionTable.css b/src/components/QuestionnaireDashboard/PromotionTable.css deleted file mode 100644 index d01e01ad6d..0000000000 --- a/src/components/QuestionnaireDashboard/PromotionTable.css +++ /dev/null @@ -1,202 +0,0 @@ -.container { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - background-color: #ffffff; - border: 1px solid #e1e4e8; - border-radius: 6px; - width: 100%; - margin: 20px auto; - color: #24292e; -} - -.header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px 24px; - border-bottom: 1px solid #e1e4e8; -} - -.header h1 { - font-size: 24px; - font-weight: 600; - margin: 0; -} - -.header .actions { - display: flex; - gap: 8px; -} - -.btn { - padding: 5px 16px; - font-size: 14px; - font-weight: 500; - border-radius: 6px; - cursor: pointer; - appearance: none; - -webkit-appearance: none; - border: 1px solid transparent; -} - -[data-theme="light"] .btn-primary { - background-color: #2c974b; - color: #ffffff; - border-color: #2c974b; -} - -[data-theme="light"] .btn-secondary { - background-color: #0d6efd; - color: #ffffff; - border-color: #0d6efd; -} - -.actions .btn { - margin-right: 100px; -} - -.actions .btn:last-child { - margin-right: 0; -} - -.promotion-table-wrapper { - overflow-x: auto; - -webkit-overflow-scrolling: touch; -} - -.promotion-table { - min-width: 700px; - width: 100%; - border-collapse: collapse; -} - -.promotion-table th, -.promotion-table td { - padding: 12px 16px; - text-align: left; - font-size: 14px; - border-bottom: 1px solid #e1e4e8; -} - -.promotion-table thead th { - background-color: #f6f8fa; - font-weight: 600; -} - -.promotion-table tbody tr:last-child td { - border-bottom: none; -} - -.section-header td { - background-color: #f6f8fa; - font-weight: bold; - color: #586069; -} - -.status-met { - color: #22863a; -} - -.status-not-met { - color: #cb2431; -} - -.status-icon { - font-weight: bold; - margin-right: 8px; -} - -.promote-checkbox { - width: 20px; - height: 20px; - cursor: pointer; -} - -/* Dark Mode Support */ -[data-theme="dark"] { - background-color: #1e2329; - color: #f0f0f0; - border-color: #383e44; -} - -[data-theme="dark"] .header { - background-color: #24292f; - border-bottom: 1px solid #383e44; -} - -[data-theme="dark"] .header h1 { - color: #f0f0f0; -} - -[data-theme="dark"] .btn-primary { - background-color: #2c974b; - color: #ffffff; - border-color: #2c974b; -} - -[data-theme="dark"] .btn-secondary { - background-color: #0d6efd; - color: #ffffff; - border-color: #0d6efd; -} - -[data-theme="dark"] .promotion-table { - width: 100%; - background-color: #2d333b; -} - -[data-theme="dark"] .promotion-table thead th { - background-color: #24292f; - border-color: #383e44; - color: #f0f0f0; -} - -[data-theme="dark"] .promotion-table th, -[data-theme="dark"] .promotion-table td { - border-bottom: 1px solid #383e44; -} - -[data-theme="dark"] .section-header td { - background-color: #24292f; - color: #f0f0f0; -} - -[data-theme="dark"] .promotion-table tbody tr.odd-row { - background-color: #24292f; -} - -[data-theme="dark"] .promotion-table tbody tr:hover { - background-color: #363b43; - cursor: pointer; -} - -[data-theme="dark"] .promotion-table tbody tr:hover td { - color: #f0f0f0; -} - -[data-theme="dark"] .status-met { - color: #53d06e; -} - -[data-theme="dark"] .status-not-met { - color: #fa7970; -} - -@media (max-width: 768px) { - .header { - flex-direction: column; - align-items: flex-start; - padding: 16px; - } - - .header .actions { - flex-direction: column; - width: 100%; - margin-top: 16px; - } - - .actions .btn { - width: 100%; - margin-right: 0; - margin-bottom: 8px; - } -} \ No newline at end of file From fdd0ba6b849bb2296c4de37c3b59f37f6079273f Mon Sep 17 00:00:00 2001 From: kristin-7 <46657300+KH07@users.noreply.github.com> Date: Fri, 31 Oct 2025 21:56:22 -0700 Subject: [PATCH 17/52] change green button color to meet minimal contrast requirement --- .../QuestionnaireDashboard/PromotionTable.module.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/QuestionnaireDashboard/PromotionTable.module.css b/src/components/QuestionnaireDashboard/PromotionTable.module.css index 579af45076..b88e2c95ba 100644 --- a/src/components/QuestionnaireDashboard/PromotionTable.module.css +++ b/src/components/QuestionnaireDashboard/PromotionTable.module.css @@ -40,9 +40,9 @@ } [data-theme="light"] .btnPrimary { - background-color: #2c974b; + background-color: #22863a; color: #ffffff; - border-color: #2c974b; + border-color: #22863a; } [data-theme="light"] .btnSecondary { @@ -135,9 +135,9 @@ } [data-theme="dark"] .btnPrimary { - background-color: #2c974b; + background-color: #22863a; color: #ffffff; - border-color: #2c974b; + border-color: #22863a; } [data-theme="dark"] .btnSecondary { From 1e81d6e21c4d1d909a3d56f6642b77749603a85f Mon Sep 17 00:00:00 2001 From: kristin-7 <46657300+KH07@users.noreply.github.com> Date: Fri, 31 Oct 2025 22:06:09 -0700 Subject: [PATCH 18/52] add comments before dummy data creation to safely pass SonarCloud pseudorandom check --- src/components/QuestionnaireDashboard/PromotionTable.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/QuestionnaireDashboard/PromotionTable.jsx b/src/components/QuestionnaireDashboard/PromotionTable.jsx index 4167291f83..01ba27028b 100644 --- a/src/components/QuestionnaireDashboard/PromotionTable.jsx +++ b/src/components/QuestionnaireDashboard/PromotionTable.jsx @@ -2,6 +2,8 @@ import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import styles from './PromotionTable.module.css'; +// Using Math.random() safely here for dummy data generation. +// sonarjs/security/detect-non-secure-random: off const names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Edward', 'Fiona', 'Grace']; const dummyMembers = Array.from({ length: 45 }, (_, i) => ({ id: i + 1, From f8fd79e6bff2c8e297e3581a7dd9e8576d64dcb0 Mon Sep 17 00:00:00 2001 From: kristin-7 <46657300+KH07@users.noreply.github.com> Date: Fri, 31 Oct 2025 22:09:35 -0700 Subject: [PATCH 19/52] add a seed function --- .../QuestionnaireDashboard/PromotionTable.jsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/QuestionnaireDashboard/PromotionTable.jsx b/src/components/QuestionnaireDashboard/PromotionTable.jsx index 01ba27028b..5df0b27953 100644 --- a/src/components/QuestionnaireDashboard/PromotionTable.jsx +++ b/src/components/QuestionnaireDashboard/PromotionTable.jsx @@ -2,16 +2,18 @@ import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import styles from './PromotionTable.module.css'; -// Using Math.random() safely here for dummy data generation. -// sonarjs/security/detect-non-secure-random: off -const names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Edward', 'Fiona', 'Grace']; +function seededRandom(seed) { + let x = Math.sin(seed) * 10000; + return x - Math.floor(x); +} + const dummyMembers = Array.from({ length: 45 }, (_, i) => ({ id: i + 1, reviewer: names[i % names.length], hasMetWeekly: i % 2 === 0, requiredPRs: 5, - totalReviews: Math.floor(Math.random() * 10), - remainingWeeks: Math.max(0, 4 - Math.floor(Math.random() * 4)), + totalReviews: Math.floor(seededRandom(i) * 10), + remainingWeeks: Math.max(0, 4 - Math.floor(seededRandom(i + 1) * 4)), promote: i % 3 === 0, isNew: i < 15, })); From ccc52a40744b67e0b42ab6ddcc3983ae89c54a42 Mon Sep 17 00:00:00 2001 From: kristin-7 <46657300+KH07@users.noreply.github.com> Date: Fri, 31 Oct 2025 22:14:04 -0700 Subject: [PATCH 20/52] add a seeded random function for dummy data generation --- src/components/QuestionnaireDashboard/PromotionTable.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/QuestionnaireDashboard/PromotionTable.jsx b/src/components/QuestionnaireDashboard/PromotionTable.jsx index 5df0b27953..230423f83a 100644 --- a/src/components/QuestionnaireDashboard/PromotionTable.jsx +++ b/src/components/QuestionnaireDashboard/PromotionTable.jsx @@ -2,6 +2,8 @@ import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import styles from './PromotionTable.module.css'; +const names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Edward', 'Fiona', 'Grace']; + function seededRandom(seed) { let x = Math.sin(seed) * 10000; return x - Math.floor(x); From 6dd270a4d5511c959380241fb5714eab843a1ce0 Mon Sep 17 00:00:00 2001 From: kristin-7 <46657300+KH07@users.noreply.github.com> Date: Sat, 1 Nov 2025 13:46:19 -0700 Subject: [PATCH 21/52] refactor member section to reduce duplication --- .../QuestionnaireDashboard/PromotionTable.jsx | 82 ++++++++----------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/src/components/QuestionnaireDashboard/PromotionTable.jsx b/src/components/QuestionnaireDashboard/PromotionTable.jsx index 230423f83a..c9200d6a3b 100644 --- a/src/components/QuestionnaireDashboard/PromotionTable.jsx +++ b/src/components/QuestionnaireDashboard/PromotionTable.jsx @@ -20,6 +20,36 @@ const dummyMembers = Array.from({ length: 45 }, (_, i) => ({ isNew: i < 15, })); +function MemberSection({ title, members, styles }) { + return ( + <> + | |
| {title} | +||||||
| + | {user.reviewer} | ++ {user.hasMetWeekly ? '✓' : '✗'} + {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} + | +{user.requiredPRs} | +{user.totalReviews} | +{user.remainingWeeks} | ++ + | +
| New Members | -||||||
| - | {user.reviewer} | -- {user.hasMetWeekly ? '✓' : '✗'} - {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} - | -{user.requiredPRs} | -{user.totalReviews} | -{user.remainingWeeks} | -- - | -
| Existing Members | -||||||
| - | {user.reviewer} | -- {user.hasMetWeekly ? '✓' : '✗'} - {user.hasMetWeekly ? 'Has Met' : 'Has not Met'} - | -{user.requiredPRs} | -{user.totalReviews} | -{user.remainingWeeks} | -- - | -
- {description ? (description.length > 120 ? `${description.slice(0, 120)}…` : description) : 'No description.'} + {description + ? description.length > 120 + ? `${description.slice(0, 120)}…` + : description + : 'No description.'}
+
{activity.author}: {activity.message}
- + {activity.time}+ {selectedDateEvents.length > 0 + ? `${selectedDateEvents.length} ${ + selectedDateEvents.length === 1 ? 'event' : 'events' + } scheduled` + : 'No events scheduled for this date'} +
+{event.description}
+Select a different date or adjust the filters to see scheduled events.
++ Core chart rendering: Showing Hits timeline for all roles. +
+- Core chart rendering: Showing Hits timeline for all roles. + Track job hits (solid line) versus applications (dashed line) over time. Click any role + to highlight.
Error: {error}
+ {retryCount < MAX_RETRIES && ( ++ Retry attempt {retryCount} of {MAX_RETRIES} +
+ )} +Error: {error}
{retryCount < MAX_RETRIES && (@@ -108,7 +108,7 @@ const InsightsWidget = () => {
)}Loading insights...
+Error: {error}
+Error: {error}
{retryCount < MAX_RETRIES && (Retry attempt {retryCount} of {MAX_RETRIES} @@ -115,6 +123,7 @@ const InsightsWidget = () => { }} aria-label="Retry loading analytics" > + ↻ Retry
Real-time analytics and performance metrics
+