Modernize direct deposit pay page with two-column layout

Complete redesign of the pay/direct deposit management page:

Layout improvements:
- Two-column responsive layout (forms left, table right)
- Forms column (4/12):
  * Add Direct Deposit form with green theme and gradient header
  * Decrypt Account form with yellow/warning theme
  * Both cards have left border accents
- Table column (8/12):
  * DataTable showing existing accounts
  * "Why Encrypted?" button in header
  * Three info cards below explaining benefits

Form enhancements:
- All form controls upgraded to large size with icons
- Input groups with trailing icons (bank, routing, lock, percent)
- Helper text below each field for guidance
- Full-width submit buttons in themed colors
- Tip boxes with security/convenience info
- Auto-clear forms after successful submission

Table improvements:
- Modern Bootstrap 5 table with hover effects
- Icons in column headers (lock, diagram, percent, gear)
- Enhanced data display:
  * Account numbers in monospace code blocks
  * Routing numbers in light badges
  * Deposit percentages in green success badges
  * Delete buttons styled as outline-danger with trash icon
- Custom DataTables pagination styling matching theme
- Empty state message for no accounts

JavaScript enhancements:
- Replace basic alerts with modern Bootstrap-styled overlays
- Decrypted account number shows in floating alert with unlock icon
- "Why Encrypted?" shows modal-like dialog with close button
- Delete confirmation improved
- Turbolinks compatibility
- Form reset after success

Info cards:
- Instant Access (blue) - explain direct deposit timing
- Secure & Encrypted (green) - highlight security features
- Split Deposits (yellow) - describe multi-account feature

The page now provides a banking-grade interface for managing
direct deposit with clear visual hierarchy and modern UX.

🤖 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 03:08:06 -05:00
parent cff40e68ac
commit aaccdd25ac
+339 -142
View File
@@ -1,144 +1,246 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
<div class="container-fluid">
<!-- Header -->
<div class="row mb-4">
<div class="col-12">
<h2 class="mb-3">
<i class="bi bi-bank text-primary"></i> Direct Deposit & Pay
</h2>
<p class="text-muted">Manage your direct deposit accounts and payment settings</p>
</div>
</div>
<!-- Alert Messages -->
<div class="row">
<div class="col-12">
<div id="success" style="display: none;" class="alert alert-success alert-dismissible fade show" role="alert">
<div class="d-flex align-items-center">
<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>
<div class="row g-3">
<!-- Left Column - Forms -->
<div class="col-lg-4">
<!-- Add Direct Deposit Form -->
<div class="card shadow-sm mb-3" style="border-left: 4px solid var(--rg-success);">
<div class="card-header py-3" style="background: linear-gradient(135deg, rgba(6, 214, 160, 0.05), rgba(30, 130, 94, 0.05));">
<h4 class="mb-0">
<i class="bi bi-plus-circle text-success"></i> Add Direct Deposit
</h4>
<p>
Information successfully updated.
</p>
<p class="text-muted mb-0 small mt-1">Set up a new account</p>
</div>
</div>
<div class="row-fluid">
<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>
<!-- Begin Row-Fluid for Inputs -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe08e;"></span> Direct Deposit
</div>
</div>
<div class="widget-body">
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "bank_info_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :bank_account_num, params[:bank_account_num], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {:placeholder => "Bank Routing Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :dd_percent, params[:dd_percent], {:placeholder => "Percentage of Deposit"} %>
<span class="add-on">%</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "dd_form_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<!-- End Row-Fluid for Inputs-->
<!-- ###################-->
<!-- Begin Dynamic Table ColSpan Table -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<!-- Begin Widget Header-->
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Accounts
</div>
</div>
<!-- End Widget Header-->
<div class="widget-body">
<div id="dt_example" class="example_alt_pagination">
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
<thead>
<tr>
<th style="width:30%">
Encrypted Bank Account Number
<%=link_to "#", { :style => "color:#AA6F93", :id => "encrypted_acct_question"} do %>
<span class="box1">
<span aria-hidden="true" class="icon-question"></span>
<div class="card-body p-4">
<%= form_tag "#", { class: "needs-validation", id: "bank_info_form" } do %>
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-hash text-success me-2"></i>Bank Account Number
</label>
<div class="input-group input-group-lg">
<%= text_field_tag :bank_account_num, params[:bank_account_num], {
placeholder: "Enter account number",
class: "form-control"
} %>
<span class="input-group-text bg-white">
<i class="bi bi-bank2"></i>
</span>
</div>
<small class="text-muted">Your bank account number</small>
</div>
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-sign-turn-right text-success me-2"></i>Bank Routing Number
</label>
<div class="input-group input-group-lg">
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {
placeholder: "Enter routing number",
class: "form-control"
} %>
<span class="input-group-text bg-white">
<i class="bi bi-diagram-3"></i>
</span>
</div>
<small class="text-muted">9-digit routing number</small>
</div>
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-percent text-success me-2"></i>Percentage of Deposit
</label>
<div class="input-group input-group-lg">
<%= text_field_tag :dd_percent, params[:dd_percent], {
placeholder: "e.g., 100",
class: "form-control"
} %>
<span class="input-group-text bg-white">%</span>
</div>
<small class="text-muted">What percentage to deposit (1-100)</small>
</div>
<div class="d-grid">
<%= submit_tag "Add Account", {
id: "dd_form_btn",
class: "btn btn-success btn-lg"
} %>
</div>
<div class="mt-3 p-3 rounded" style="background: var(--rg-light); border-left: 3px solid var(--rg-success);">
<small class="text-muted">
<i class="bi bi-shield-lock-fill text-success me-1"></i>
<strong>Secure:</strong> All account information is encrypted
</small>
</div>
<% end %>
</div>
</div>
<!-- Decrypt Form -->
<div class="card shadow-sm" style="border-left: 4px solid var(--rg-warning);">
<div class="card-header py-3" style="background: linear-gradient(135deg, rgba(255, 183, 3, 0.05), rgba(251, 133, 0, 0.05));">
<h4 class="mb-0">
<i class="bi bi-unlock text-warning"></i> Decrypt Account Number
</h4>
<p class="text-muted mb-0 small mt-1">View unencrypted account number</p>
</div>
<div class="card-body p-4">
<%= form_tag "#", { class: "needs-validation", id: "decrypt_form" } do %>
<div class="mb-4">
<label class="form-label fw-semibold">
<i class="bi bi-key-fill text-warning me-2"></i>Encrypted Account Number
</label>
<div class="input-group input-group-lg">
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {
placeholder: "Paste encrypted value",
class: "form-control"
} %>
<span class="input-group-text bg-white">
<i class="bi bi-lock"></i>
</span>
</div>
<small class="text-muted">Copy from the table on the right</small>
</div>
<div class="d-grid">
<%= submit_tag "Decrypt", {
id: "decrypt_btn",
class: "btn btn-warning btn-lg"
} %>
</div>
<div class="mt-3 p-3 rounded" style="background: var(--rg-light); border-left: 3px solid var(--rg-warning);">
<small class="text-muted">
<i class="bi bi-info-circle-fill text-warning me-1"></i>
Decryption is for your convenience and security verification
</small>
</div>
<% end %>
</div>
</div>
</div>
<!-- Right Column - Accounts Table -->
<div class="col-lg-8">
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<div class="d-flex justify-content-between align-items-start">
<div>
<h4 class="mb-0">
<i class="bi bi-list-ul text-primary"></i> Direct Deposit Accounts
</h4>
<p class="text-muted mb-0 small mt-1">Your configured bank accounts</p>
</div>
<button type="button" class="btn btn-sm btn-outline-secondary" id="encrypted_acct_question">
<i class="bi bi-question-circle"></i> Why Encrypted?
</button>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0" id="data_table">
<thead class="table-light">
<tr>
<th style="width: 35%;">
<i class="bi bi-shield-lock me-2"></i>Encrypted Account Number
</th>
<th style="width:25%">
Bank Routing Number
<th style="width: 25%;">
<i class="bi bi-diagram-3 me-2"></i>Routing Number
</th>
<th style="width:20%">
Percentage of Deposit
<th style="width: 20%;">
<i class="bi bi-percent me-2"></i>Deposit %
</th>
<th style="width:25%">
Action
<th style="width: 20%;">
<i class="bi bi-gear me-2"></i>Actions
</th>
</tr>
</thead>
<tbody>
<!-- DataTable will populate this -->
</tbody>
</table>
<div class="clearfix">
</div>
</div>
</div> <!-- end of widget body-->
</div>
</div>
</div
<!-- End Dynamic Table ColSpan Table -->
<!-- ###################-->
<!-- Begin Row-Fluid for Decryption Input -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe11b;"></span> Decrypt Bank Account Number
</div>
</div>
<div class="widget-body">
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "decrypt_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "decrypt_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
<!-- Info Cards -->
<div class="row mt-3 g-3">
<div class="col-md-4">
<div class="card shadow-sm h-100" style="border-left: 3px solid #579da9;">
<div class="card-body">
<h6 class="card-title">
<i class="bi bi-lightning-charge-fill text-primary"></i> Instant Access
</h6>
<p class="card-text small text-muted">
Your paycheck is deposited directly into your account on payday.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm h-100" style="border-left: 3px solid #1e825e;">
<div class="card-body">
<h6 class="card-title">
<i class="bi bi-shield-check text-success"></i> Secure & Encrypted
</h6>
<p class="card-text small text-muted">
All banking information is encrypted using industry-standard security.
</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm h-100" style="border-left: 3px solid var(--rg-warning);">
<div class="card-body">
<h6 class="card-title">
<i class="bi bi-piggy-bank text-warning"></i> Split Deposits
</h6>
<p class="card-text small text-muted">
Allocate different percentages of your pay to multiple accounts.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Row-Fluid for Decryption Input -->
</div>
</div>
@@ -151,9 +253,8 @@
a user to delete that direct deposit entry
*/
function buildDeleteLink(dd_id){
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="delete-row">' +
'<i class="icon-trash">'+
'</i></a>'
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="btn btn-sm btn-outline-danger delete-row">' +
'<i class="bi bi-trash"></i> Delete</a>'
return link
};
@@ -165,13 +266,12 @@ function parseDirectDepostInfo(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
$.each(msg.user, function(index, val){
$('#data_table').dataTable().fnAddData( [
val.bank_account_num,
val.bank_routing_num,
val.percent_of_deposit,
'<code class="text-monospace">' + val.bank_account_num + '</code>',
'<span class="badge bg-light text-dark">' + val.bank_routing_num + '</span>',
'<span class="badge bg-success">' + val.percent_of_deposit + '%</span>',
buildDeleteLink(val.id)
] );
});
};
/*
@@ -198,7 +298,10 @@ function populateTable() {
*/
function createDataTable(){
$('#data_table').dataTable({
"sPaginationType": "full_numbers"
"sPaginationType": "full_numbers",
"language": {
"emptyTable": "No direct deposit accounts configured yet"
}
});
};
@@ -206,12 +309,12 @@ function createDataTable(){
This function doesn't really work right now but is supposed to offer the user a
"delete confirmation" message
*/
$('.delete-row').click(function () {
var conf = confirm('Continue delete?');
if (conf) $(this).parents('tr').fadeOut(function () {
$(this).remove();
});
$(document).on('click', '.delete-row', function (e) {
var conf = confirm('Are you sure you want to delete this account?');
if (!conf) {
e.preventDefault();
return false;
}
});
/*
@@ -220,7 +323,27 @@ $('.delete-row').click(function () {
*/
function decryptShow(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
alert("Decrypted Account Number: " + msg.account_num);
// Modern alert using Bootstrap modal-like appearance
var alertHtml = '<div class="alert alert-info alert-dismissible fade show" role="alert" style="position: fixed; top: 100px; left: 50%; transform: translateX(-50%); z-index: 9999; min-width: 400px; box-shadow: 0 4px 16px rgba(0,0,0,0.2);">' +
'<div class="d-flex align-items-center">' +
'<i class="bi bi-unlock-fill me-3" style="font-size: 2rem;"></i>' +
'<div>' +
'<h5 class="alert-heading mb-1">Decrypted Account Number</h5>' +
'<p class="mb-0"><strong style="font-size: 1.2rem; font-family: monospace;">' + msg.account_num + '</strong></p>' +
'</div>' +
'</div>' +
'<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>' +
'</div>';
$('body').append(alertHtml);
// Auto-remove after 5 seconds
setTimeout(function() {
$('.alert').fadeOut(function() {
$(this).remove();
});
}, 5000);
};
/*
@@ -239,6 +362,7 @@ $("#decrypt_btn").click(function(event){
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
decryptShow(response);
$('#decrypt_form')[0].reset();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
@@ -260,6 +384,7 @@ $("#dd_form_btn").click(function(event) {
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
$('#bank_info_form')[0].reset();
populateTable();
},
error: function(event) {
@@ -270,10 +395,27 @@ $("#dd_form_btn").click(function(event) {
$("#encrypted_acct_question").click(function(event) {
event.preventDefault();
alert("For your safety your account number is stored encrypted as well as presented to you \nin an encrypted form.\n\n" +
"For your convenience, you can decrypt your bank account number at any time using our\n" +
"conveniently located decryption function."
)
// Create modern Bootstrap modal-like alert
var modalHtml = '<div class="modal fade show d-block" tabindex="-1" style="background: rgba(0,0,0,0.5);">' +
'<div class="modal-dialog modal-dialog-centered">' +
'<div class="modal-content">' +
'<div class="modal-header" style="background: linear-gradient(135deg, rgba(69, 123, 157, 0.1), rgba(29, 53, 87, 0.1)); border-left: 4px solid var(--rg-secondary);">' +
'<h5 class="modal-title"><i class="bi bi-shield-lock-fill text-primary me-2"></i>Why Are Account Numbers Encrypted?</h5>' +
'<button type="button" class="btn-close" onclick="$(this).closest(\'.modal\').remove();"></button>' +
'</div>' +
'<div class="modal-body">' +
'<p class="mb-3"><i class="bi bi-check-circle-fill text-success me-2"></i><strong>For your safety</strong>, your account number is stored encrypted in our database and presented to you in an encrypted form.</p>' +
'<p class="mb-0"><i class="bi bi-unlock-fill text-warning me-2"></i><strong>For your convenience</strong>, you can decrypt your bank account number at any time using our conveniently located decryption function.</p>' +
'</div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-primary" onclick="$(this).closest(\'.modal\').remove();">Got It</button>' +
'</div>' +
'</div>' +
'</div>' +
'</div>';
$('body').append(modalHtml);
});
/*
@@ -288,10 +430,65 @@ function makeActive(){
2) createDataTable - Initializes the dataTable as such
3) populateTable - populates the newly initialized dataTable
*/
$(document).ready(
makeActive,
createDataTable(),
populateTable()
)
$(document).ready(function() {
makeActive();
createDataTable();
populateTable();
});
// Handle Turbolinks page loads
$(document).on('turbolinks:load', function() {
makeActive();
});
</script>
<style>
/* DataTables styling adjustments */
.dataTables_wrapper .dataTables_paginate .paginate_button {
padding: 0.375rem 0.75rem;
margin: 0 0.125rem;
border-radius: 0.5rem;
border: 1px solid #dee2e6;
background: white;
color: var(--rg-dark);
}
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
background: var(--rg-primary);
color: white;
border-color: var(--rg-primary);
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
background: var(--rg-primary);
color: white;
border-color: var(--rg-primary);
}
.dataTables_filter input {
border-radius: 0.75rem;
border: 2px solid #e9ecef;
padding: 0.5rem 1rem;
}
.dataTables_filter input:focus {
border-color: var(--rg-primary);
outline: none;
box-shadow: 0 0 0 3px rgba(230, 57, 70, 0.1);
}
.text-monospace {
font-family: 'Courier New', monospace;
font-size: 0.9rem;
}
/* Table hover effect */
#data_table tbody tr {
transition: background-color 0.2s ease;
}
#data_table tbody tr:hover {
background-color: rgba(230, 57, 70, 0.03);
}
</style>