mirror of
https://github.com/rickbarrette/redmine_qbo.git
synced 2026-04-02 08:21:57 -04:00
Got the UI working
This commit is contained in:
@@ -9,4 +9,4 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<%= render "line_items/issue_form" %>
|
<%= render "line_items/issue_form", f: f %>
|
||||||
@@ -1,31 +1,17 @@
|
|||||||
<%= form_with model: @issue do |f| %>
|
<% @issue.line_items.build if @issue.line_items.empty? %>
|
||||||
|
|
||||||
<!-- Existing issue fields -->
|
<div data-nested-form data-wrapper-selector=".line-item">
|
||||||
|
<div data-nested-form-container>
|
||||||
<h3>Invoice Line Items</h3>
|
<%= f.fields_for :line_items do |item_form| %>
|
||||||
|
<%= render "line_items/line_item_fields", f: item_form %>
|
||||||
<div
|
<% end %>
|
||||||
data-controller="nested-form"
|
|
||||||
data-nested-form-wrapper-selector-value=".line-item"
|
|
||||||
>
|
|
||||||
<div data-nested-form-target="container">
|
|
||||||
<%= f.fields_for :invoice_line_items do |item_form| %>
|
|
||||||
<%= render "line_items/invoice_line_item_fields", f: item_form %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template data-nested-form-target="template">
|
|
||||||
<%= f.fields_for :invoice_line_items,
|
|
||||||
LineItem.new,
|
|
||||||
child_index: "NEW_RECORD" do |item_form| %>
|
|
||||||
<%= render "line_items/invoice_line_item_fields", f: item_form %>
|
|
||||||
<% end %>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<button type="button" data-action="nested-form#add">
|
|
||||||
Add Line Item
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= f.submit %>
|
<template data-nested-form-template>
|
||||||
<% end %>
|
<%= f.fields_for :line_items, LineItem.new, child_index: "NEW_RECORD" do |item_form| %>
|
||||||
|
<%= render "line_items/line_item_fields", f: item_form %>
|
||||||
|
<% end %>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<button type="button" data-nested-form-add>Add Line Item</button>
|
||||||
|
</div>
|
||||||
@@ -2,12 +2,12 @@
|
|||||||
<%= f.hidden_field :id %>
|
<%= f.hidden_field :id %>
|
||||||
|
|
||||||
<%= f.text_field :description, placeholder: "Description" %>
|
<%= f.text_field :description, placeholder: "Description" %>
|
||||||
<%= f.number_field :quantity, step: 1 ,placeholder: "Quantity"%>
|
<%= f.number_field :quantity, step: 1, placeholder: "Quantity" %>
|
||||||
<%= f.number_field :unit_price, step: 0.01, placeholder: "Unit Price" %>
|
<%= f.number_field :unit_price, step: 0.01, placeholder: "Unit Price" %>
|
||||||
|
|
||||||
<%= f.hidden_field :_destroy %>
|
<%= f.hidden_field :_destroy %>
|
||||||
|
|
||||||
<button type="button" data-action="nested-form#remove">
|
<button type="button" data-nested-form-remove>
|
||||||
Remove
|
Remove
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
document.addEventListener("DOMContentLoaded", () => {
|
|
||||||
if (typeof Stimulus === "undefined") {
|
|
||||||
console.error("Stimulus is not loaded. Make sure the UMD script is included first.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const application = Stimulus.Application.start();
|
|
||||||
application.register("nested-form", window.NestedFormController);
|
|
||||||
});
|
|
||||||
@@ -1,19 +1,53 @@
|
|||||||
(function() {
|
(function () {
|
||||||
class NestedFormController extends Stimulus.Controller {
|
function initNestedForms() {
|
||||||
static targets = ["container", "template"]
|
document.querySelectorAll("[data-nested-form]").forEach(function (wrapper) {
|
||||||
|
if (wrapper.dataset.initialized === "true") return;
|
||||||
|
wrapper.dataset.initialized = "true";
|
||||||
|
|
||||||
add(event) {
|
const container = wrapper.querySelector("[data-nested-form-container]");
|
||||||
event.preventDefault();
|
const template = wrapper.querySelector("[data-nested-form-template]");
|
||||||
const content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime());
|
|
||||||
this.containerTarget.insertAdjacentHTML("beforeend", content);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(event) {
|
if (!container || !template) return;
|
||||||
event.preventDefault();
|
|
||||||
event.target.closest(".nested-fields").remove();
|
wrapper.addEventListener("click", function (event) {
|
||||||
}
|
const addButton = event.target.closest("[data-nested-form-add]");
|
||||||
|
const removeButton = event.target.closest("[data-nested-form-remove]");
|
||||||
|
|
||||||
|
// ADD
|
||||||
|
if (addButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const content = template.innerHTML.replace(
|
||||||
|
/NEW_RECORD/g,
|
||||||
|
Date.now().toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
container.insertAdjacentHTML("beforeend", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// REMOVE
|
||||||
|
if (removeButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const lineItem = removeButton.closest(wrapper.dataset.wrapperSelector);
|
||||||
|
if (!lineItem) return;
|
||||||
|
|
||||||
|
const destroyField = lineItem.querySelector("input[name*='_destroy']");
|
||||||
|
|
||||||
|
if (destroyField) {
|
||||||
|
destroyField.value = "1";
|
||||||
|
lineItem.style.display = "none";
|
||||||
|
} else {
|
||||||
|
lineItem.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose globally so index.js can access it
|
// Works for full load
|
||||||
window.NestedFormController = NestedFormController;
|
document.addEventListener("DOMContentLoaded", initNestedForms);
|
||||||
|
|
||||||
|
// Works for Turbo navigation
|
||||||
|
document.addEventListener("turbo:load", initNestedForms);
|
||||||
})();
|
})();
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -64,7 +64,8 @@ module RedmineQbo
|
|||||||
locals: {
|
locals: {
|
||||||
search_customer: search_customer,
|
search_customer: search_customer,
|
||||||
customer_id: customer_id,
|
customer_id: customer_id,
|
||||||
select_estimate: select_estimate
|
select_estimate: select_estimate,
|
||||||
|
f: context[:form]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,11 +16,9 @@ module RedmineQbo
|
|||||||
# 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([
|
||||||
'<script src="https://unpkg.com/@hotwired/stimulus/dist/stimulus.umd.js"></script>'.html_safe,
|
|
||||||
javascript_include_tag( 'application.js', plugin: :redmine_qbo),
|
javascript_include_tag( 'application.js', plugin: :redmine_qbo),
|
||||||
javascript_include_tag( 'autocomplete-rails.js', plugin: :redmine_qbo),
|
javascript_include_tag( 'autocomplete-rails.js', plugin: :redmine_qbo),
|
||||||
javascript_include_tag( 'checkbox_controller.js', plugin: :redmine_qbo),
|
javascript_include_tag( 'checkbox_controller.js', plugin: :redmine_qbo),
|
||||||
javascript_include_tag( 'index.js', plugin: :redmine_qbo),
|
|
||||||
javascript_include_tag( 'nested_form_controller.js', plugin: :redmine_qbo)
|
javascript_include_tag( 'nested_form_controller.js', plugin: :redmine_qbo)
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ module RedmineQbo
|
|||||||
belongs_to :customer_token, primary_key: :id
|
belongs_to :customer_token, primary_key: :id
|
||||||
belongs_to :estimate, primary_key: :id
|
belongs_to :estimate, primary_key: :id
|
||||||
has_and_belongs_to_many :invoices
|
has_and_belongs_to_many :invoices
|
||||||
|
has_many :line_items, dependent: :destroy
|
||||||
|
accepts_nested_attributes_for :line_items, allow_destroy: true
|
||||||
|
|
||||||
before_save :titlize_subject
|
before_save :titlize_subject
|
||||||
after_commit :enqueue_billing, on: :update
|
after_commit :enqueue_billing, on: :update
|
||||||
|
|||||||
Reference in New Issue
Block a user