tweaked layout, added JS for live totals

This commit is contained in:
2026-03-07 21:47:03 -05:00
parent 67513c527e
commit 151bbf2d7f
6 changed files with 126 additions and 17 deletions

View File

@@ -6,14 +6,15 @@
data-nested-form
data-wrapper-selector=".line-item">
<p><strong><%= t :label_line_items %></strong></p>
<strong><%= t :label_line_items %></strong>
<table class="list line-items-table">
<thead>
<tr>
<th><%= t :label_description %></th>
<th style="width:120px;"><%= t :label_qty %></th>
<th style="width:150px;"><%= t :label_price %></th>
<th style="width:60%;"><%= t :label_description %></th>
<th style="width:10%;"><%= t :label_qty %></th>
<th style="width:10%;"><%= t :label_price %></th>
<th style="width:10%;"><%= t :label_total %></th>
<% unless readonly %>
<th style="width:80px;"></th>
<% end %>
@@ -29,6 +30,11 @@
</tbody>
</table>
<p class="line-items-grand-total">
<strong><%= t :label_total %>:</strong>
<span id="line-items-grand-total">0.00</span>
</p>
<% unless readonly %>
<template data-nested-form-template>
<%= f.fields_for :line_items, LineItem.new, child_index: "NEW_RECORD" do |item_form| %>

View File

@@ -6,10 +6,10 @@
<table class="list line-items-table">
<thead>
<tr>
<th><%= t :label_description %></th>
<th style="width:120px;"><%= t :label_qty %></th>
<th style="width:150px;"><%= t :label_price %></th>
<th style="width:150px;"><%= t :label_total %></th>
<th style="width:70%;"><%= t :label_description %></th>
<th style="width:10%;"><%= t :label_qty %></th>
<th style="width:10%;"><%= t :label_price %></th>
<th style="width:10%;"><%= t :label_total %></th>
</tr>
</thead>

View File

@@ -2,36 +2,39 @@
<%= f.hidden_field :id %>
<%= f.hidden_field :_destroy %>
<td>
<td data-label="<%= t :label_description %>">
<%= f.text_field :description,
size: 50,
placeholder: l(:label_description),
no_label: true,
disabled: readonly %>
</td>
<td>
<td data-label="<%= t :label_qty %>">
<%= f.number_field :quantity,
step: 1,
min: 0,
style: "width:90px;",
class: "qty-field",
no_label: true,
disabled: readonly %>
</td>
<td>
<td data-label="<%= t :label_price %>">
<%= f.number_field :unit_price,
step: 0.01,
style: "width:120px;",
class: "price-field",
no_label: true,
disabled: readonly %>
</td>
<td class="line-total" data-label="<%= t :label_total %>">
0.00
</td>
<% unless readonly %>
<td style="text-align:center;">
<td class="actions">
<button type="button"
class="icon-only icon-del"
title="<%=l(:label_remove)%>"
title="<%= l(:label_remove) %>"
data-nested-form-remove>
</button>
</td>

View File

@@ -0,0 +1,40 @@
function updateLineItemTotals() {
let grandTotal = 0;
document.querySelectorAll(".line-item").forEach(function(row){
let qty = parseFloat(row.querySelector(".qty-field")?.value || 0);
let price = parseFloat(row.querySelector(".price-field")?.value || 0);
let total = qty * price;
row.querySelector(".line-total").textContent =
total.toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2});
grandTotal += total;
});
let grand = document.getElementById("line-items-grand-total");
if(grand){
grand.textContent =
grandTotal.toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2});
}
}
document.addEventListener("input", function(e){
if(e.target.classList.contains("qty-field") ||
e.target.classList.contains("price-field")){
updateLineItemTotals();
}
});
document.addEventListener("DOMContentLoaded", function(){
updateLineItemTotals();
});

View File

@@ -0,0 +1,58 @@
.line-items-table {
width: 100%;
}
.line-items-table input {
width: 100%;
box-sizing: border-box;
}
.qty-field {
max-width: 90px;
}
.price-field {
max-width: 120px;
}
/* MOBILE MODE */
@media screen and (max-width: 700px) {
.line-items-table thead {
display: none;
}
.line-items-table,
.line-items-table tbody,
.line-items-table tr,
.line-items-table td {
display: block;
width: 100%;
}
.line-items-table tr {
border: 1px solid #ddd;
border-radius: 6px;
padding: 10px;
margin-bottom: 10px;
background: #fff;
}
.line-items-table td {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
}
.line-items-table td::before {
content: attr(data-label);
font-weight: bold;
margin-right: 10px;
}
.line-items-table td.actions {
justify-content: flex-end;
}
}

View File

@@ -16,7 +16,9 @@ module RedmineQboLineItems
# Load the javascript to support the autocomplete forms
def view_layouts_base_html_head(context = {})
safe_join([
javascript_include_tag( 'nested_form_controller.js', plugin: :redmine_qbo_lineitems)
javascript_include_tag( 'nested_form_controller.js', plugin: :redmine_qbo_lineitems),
javascript_include_tag("line_items", plugin: :redmine_qbo_lineitems),
stylesheet_link_tag("line_items", plugin: :redmine_qbo_lineitems)
])
end