mirror of
https://github.com/rickbarrette/redmine_qbo_lineitems.git
synced 2026-04-02 07:01:59 -04:00
tweaked layout, added JS for live totals
This commit is contained in:
@@ -6,14 +6,15 @@
|
|||||||
data-nested-form
|
data-nested-form
|
||||||
data-wrapper-selector=".line-item">
|
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">
|
<table class="list line-items-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><%= t :label_description %></th>
|
<th style="width:60%;"><%= t :label_description %></th>
|
||||||
<th style="width:120px;"><%= t :label_qty %></th>
|
<th style="width:10%;"><%= t :label_qty %></th>
|
||||||
<th style="width:150px;"><%= t :label_price %></th>
|
<th style="width:10%;"><%= t :label_price %></th>
|
||||||
|
<th style="width:10%;"><%= t :label_total %></th>
|
||||||
<% unless readonly %>
|
<% unless readonly %>
|
||||||
<th style="width:80px;"></th>
|
<th style="width:80px;"></th>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -29,6 +30,11 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<p class="line-items-grand-total">
|
||||||
|
<strong><%= t :label_total %>:</strong>
|
||||||
|
<span id="line-items-grand-total">0.00</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
<% unless readonly %>
|
<% unless readonly %>
|
||||||
<template data-nested-form-template>
|
<template data-nested-form-template>
|
||||||
<%= f.fields_for :line_items, LineItem.new, child_index: "NEW_RECORD" do |item_form| %>
|
<%= f.fields_for :line_items, LineItem.new, child_index: "NEW_RECORD" do |item_form| %>
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
<table class="list line-items-table">
|
<table class="list line-items-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><%= t :label_description %></th>
|
<th style="width:70%;"><%= t :label_description %></th>
|
||||||
<th style="width:120px;"><%= t :label_qty %></th>
|
<th style="width:10%;"><%= t :label_qty %></th>
|
||||||
<th style="width:150px;"><%= t :label_price %></th>
|
<th style="width:10%;"><%= t :label_price %></th>
|
||||||
<th style="width:150px;"><%= t :label_total %></th>
|
<th style="width:10%;"><%= t :label_total %></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
|
|||||||
@@ -2,36 +2,39 @@
|
|||||||
<%= f.hidden_field :id %>
|
<%= f.hidden_field :id %>
|
||||||
<%= f.hidden_field :_destroy %>
|
<%= f.hidden_field :_destroy %>
|
||||||
|
|
||||||
<td>
|
<td data-label="<%= t :label_description %>">
|
||||||
<%= f.text_field :description,
|
<%= f.text_field :description,
|
||||||
size: 50,
|
|
||||||
placeholder: l(:label_description),
|
placeholder: l(:label_description),
|
||||||
no_label: true,
|
no_label: true,
|
||||||
disabled: readonly %>
|
disabled: readonly %>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td data-label="<%= t :label_qty %>">
|
||||||
<%= f.number_field :quantity,
|
<%= f.number_field :quantity,
|
||||||
step: 1,
|
step: 1,
|
||||||
min: 0,
|
min: 0,
|
||||||
style: "width:90px;",
|
class: "qty-field",
|
||||||
no_label: true,
|
no_label: true,
|
||||||
disabled: readonly %>
|
disabled: readonly %>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td data-label="<%= t :label_price %>">
|
||||||
<%= f.number_field :unit_price,
|
<%= f.number_field :unit_price,
|
||||||
step: 0.01,
|
step: 0.01,
|
||||||
style: "width:120px;",
|
class: "price-field",
|
||||||
no_label: true,
|
no_label: true,
|
||||||
disabled: readonly %>
|
disabled: readonly %>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<td class="line-total" data-label="<%= t :label_total %>">
|
||||||
|
0.00
|
||||||
|
</td>
|
||||||
|
|
||||||
<% unless readonly %>
|
<% unless readonly %>
|
||||||
<td style="text-align:center;">
|
<td class="actions">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="icon-only icon-del"
|
class="icon-only icon-del"
|
||||||
title="<%=l(:label_remove)%>"
|
title="<%= l(:label_remove) %>"
|
||||||
data-nested-form-remove>
|
data-nested-form-remove>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
40
assets/javascripts/line_items.js
Normal file
40
assets/javascripts/line_items.js
Normal 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();
|
||||||
|
});
|
||||||
58
assets/stylesheets/line_items.css
Normal file
58
assets/stylesheets/line_items.css
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,7 +16,9 @@ module RedmineQboLineItems
|
|||||||
# Load the javascript to support the autocomplete forms
|
# Load the javascript to support the autocomplete forms
|
||||||
def view_layouts_base_html_head(context = {})
|
def view_layouts_base_html_head(context = {})
|
||||||
safe_join([
|
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
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user