Modernize PTO (Paid Time Off) page with contemporary design
Complete redesign of the PTO management page:
**Layout Improvements**:
- Migrated from Bootstrap 2 to Bootstrap 5 grid system
- Replaced .span classes with modern .col classes
- Side-by-side calendar and form layout on desktop
- Responsive cards with proper spacing
**Removed Google Charts**:
- Replaced sick days chart with 3 colorful stat cards
- Replaced PTO chart with 3 colorful stat cards
- Shows Earned, Taken, Remaining at a glance
- Color-coded with left borders (blue, red, green)
- No loading delays or JavaScript errors
**Modern Form**:
- Bootstrap 5 form controls with proper labels
- Icon-enhanced input groups
- Rounded inputs with better spacing
- Primary button for submission
- Form clears after successful submission
**Enhanced Calendar**:
- Kept FullCalendar but styled with modern theme
- Rounded corners and better button styling
- Brand-colored buttons and events
- Responsive layout
**Improved Alerts**:
- Bootstrap 5 dismissible alerts
- Icon-enhanced success/error messages
- Better visual hierarchy
**Additional Polish**:
- Formatted dates ("December 07, 2024" format)
- Info icons with contextual help
- Card shadows for depth
- Consistent spacing throughout
- Turbolinks compatibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,116 +1,177 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="container-fluid">
|
||||||
<div class="main-container">
|
<!-- Alert Messages -->
|
||||||
<div class="row-fluid">
|
<div class="row">
|
||||||
<div class="span12">
|
<div class="col-12">
|
||||||
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
<div id="success" style="display: none;" class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
<h4 class="alert-heading">
|
<div class="d-flex align-items-center">
|
||||||
Success!
|
<i class="bi bi-check-circle-fill me-2" style="font-size: 1.5rem;"></i>
|
||||||
|
<div>
|
||||||
|
<h5 class="alert-heading mb-1">Success!</h5>
|
||||||
|
<p class="mb-0">Information successfully updated.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="failure" style="display: none;" class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill me-2" style="font-size: 1.5rem;"></i>
|
||||||
|
<div>
|
||||||
|
<h5 class="alert-heading mb-1">Error!</h5>
|
||||||
|
<p class="mb-0">Failed to update. Please try again.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Calendar and Schedule Form Row -->
|
||||||
|
<div class="row g-3">
|
||||||
|
<!-- PTO Calendar -->
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h4 class="mb-0">
|
||||||
|
<i class="bi bi-calendar3 text-primary"></i> PTO Calendar
|
||||||
</h4>
|
</h4>
|
||||||
<p>
|
|
||||||
Information successfully updated.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="calendarDiv" class="card-body">
|
||||||
</div>
|
|
||||||
<div class="row-fluid">
|
|
||||||
<div class="span12">
|
|
||||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
|
||||||
<h4 class="alert-heading">
|
|
||||||
Error!
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
Failed to update.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Begin DP-->
|
|
||||||
<div class="row-fluid">
|
|
||||||
<!-- begin cal -->
|
|
||||||
<div class="span6">
|
|
||||||
<div class="widget">
|
|
||||||
<div class="widget-header">
|
|
||||||
<div class="title">
|
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> PTO Calendar
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="calendarDiv" class="widget-body">
|
|
||||||
<div id='calendar'></div>
|
<div id='calendar'></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End cal-->
|
|
||||||
<div class="span6">
|
<!-- Schedule PTO Form -->
|
||||||
<div class="widget">
|
<div class="col-lg-6">
|
||||||
<div class="widget-header">
|
<div class="card shadow-sm">
|
||||||
<div class="title">
|
<div class="card-header bg-white py-3">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Schedule PTO
|
<h4 class="mb-0">
|
||||||
|
<i class="bi bi-calendar-plus text-primary"></i> Schedule PTO
|
||||||
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="scheduleDiv" class="card-body">
|
||||||
|
<%= form_for @schedule, url: "#", html: { id: "cal_update" } do |s| %>
|
||||||
|
<div class="mb-3">
|
||||||
|
<%= s.label :event_name, "Event Name", class: "form-label" %>
|
||||||
|
<%= s.text_field :event_name, {
|
||||||
|
placeholder: "My PTO",
|
||||||
|
class: "form-control"
|
||||||
|
} %>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin WB-->
|
|
||||||
<div id="scheduleDiv" class="widget-body">
|
<%= s.text_field :event_type, type: "hidden", value: "pto" %>
|
||||||
<%= form_for @schedule, :url => "#",:html => {:id => "cal_update"} do |s|%>
|
|
||||||
<div class="control-group">
|
<div class="mb-3">
|
||||||
<%= s.label :event_name, "Event Name", {:class => "control-label"}%>
|
<%= s.label :event_desc, "Event Description", class: "form-label" %>
|
||||||
<%= s.text_field :event_name, {:placeholder => "My PTO", :class => "span6"}%>
|
<%= s.text_field :event_desc, {
|
||||||
|
placeholder: "Travel to Europe",
|
||||||
|
class: "form-control"
|
||||||
|
} %>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
|
||||||
<%= s.text_field :event_type, {:type => "hidden", :value => "pto", :class => "span6"}%>
|
<div class="mb-3">
|
||||||
</div>
|
<label class="form-label" for="date_range1">Event Dates</label>
|
||||||
<div class="control-group">
|
<div class="input-group">
|
||||||
<%= s.label :event_desc, "Event Description", {:class => "control-label"}%>
|
<span class="input-group-text">
|
||||||
<%= s.text_field :event_desc, {:placeholder => "Travel to Europe", :class => "span6"}%>
|
<i class="bi bi-calendar-range"></i>
|
||||||
</div>
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label" for="date_range1">
|
|
||||||
Event Dates
|
|
||||||
</label>
|
|
||||||
<div class="controls">
|
|
||||||
<div class="input-append">
|
|
||||||
<input type="text" name="date_range1" id="date_range1" class="span8 date_picker" placeholder="Select Date"/>
|
|
||||||
<span class="add-on">
|
|
||||||
<i class="icon-calendar"></i>
|
|
||||||
</span>
|
</span>
|
||||||
|
<input type="text" name="date_range1" id="date_range1" class="form-control date_picker" placeholder="Select Date Range"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<%= s.submit "Submit", {:id => 'cal_update_submit', :class => "btn btn-primary pull-left"} %>
|
<%= s.submit "Schedule PTO", {
|
||||||
|
id: 'cal_update_submit',
|
||||||
|
class: "btn btn-primary"
|
||||||
|
} %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="clearfix">
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Sick Days Stats -->
|
||||||
|
<div class="row mt-3">
|
||||||
|
<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-bandaid text-primary"></i> Sick Days
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center" style="border-left: 4px solid #579da9;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-muted small mb-1">Days Earned</div>
|
||||||
|
<h3 class="mb-0" style="color: #579da9;"><%= @pto.sick_days_earned %></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center" style="border-left: 4px solid #e26666;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-muted small mb-1">Days Taken</div>
|
||||||
|
<h3 class="mb-0" style="color: #e26666;"><%= @pto.sick_days_taken %></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center" style="border-left: 4px solid #1e825e;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-muted small mb-1">Days Remaining</div>
|
||||||
|
<h3 class="mb-0" style="color: #1e825e;"><%= @pto.sick_days_remaining %></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center text-muted mt-3 small">
|
||||||
|
<i class="bi bi-info-circle"></i> As of today: <%= Date.today.strftime("%B %d, %Y") %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PTO Stats -->
|
||||||
|
<div class="row mt-3">
|
||||||
|
<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-umbrella-fill text-primary"></i> Paid Time Off
|
||||||
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<!-- End WB-->
|
<div class="card-body">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center" style="border-left: 4px solid #579da9;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-muted small mb-1">Days Earned</div>
|
||||||
|
<h3 class="mb-0" style="color: #579da9;"><%= @pto.pto_earned %></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End DP-->
|
<div class="col-md-4">
|
||||||
<div class="row-fluid">
|
<div class="card text-center" style="border-left: 4px solid #e26666;">
|
||||||
<div class="span12">
|
<div class="card-body">
|
||||||
<div class="widget">
|
<div class="text-muted small mb-1">Days Taken</div>
|
||||||
<div class="widget-header">
|
<h3 class="mb-0" style="color: #e26666;"><%= @pto.pto_taken %></h3>
|
||||||
<div class="title">
|
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Sick Days
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
</div>
|
||||||
<div id="column_chart_1"></div>
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center" style="border-left: 4px solid #1e825e;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-muted small mb-1">Days Remaining</div>
|
||||||
|
<h3 class="mb-0" style="color: #1e825e;"><%= @pto.pto_days_remaining %></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="text-center text-muted mt-3 small">
|
||||||
<div class="span12">
|
<i class="bi bi-info-circle"></i> As of today: <%= Date.today.strftime("%B %d, %Y") %>
|
||||||
<div class="widget">
|
|
||||||
<div class="widget-header">
|
|
||||||
<div class="title">
|
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Paid Time Off
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="widget-body">
|
|
||||||
<div id="column_chart_2"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,114 +183,40 @@
|
|||||||
<%= javascript_include_tag "fullcalendar.min.js" %>
|
<%= javascript_include_tag "fullcalendar.min.js" %>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function makeActive() {
|
function makeActive() {
|
||||||
$('li[id="pto"]').addClass('active');
|
$('li[id="pto"]').addClass('active');
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
makeActive();
|
||||||
|
|
||||||
|
// Initialize FullCalendar
|
||||||
$('#calendar').fullCalendar({
|
$('#calendar').fullCalendar({
|
||||||
events: <%= get_pto_schedule_schedule_index_path(:format => "json").inspect.html_safe %>,
|
events: <%= get_pto_schedule_schedule_index_path(:format => "json").inspect.html_safe %>,
|
||||||
});
|
height: 'auto',
|
||||||
|
contentHeight: 'auto',
|
||||||
|
aspectRatio: 1.5
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize date range picker
|
||||||
|
|
||||||
//Date picker
|
|
||||||
$('.date_picker').daterangepicker({
|
$('.date_picker').daterangepicker({
|
||||||
opens: 'right'
|
opens: 'right',
|
||||||
|
locale: {
|
||||||
|
format: 'MM/DD/YYYY'
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).ready(function () {
|
// Handle Turbolinks page loads
|
||||||
drawChart1(),
|
$(document).on('turbolinks:load', function() {
|
||||||
drawChart2(),
|
makeActive();
|
||||||
resizeTopWidgets(),
|
|
||||||
makeActive()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
google.load("visualization", "1", {
|
// Form submission
|
||||||
packages: ["corechart"]
|
|
||||||
});
|
|
||||||
|
|
||||||
function drawChart1() {
|
|
||||||
var data = google.visualization.arrayToDataTable([
|
|
||||||
['Current Date', 'Days Earned', 'Days Taken', 'Days Remaining'],
|
|
||||||
[ <%= "As of today: #{Date.today}".inspect.html_safe %>, <%= @pto.sick_days_earned %>, <%= @pto.sick_days_taken %>, <%= @pto.sick_days_remaining %> ], ]);
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
width: 'auto',
|
|
||||||
height: '160',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
colors: ['#579da9', '#e26666', '#1e825e'],
|
|
||||||
tooltip: {
|
|
||||||
textStyle: {
|
|
||||||
color: '#666666',
|
|
||||||
fontSize: 11
|
|
||||||
},
|
|
||||||
showColorCode: true
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
textStyle: {
|
|
||||||
color: 'black',
|
|
||||||
fontSize: 12
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chartArea: {
|
|
||||||
left: 60,
|
|
||||||
top: 10,
|
|
||||||
height: '80%'
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var chart = new google.visualization.ColumnChart(document.getElementById('column_chart_1'));
|
|
||||||
chart.draw(data, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawChart2() {
|
|
||||||
var data = google.visualization.arrayToDataTable([
|
|
||||||
['Current Date', 'Days Earned', 'Days Taken', 'Days Remaining'],
|
|
||||||
[ <%= "As of today: #{Date.today}".inspect.html_safe %>, <%= @pto.pto_earned %>, <%= @pto.pto_taken %>, <%= @pto.pto_days_remaining %> ], ]);
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
width: 'auto',
|
|
||||||
height: '160',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
colors: ['#579da9', '#e26666', '#1e825e'],
|
|
||||||
tooltip: {
|
|
||||||
textStyle: {
|
|
||||||
color: '#666666',
|
|
||||||
fontSize: 11
|
|
||||||
},
|
|
||||||
showColorCode: true
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
textStyle: {
|
|
||||||
color: 'black',
|
|
||||||
fontSize: 12
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chartArea: {
|
|
||||||
left: 60,
|
|
||||||
top: 10,
|
|
||||||
height: '80%'
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var chart = new google.visualization.ColumnChart(document.getElementById('column_chart_2'));
|
|
||||||
chart.draw(data, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeTopWidgets(){
|
|
||||||
var calHeight = $("#calendarDiv").height();
|
|
||||||
$("#scheduleDiv").css({'height':calHeight});
|
|
||||||
};
|
|
||||||
|
|
||||||
$("#cal_update_submit").click(function(event) {
|
$("#cal_update_submit").click(function(event) {
|
||||||
var valuesToSubmit = $("#cal_update").serialize();
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
var valuesToSubmit = $("#cal_update").serialize();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/schedule.json",
|
url: "/schedule.json",
|
||||||
data: valuesToSubmit,
|
data: valuesToSubmit,
|
||||||
@@ -239,7 +226,9 @@ $("#cal_update_submit").click(function(event) {
|
|||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
} else {
|
} else {
|
||||||
$('#success').show(500).delay(1500).fadeOut();
|
$('#success').show(500).delay(1500).fadeOut();
|
||||||
$('#calendar').fullCalendar('refetchEvents')
|
$('#calendar').fullCalendar('refetchEvents');
|
||||||
|
// Clear form
|
||||||
|
$('#cal_update')[0].reset();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
@@ -247,5 +236,47 @@ $("#cal_update_submit").click(function(event) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* FullCalendar modern styling */
|
||||||
|
#calendar {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-toolbar {
|
||||||
|
background: var(--rg-light);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem 0.5rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-button {
|
||||||
|
background: var(--rg-primary) !important;
|
||||||
|
border-color: var(--rg-primary) !important;
|
||||||
|
border-radius: 0.5rem !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
padding: 0.375rem 0.75rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-button:hover {
|
||||||
|
background: var(--rg-primary-dark) !important;
|
||||||
|
border-color: var(--rg-primary-dark) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-day-header {
|
||||||
|
background: var(--rg-light);
|
||||||
|
padding: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event {
|
||||||
|
background: var(--rg-primary);
|
||||||
|
border-color: var(--rg-primary);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-today {
|
||||||
|
background: rgba(230, 57, 70, 0.05) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user