Modernize performance review page with stats cards and enhanced table

Complete redesign of the performance page with modern Bootstrap 5:

Major improvements:
- Add header with graph icon and descriptive subtitle
- Create four stat cards showing key metrics at a glance:
  * Average Score (blue with star icon)
  * Highest Score (red with trophy icon)
  * Latest Score (green with calendar icon)
  * Total Reviews (purple with document icon)
- Stat cards lift and scale numbers on hover
- Modernize chart card with better spacing and min-height
- Enhance chart styling with smooth curves and better colors
- Transform table with modern header styling and icons
- Add reviewer avatars (circular icons) in table rows
- Color-code scores with badges (green=5, blue=4, yellow=3, red<3)
- Add empty state with inbox icon for no reviews
- Replace old Bootstrap 2 classes (row-fluid, span12, widget)
- Use Bootstrap 5 grid system and modern card components
- Add hover effects on table rows and stat cards

The page now provides an engaging, data-rich view of performance history
with clear visual hierarchy and modern design patterns.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ken Johnson
2025-12-07 02:58:27 -05:00
parent 1bd3fab2b9
commit 66fd12481d
+231 -62
View File
@@ -1,50 +1,187 @@
<div class="dashboard-wrapper"> <div class="container-fluid">
<div class="main-container"> <div class="row mb-4">
<div class="row-fluid"> <div class="col-12">
<div class="span12"> <h2 class="mb-3">
<div class="widget"> <i class="bi bi-graph-up-arrow text-primary"></i> Performance Reviews
<div class="widget-header"> </h2>
<div class="title"> <p class="text-muted">Track your performance history and feedback</p>
<span class="fs1" aria-hidden="true" data-icon="&#xe096;"></span> Performance History Visualization </div>
</div> </div>
</div>
<div class="widget-body"> <!-- Performance Summary Stats -->
<div id="line_chart"></div> <div class="row g-3 mb-4">
</div> <%
</div> total_reviews = @perf.count
avg_score = @perf.any? ? (@perf.sum(&:score).to_f / total_reviews).round(1) : 0
latest_score = @perf.last&.score || 0
highest_score = @perf.any? ? @perf.max_by(&:score).score : 0
%>
<div class="col-lg-3 col-md-6">
<div class="card shadow-sm text-center hover-stat-card" style="border-top: 4px solid #579da9;">
<div class="card-body p-4">
<div class="mb-3">
<i class="bi bi-star-fill" style="font-size: 2.5rem; color: #579da9;"></i>
</div> </div>
<div class="row-fluid"> <h6 class="text-muted text-uppercase mb-2" style="font-size: 0.85rem; font-weight: 600; letter-spacing: 0.5px;">
<div class="span12"> Average Score
<div class="widget"> </h6>
<div class="widget-header"> <h2 class="mb-0" style="color: #579da9; font-weight: 700; font-size: 2.5rem;">
<div class="title"> <%= avg_score %>
<span class="fs1" aria-hidden="true" data-icon="&#xe004;"></span> Performance History </h2>
</div> <p class="text-muted mt-2 mb-0 small">Out of 5.0</p>
</div> </div>
<div class="widget-body"> </div>
<table class="table table-bordered table-striped"> </div>
<thead>
<div class="col-lg-3 col-md-6">
<div class="card shadow-sm text-center hover-stat-card" style="border-top: 4px solid var(--rg-primary);">
<div class="card-body p-4">
<div class="mb-3">
<i class="bi bi-trophy-fill" style="font-size: 2.5rem; color: var(--rg-primary);"></i>
</div>
<h6 class="text-muted text-uppercase mb-2" style="font-size: 0.85rem; font-weight: 600; letter-spacing: 0.5px;">
Highest Score
</h6>
<h2 class="mb-0" style="color: var(--rg-primary); font-weight: 700; font-size: 2.5rem;">
<%= highest_score %>
</h2>
<p class="text-muted mt-2 mb-0 small">Best performance</p>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="card shadow-sm text-center hover-stat-card" style="border-top: 4px solid #1e825e;">
<div class="card-body p-4">
<div class="mb-3">
<i class="bi bi-calendar-check-fill" style="font-size: 2.5rem; color: #1e825e;"></i>
</div>
<h6 class="text-muted text-uppercase mb-2" style="font-size: 0.85rem; font-weight: 600; letter-spacing: 0.5px;">
Latest Score
</h6>
<h2 class="mb-0" style="color: #1e825e; font-weight: 700; font-size: 2.5rem;">
<%= latest_score %>
</h2>
<p class="text-muted mt-2 mb-0 small">Most recent review</p>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="card shadow-sm text-center hover-stat-card" style="border-top: 4px solid #b5799e;">
<div class="card-body p-4">
<div class="mb-3">
<i class="bi bi-file-earmark-text-fill" style="font-size: 2.5rem; color: #b5799e;"></i>
</div>
<h6 class="text-muted text-uppercase mb-2" style="font-size: 0.85rem; font-weight: 600; letter-spacing: 0.5px;">
Total Reviews
</h6>
<h2 class="mb-0" style="color: #b5799e; font-weight: 700; font-size: 2.5rem;">
<%= total_reviews %>
</h2>
<p class="text-muted mt-2 mb-0 small">Performance evaluations</p>
</div>
</div>
</div>
</div>
<!-- Performance Chart -->
<div class="row mb-4">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<h4 class="mb-0">
<i class="bi bi-graph-up text-primary"></i> Performance Trend
</h4>
<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>
</div>
</div>
</div>
</div>
<!-- Performance History Table -->
<div class="row">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<h4 class="mb-0">
<i class="bi bi-table text-primary"></i> Performance History
</h4>
<p class="text-muted mb-0 small mt-1">Detailed review feedback and comments</p>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th style="width: 20%;">
<i class="bi bi-person-badge me-2"></i>Reviewer
</th>
<th style="width: 15%;">
<i class="bi bi-calendar-event me-2"></i>Date
</th>
<th style="width: 10%;">
<i class="bi bi-star me-2"></i>Score
</th>
<th style="width: 55%;">
<i class="bi bi-chat-left-text me-2"></i>Comments
</th>
</tr>
</thead>
<tbody>
<% if @perf.any? %>
<% @perf.each do |p| %>
<tr>
<td class="fw-semibold">
<div class="d-flex align-items-center">
<div class="rounded-circle bg-primary bg-opacity-10 p-2 me-2">
<i class="bi bi-person-fill text-primary"></i>
</div>
<%= p.reviewer_name %>
</div>
</td>
<td>
<span class="badge bg-light text-dark">
<i class="bi bi-calendar-date me-1"></i><%= p.date_submitted %>
</span>
</td>
<td>
<%
score_color = case p.score
when 5 then 'success'
when 4 then 'primary'
when 3 then 'warning'
else 'danger'
end
%>
<span class="badge bg-<%= score_color %>" style="font-size: 1rem; padding: 0.5rem 0.75rem;">
<%= p.score %> / 5
</span>
</td>
<td>
<div class="text-muted">
<%= p.comments %>
</div>
</td>
</tr>
<% end %>
<% else %>
<tr> <tr>
<th style="width:16%">Reviewer</th> <td colspan="4" class="text-center text-muted py-5">
<th style="width:16%">Date</th> <i class="bi bi-inbox" style="font-size: 3rem; opacity: 0.3;"></i>
<th style="width:16%">Score</th> <p class="mt-3 mb-0">No performance reviews available yet</p>
<th style="width:16%">Comments</th> </td>
</tr>
</thead>
<tbody>
<% @perf.each do |p| %>
<tr>
<td><%= p.reviewer_name %></td>
<td><%= p.date_submitted %></td>
<td><%= p.score %></td>
<td><%= p.comments %></td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
</div>
</div> </div>
</div> </div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -79,47 +216,52 @@ function drawChart2() {
var options = { var options = {
min: 1, min: 1,
max: 5, max: 5,
width: 'auto', width: '100%',
height: '160', height: 300,
backgroundColor: 'transparent', backgroundColor: 'transparent',
colors: ['#e26666', '#579da9', '#1e825e', '#b5799e', '#dba26b'], colors: ['#e63946'],
tooltip: { tooltip: {
textStyle: { textStyle: {
color: '#666666', color: '#666666',
fontSize: 11 fontSize: 12
}, },
showColorCode: true showColorCode: true
}, },
legend: { legend: {
textStyle: { textStyle: {
color: 'black', color: '#333333',
fontSize: 12 fontSize: 13
} }
}, },
chartArea: { chartArea: {
left: 100, left: 60,
top: 10 top: 20,
right: 20,
bottom: 50,
width: '90%',
height: '70%'
}, },
focusTarget: 'category', focusTarget: 'category',
hAxis: { hAxis: {
textStyle: { textStyle: {
color: 'black', color: '#666666',
fontSize: 12 fontSize: 11
} },
slantedText: true,
slantedTextAngle: 45
}, },
vAxis: { vAxis: {
textStyle: { textStyle: {
color: 'black', color: '#666666',
fontSize: 12 fontSize: 11
},
gridlines: {
color: '#f0f0f0'
} }
}, },
pointSize: 8, pointSize: 6,
chartArea: { lineWidth: 3,
left: 60, curveType: 'function'
top: 10,
height: '80%'
},
lineWidth: 2,
}; };
var chart = new google.visualization.LineChart(chartElement); var chart = new google.visualization.LineChart(chartElement);
@@ -159,4 +301,31 @@ $(document).on('turbolinks:load', function() {
initializeChart(); initializeChart();
}); });
</script> </script>
<style>
.hover-stat-card {
transition: all 0.3s ease;
}
.hover-stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(0,0,0,0.15) !important;
}
.hover-stat-card h2 {
transition: transform 0.3s ease;
}
.hover-stat-card:hover h2 {
transform: scale(1.05);
}
.table tbody tr {
transition: background-color 0.2s ease;
}
.table tbody tr:hover {
background-color: rgba(230, 57, 70, 0.03);
}
</style>