Replace Google Charts with modern CSS timeline visualization
Removes problematic Google Charts dependency and creates a cleaner, more reliable performance trend visualization. Changes: - Remove all Google Charts JavaScript code (100+ lines) - Replace chart with visual timeline showing each review chronologically - Each timeline item displays: * Date at top, reviewer name at bottom * Colored circular badge with score number (green=5, blue=4, yellow=3, red<3) * Horizontal progress bar showing score percentage with comments - Add smooth animations: fade-in on load, scale on dot hover, slide on bar hover - Color-coded by score for instant visual feedback - Fully responsive with mobile layout - No external dependencies - pure CSS solution - Add empty state with graph icon if no performance data The timeline provides better visual hierarchy and eliminates the blank space issue caused by Google Charts loading failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -86,7 +86,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Performance Chart -->
|
||||
<!-- Performance Timeline -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm">
|
||||
@@ -97,7 +97,48 @@
|
||||
<p class="text-muted mb-0 small mt-1">Your performance scores over time</p>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<div id="line_chart" style="min-height: 300px;"></div>
|
||||
<% if @perf.any? %>
|
||||
<div class="performance-timeline">
|
||||
<% @perf.each_with_index do |p, index| %>
|
||||
<%
|
||||
score_percentage = (p.score.to_f / 5.0) * 100
|
||||
score_color = case p.score
|
||||
when 5 then '#1e825e'
|
||||
when 4 then '#579da9'
|
||||
when 3 then '#ffb703'
|
||||
else '#e26666'
|
||||
end
|
||||
%>
|
||||
<div class="timeline-item" style="animation-delay: <%= index * 0.1 %>s;">
|
||||
<div class="timeline-marker">
|
||||
<div class="timeline-date">
|
||||
<small class="text-muted"><%= p.date_submitted %></small>
|
||||
</div>
|
||||
<div class="timeline-dot" style="background-color: <%= score_color %>;">
|
||||
<span style="font-weight: 700; color: white; font-size: 0.9rem;"><%= p.score %></span>
|
||||
</div>
|
||||
<div class="timeline-reviewer">
|
||||
<small class="text-muted"><%= p.reviewer_name %></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="progress" style="height: 30px; border-radius: 15px;">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
style="width: <%= score_percentage %>%; background-color: <%= score_color %>; font-weight: 600; font-size: 1rem;"
|
||||
aria-valuenow="<%= p.score %>" aria-valuemin="0" aria-valuemax="5">
|
||||
<%= p.score %> / 5 - <%= p.comments %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="bi bi-graph-up" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
<p class="mt-3 mb-0">No performance data to display</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -187,120 +228,18 @@
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
// Prevent multiple initializations with Turbolinks
|
||||
if (typeof window.performanceChartLoaded === 'undefined') {
|
||||
window.performanceChartLoaded = false;
|
||||
}
|
||||
|
||||
function drawChart2() {
|
||||
// Guard against calling before Google Charts is loaded
|
||||
if (typeof google === 'undefined' || !google.visualization) {
|
||||
console.warn('Google Charts not loaded yet');
|
||||
return;
|
||||
}
|
||||
|
||||
var chartElement = document.getElementById('line_chart');
|
||||
if (!chartElement) {
|
||||
console.warn('Chart element not found');
|
||||
return;
|
||||
}
|
||||
|
||||
var data = google.visualization.arrayToDataTable([
|
||||
['Year', 'Score'],
|
||||
<% @perf.each do |p| %>
|
||||
// Let's just hope this data isn't suspectible during later releases ;-)
|
||||
[ <%= "#{p.date_submitted}".inspect.html_safe %>, <%= p.score %> ],
|
||||
<% end %>
|
||||
]);
|
||||
|
||||
var options = {
|
||||
min: 1,
|
||||
max: 5,
|
||||
width: '100%',
|
||||
height: 300,
|
||||
backgroundColor: 'transparent',
|
||||
colors: ['#e63946'],
|
||||
tooltip: {
|
||||
textStyle: {
|
||||
color: '#666666',
|
||||
fontSize: 12
|
||||
},
|
||||
showColorCode: true
|
||||
},
|
||||
legend: {
|
||||
textStyle: {
|
||||
color: '#333333',
|
||||
fontSize: 13
|
||||
}
|
||||
},
|
||||
chartArea: {
|
||||
left: 60,
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 50,
|
||||
width: '90%',
|
||||
height: '70%'
|
||||
},
|
||||
focusTarget: 'category',
|
||||
hAxis: {
|
||||
textStyle: {
|
||||
color: '#666666',
|
||||
fontSize: 11
|
||||
},
|
||||
slantedText: true,
|
||||
slantedTextAngle: 45
|
||||
},
|
||||
vAxis: {
|
||||
textStyle: {
|
||||
color: '#666666',
|
||||
fontSize: 11
|
||||
},
|
||||
gridlines: {
|
||||
color: '#f0f0f0'
|
||||
}
|
||||
},
|
||||
pointSize: 6,
|
||||
lineWidth: 3,
|
||||
curveType: 'function'
|
||||
};
|
||||
|
||||
var chart = new google.visualization.LineChart(chartElement);
|
||||
chart.draw(data, options);
|
||||
}
|
||||
|
||||
function makeActive(){
|
||||
$('li[id="performance"]').addClass('active');
|
||||
};
|
||||
|
||||
function initializeChart() {
|
||||
makeActive();
|
||||
|
||||
// Check if Google Charts visualization is already loaded
|
||||
if (typeof google !== 'undefined' && google.visualization && google.visualization.LineChart) {
|
||||
drawChart2();
|
||||
window.performanceChartLoaded = true;
|
||||
} else if (typeof google !== 'undefined' && google.load && !window.performanceChartLoaded) {
|
||||
// Load Google Charts
|
||||
google.load("visualization", "1", {
|
||||
packages: ["corechart"],
|
||||
callback: function() {
|
||||
drawChart2();
|
||||
window.performanceChartLoaded = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('Google JSAPI not available');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize immediately (page is already loaded with Turbolinks)
|
||||
initializeChart();
|
||||
$(document).ready(function() {
|
||||
makeActive();
|
||||
});
|
||||
|
||||
// Handle Turbolinks page loads
|
||||
$(document).on('turbolinks:load', function() {
|
||||
initializeChart();
|
||||
makeActive();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -328,4 +267,81 @@ $(document).on('turbolinks:load', function() {
|
||||
.table tbody tr:hover {
|
||||
background-color: rgba(230, 57, 70, 0.03);
|
||||
}
|
||||
|
||||
/* Performance Timeline Styles */
|
||||
.performance-timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
opacity: 0;
|
||||
animation: fadeInUp 0.6s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 120px;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.timeline-item:hover .timeline-dot {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.timeline-date, .timeline-reviewer {
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.progress {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.timeline-item:hover .progress {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.timeline-item {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user