Files
railsgoat/app/views/pay/index.html.erb
T
Ken Johnson 117203307d Simplify and declutter pay page design
Removes visual clutter and simplifies the interface for better usability:

Layout simplification:
- Change column split from 4/8 to 5/7 for better balance
- Remove gradient backgrounds from card headers
- Use simple white headers with clean icons
- Increase spacing between sections (g-4 gap)
- Remove info cards at bottom to reduce page length

Form simplification:
- Remove input group icons and addons
- Use clean standalone inputs without decorations
- Remove helper text under each field (info in placeholder)
- Reduce button sizes from btn-lg to standard
- Remove decorative tip boxes
- Simpler labels without icons
- Reduce vertical spacing (mb-3 instead of mb-4)

Table simplification:
- Remove icons from table headers
- Cleaner header text ("Your Accounts" vs "Direct Deposit Accounts")
- Remove subtitle text from headers

Input styling:
- Smaller, cleaner inputs (0.5rem radius, 1px border)
- Smaller padding (0.625rem vs 0.875rem)
- Smaller font size (0.95rem)
- Subtle focus rings (3px glow)
- Color-coded focus: green for add, yellow for decrypt
- Removed complex gradients and shadows

The page now has a clean, uncluttered appearance with better
visual hierarchy and easier-to-scan content.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-07 03:22:15 -05:00

457 lines
14 KiB
Plaintext

<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-4">
<!-- Left Column - Forms -->
<div class="col-lg-5">
<!-- Add Direct Deposit Form -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-white py-3">
<h5 class="mb-0">
<i class="bi bi-plus-circle text-success me-2"></i>Add Direct Deposit
</h5>
</div>
<div class="card-body p-4">
<%= form_tag "#", { class: "needs-validation", id: "bank_info_form" } do %>
<div class="mb-3">
<label class="form-label">Bank Account Number</label>
<%= text_field_tag :bank_account_num, params[:bank_account_num], {
placeholder: "Enter account number",
class: "form-control"
} %>
</div>
<div class="mb-3">
<label class="form-label">Bank Routing Number</label>
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {
placeholder: "9-digit routing number",
class: "form-control"
} %>
</div>
<div class="mb-3">
<label class="form-label">Percentage of Deposit</label>
<%= text_field_tag :dd_percent, params[:dd_percent], {
placeholder: "1-100",
class: "form-control"
} %>
</div>
<div class="d-grid mt-4">
<%= submit_tag "Add Account", {
id: "dd_form_btn",
class: "btn btn-success"
} %>
</div>
<% end %>
</div>
</div>
<!-- Decrypt Form -->
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<h5 class="mb-0">
<i class="bi bi-unlock text-warning me-2"></i>Decrypt Account
</h5>
</div>
<div class="card-body p-4">
<%= form_tag "#", { class: "needs-validation", id: "decrypt_form" } do %>
<div class="mb-3">
<label class="form-label">Encrypted Account Number</label>
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {
placeholder: "Paste encrypted value",
class: "form-control"
} %>
</div>
<div class="d-grid mt-4">
<%= submit_tag "Decrypt", {
id: "decrypt_btn",
class: "btn btn-warning"
} %>
</div>
<% end %>
</div>
</div>
</div>
<!-- Right Column - Accounts Table -->
<div class="col-lg-7">
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="bi bi-list-ul text-primary me-2"></i>Your Accounts
</h5>
<button type="button" class="btn btn-sm btn-outline-secondary" id="encrypted_acct_question">
<i class="bi bi-question-circle me-1"></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>Account Number</th>
<th>Routing Number</th>
<th>Deposit %</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- DataTable will populate this -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<%= javascript_include_tag "jquery.dataTables.min.js" %>
<script type="text/javascript">
/*
buildDeleteLink accepts a direct deposit ID and builds a link that enables
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="btn btn-sm btn-outline-danger delete-row">' +
'<i class="bi bi-trash"></i> Delete</a>'
return link
};
/*
parseDirectDepositInfo accepts the response object and parses the JSON response, then
populates the direct deposit data table.
*/
function parseDirectDepostInfo(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
var table = $('#data_table').DataTable();
$.each(msg.user, function(index, val){
table.row.add( [
'<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)
] );
});
table.draw();
};
/*
createDataTable initializes the dd table as a datatable
*/
function createDataTable(){
// Check if DataTable is already initialized
if ($.fn.DataTable.isDataTable('#data_table')) {
$('#data_table').DataTable().destroy();
}
$('#data_table').DataTable({
"sPaginationType": "full_numbers",
"language": {
"emptyTable": "No direct deposit accounts configured yet"
},
"autoWidth": false,
"searching": true,
"ordering": true
});
};
/*
populateTable will first clear the existing dd table, then call the appropriate
endpoint to retrieve direct deposit entries and finally, provide parseDirectDepositInfo
with the response from the endpoint in order to populate the data table.
*/
function populateTable() {
var table = $('#data_table').DataTable();
table.clear();
$.ajax({
url: <%= sanitize(user_pay_path(:format => "json", user_id: current_user.id, id: current_user.id).inspect) %>,
type: "GET",
success: function(response) {
parseDirectDepostInfo(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
};
/*
This function doesn't really work right now but is supposed to offer the user a
"delete confirmation" message
*/
$(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;
}
});
/*
decryptShow parses the json response from the application and then renders
a decrypted version of the user's account number
*/
function decryptShow(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
// 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);
};
/*
This function overrides the decrypt buttons (submit button's) native behavior,
allowing an ajax call to be made with the decrypt_form's inputs which is decrypted
server side with a JSON response containing the decrypted value. The decrypted value is
then passed to decryptShow();
*/
$("#decrypt_btn").click(function(event){
var valuesToSubmit = $("#decrypt_form").serialize();
event.preventDefault();
$.ajax({
url: <%= sanitize(decrypted_bank_acct_num_user_pay_index_path(:format => "json", user_id: current_user.id).inspect) %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
decryptShow(response);
$('#decrypt_form')[0].reset();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
/*
This function overrides the dd_form_btn's native behavior in order to submit an ajax request
that updates the user's direct deposit information. Upon success, the populateTable() function
is called in order to update the dataTable on the page to reflect the latest entry.
*/
$("#dd_form_btn").click(function(event) {
var valuesToSubmit = $("#bank_info_form").serialize();
event.preventDefault();
$.ajax({
url: <%= sanitize(update_dd_info_user_pay_index_path(:format => "json").inspect) %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
$('#bank_info_form')[0].reset();
populateTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
$("#encrypted_acct_question").click(function(event) {
event.preventDefault();
// 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);
});
/*
Make the sidebar element "Pay" active.
*/
function makeActive(){
$('li[id="pay"]').addClass('active');
};
/*
Initialize page - called on both ready and turbolinks:load
*/
function initializePage() {
makeActive();
createDataTable();
populateTable();
}
// Handle normal page loads
$(document).ready(function() {
initializePage();
});
// Handle Turbolinks page loads
$(document).on('turbolinks:load', function() {
initializePage();
});
</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);
}
/* Clean, minimal form controls */
.form-control {
border-radius: 0.5rem;
border: 1px solid #dee2e6;
padding: 0.625rem 0.875rem;
font-size: 0.95rem;
transition: all 0.2s ease;
}
.form-control:focus {
border-color: var(--rg-primary);
box-shadow: 0 0 0 3px rgba(230, 57, 70, 0.1);
outline: none;
}
#bank_info_form .form-control:focus {
border-color: var(--rg-success);
box-shadow: 0 0 0 3px rgba(6, 214, 160, 0.1);
}
#decrypt_form .form-control:focus {
border-color: var(--rg-warning);
box-shadow: 0 0 0 3px rgba(255, 183, 3, 0.1);
}
.form-label {
font-weight: 500;
color: var(--rg-dark);
margin-bottom: 0.4rem;
font-size: 0.9rem;
}
/* Button styling */
.btn {
border-radius: 0.5rem;
font-weight: 500;
transition: all 0.2s ease;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
</style>