folder rename

This commit is contained in:
gsinghpal
2026-04-16 20:53:53 -04:00
parent 3f3ddcbab4
commit 7c7ef06057
634 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,320 @@
/** @odoo-module **/
// Copyright 2026 Nexa Systems Inc.
// License OPL-1 (Odoo Proprietary License v1.0)
// Part of the Fusion Plating product family.
import { Interaction } from "@web/public/interaction";
import { registry } from "@web/core/registry";
/**
* Multi-part RFQ form interaction.
*
* Manages dynamic part rows in the quote request form: add/remove parts,
* drag-drop file uploads per part, billing address toggle, and serialises
* the parts data to a hidden JSON field before form submission.
*/
function _el(tag, attrs, children) {
const el = document.createElement(tag);
if (attrs) {
for (const [k, v] of Object.entries(attrs)) {
if (k === "className") {
el.className = v;
} else if (k === "textContent") {
el.textContent = v;
} else {
el.setAttribute(k, v);
}
}
}
if (children) {
for (const child of children) {
if (typeof child === "string") {
el.appendChild(document.createTextNode(child));
} else if (child) {
el.appendChild(child);
}
}
}
return el;
}
function _icon(cls) {
const i = document.createElement("i");
i.className = cls;
return i;
}
class FpRfqFormInteraction extends Interaction {
static selector = "#fp_rfq_form";
setup() {
this.partIndex = 0;
// Add first part row automatically
this._addPartRow();
// Event listeners
this.addListener("#fp_add_part_btn", "click", this._onAddPart);
this.addListener("#billing_same_as_shipping", "change", this._onBillingSameToggle);
this.addListener("#fp_rfq_form", "submit", this._onSubmit);
this.addListener("#fp_parts_container", "click", this._onContainerClick);
}
_onAddPart() {
this._addPartRow();
}
_onContainerClick(ev) {
const removeBtn = ev.target.closest(".o_fp_remove_part");
if (removeBtn) {
const row = removeBtn.closest(".o_fp_part_row");
if (row) {
row.remove();
this._renumberParts();
}
}
}
_onBillingSameToggle(ev) {
const billingSelect = this.el.querySelector("#billing_address_id");
if (billingSelect) {
billingSelect.disabled = ev.target.checked;
if (ev.target.checked) {
billingSelect.value = "";
}
}
}
_addPartRow() {
const container = this.el.querySelector("#fp_parts_container");
if (!container) return;
const idx = this.partIndex++;
const rowCount = container.querySelectorAll(".o_fp_part_row").length + 1;
// Build the part row using safe DOM methods
const row = _el("div", { className: "o_fp_part_row", "data-part-idx": String(idx) });
// Header
const header = _el("div", { className: "o_fp_part_row_header" }, [
_el("span", { className: "o_fp_part_num", textContent: "Part #" + rowCount }),
_el("span", { className: "o_fp_remove_part", title: "Remove this part" }, [
_icon("fa fa-times"),
]),
]);
row.appendChild(header);
// Row 1: Part Number, Qty, Count, Product
const r1 = _el("div", { className: "row" });
// Part Number
const c1 = _el("div", { className: "col-md-4 mb-2" }, [
_el("label", { className: "form-label small", textContent: "Part Number" }),
_el("input", {
type: "text",
className: "form-control form-control-sm fp_part_number",
placeholder: "e.g. PN-12345",
}),
]);
r1.appendChild(c1);
// Quantity
const c2 = _el("div", { className: "col-md-2 mb-2" }, [
_el("label", { className: "form-label small", textContent: "Quantity" }),
_el("input", {
type: "number",
className: "form-control form-control-sm fp_part_qty",
value: "1",
min: "1",
}),
]);
r1.appendChild(c2);
// Count
const c3 = _el("div", { className: "col-md-2 mb-2" }, [
_el("label", { className: "form-label small", textContent: "Count" }),
_el("input", {
type: "number",
className: "form-control form-control-sm fp_part_count",
value: "1",
min: "1",
}),
]);
r1.appendChild(c3);
// Product select
const prodSelect = _el("select", { className: "form-select form-select-sm fp_part_product" }, [
_el("option", { value: "", textContent: "-- Select --" }),
]);
const c4 = _el("div", { className: "col-md-4 mb-2" }, [
_el("label", { className: "form-label small", textContent: "Product" }),
prodSelect,
]);
r1.appendChild(c4);
row.appendChild(r1);
// Row 2: Description
const r2 = _el("div", { className: "row" }, [
_el("div", { className: "col-md-12 mb-2" }, [
_el("label", { className: "form-label small", textContent: "Description" }),
_el("textarea", {
className: "form-control form-control-sm fp_part_desc",
rows: "2",
placeholder: "Describe this part...",
}),
]),
]);
row.appendChild(r2);
// Row 3: Spec Parameters
const r3 = _el("div", { className: "row" }, [
_el("div", { className: "col-md-12 mb-2" }, [
_el("label", { className: "form-label small", textContent: "Spec Parameters" }),
_el("textarea", {
className: "form-control form-control-sm fp_part_spec",
rows: "2",
placeholder: "Spec details for this part...",
}),
]),
]);
row.appendChild(r3);
// Row 4: File upload
const fileInputName = "line_file_" + container.querySelectorAll(".o_fp_part_row").length;
const fileInput = _el("input", {
type: "file",
name: fileInputName,
multiple: "multiple",
className: "d-none fp_line_file_input",
});
const dropZone = _el("div", {
className: "o_fp_file_drop_zone",
"data-idx": String(idx),
}, [
_icon("fa fa-cloud-upload"),
document.createTextNode(" "),
_el("span", { textContent: "Drag files here or click to upload" }),
fileInput,
]);
const fileListEl = _el("div", { className: "fp_file_list small text-muted mt-1" });
const r4 = _el("div", { className: "row" }, [
_el("div", { className: "col-md-12" }, [
_el("label", { className: "form-label small", textContent: "Files" }),
dropZone,
fileListEl,
]),
]);
row.appendChild(r4);
container.appendChild(row);
// Populate product dropdown
this._populateProductDropdown(prodSelect);
// Set up drag-drop zone
this._setupDropZone(dropZone, fileInput, fileListEl);
}
_populateProductDropdown(selectEl) {
// Clone options from the hidden source select rendered by QWeb
const sourceSelect = document.getElementById("fp_products_source");
if (sourceSelect) {
for (const opt of sourceSelect.options) {
const clone = opt.cloneNode(true);
selectEl.appendChild(clone);
}
}
}
_setupDropZone(zone, fileInput, fileListEl) {
zone.addEventListener("click", (ev) => {
if (ev.target !== fileInput) {
fileInput.click();
}
});
zone.addEventListener("dragover", (ev) => {
ev.preventDefault();
zone.classList.add("o_fp_drag_over");
});
zone.addEventListener("dragleave", () => {
zone.classList.remove("o_fp_drag_over");
});
zone.addEventListener("drop", (ev) => {
ev.preventDefault();
zone.classList.remove("o_fp_drag_over");
if (ev.dataTransfer.files.length) {
fileInput.files = ev.dataTransfer.files;
this._updateFileList(fileInput, fileListEl);
}
});
fileInput.addEventListener("change", () => {
this._updateFileList(fileInput, fileListEl);
});
}
_updateFileList(fileInput, fileListEl) {
if (!fileListEl) return;
const names = [];
for (const f of fileInput.files) {
names.push(f.name);
}
fileListEl.textContent = names.length ? names.join(", ") : "";
}
_renumberParts() {
const container = this.el.querySelector("#fp_parts_container");
if (!container) return;
const rows = container.querySelectorAll(".o_fp_part_row");
rows.forEach((row, i) => {
const numEl = row.querySelector(".o_fp_part_num");
if (numEl) numEl.textContent = "Part #" + (i + 1);
const fileInput = row.querySelector(".fp_line_file_input");
if (fileInput) fileInput.name = "line_file_" + i;
});
}
_onSubmit() {
const container = this.el.querySelector("#fp_parts_container");
const hiddenField = this.el.querySelector("#fp_parts_data");
if (!container || !hiddenField) return;
const rows = container.querySelectorAll(".o_fp_part_row");
const parts = [];
rows.forEach((row) => {
const partNumber = (row.querySelector(".fp_part_number") || {}).value || "";
const quantity = (row.querySelector(".fp_part_qty") || {}).value || "1";
const count = (row.querySelector(".fp_part_count") || {}).value || "1";
const description = (row.querySelector(".fp_part_desc") || {}).value || "";
const specText = (row.querySelector(".fp_part_spec") || {}).value || "";
const productId = (row.querySelector(".fp_part_product") || {}).value || "";
if (partNumber || description || productId) {
parts.push({
part_number: partNumber,
quantity: quantity,
count: count,
description: description,
spec_text: specText,
product_id: productId,
});
}
});
hiddenField.value = JSON.stringify(parts);
}
}
registry
.category("public.interactions")
.add("fusion_plating_portal.rfq_form", FpRfqFormInteraction);