function createInternalSrForm({ MODAL_ID, searchableSelect, formatDate, getOpportunity, requestPlatform, requestServices, requestSave, notify, environment }) { const { document } = environment; let active = null; function node(tag, className, text) { const el = document.createElement(tag); if (className) el.className = className; if (text !== undefined) el.textContent = text; return el; } function close(restoreFocus = true) { if (!active) return false; if (active.saving) return true; const previous = active; active = null; previous.modal.remove(); if (restoreFocus && previous.trigger?.isConnected) previous.trigger.focus(); return true; } function open(parent, trigger) { if (active?.saving) return; const host = document.getElementById(MODAL_ID); if (!host || host.hidden) return; close(false); const modal = node("section", "opportunities-extension-action-form-modal"); modal.setAttribute("role", "dialog"); modal.setAttribute("aria-modal", "true"); modal.setAttribute("aria-labelledby", "internal-sr-form-title"); const dialog = node("div", "opportunities-extension-action-form-dialog opportunities-extension-internal-sr-dialog"); const header = node("header", "opportunities-extension-account-plans-header"); const heading = node("div", "opportunities-extension-action-form-heading"); const title = node("h2", "", "Create Internal Service Request"); title.id = "internal-sr-form-title"; const context = node("dl", "opportunities-extension-action-form-context"); const opportunity = getOpportunity(parent.OpportunityName_Id_c?.OptyNumber); for (const [label, value] of [["SR Number", parent.SrNumber], ["Opportunity", opportunity?.Name], ["Title", parent.Title]]) { context.append(node("dt", "", label), node("dd", "", value == null || value === "" ? "N/A" : String(value))); } heading.append(title, context); header.append(heading); const form = node("form", "opportunities-extension-action-form"); form.noValidate = true; const fields = new Map(); const combos = new Map(); const ready = new Set(); function field(name, label, type, wide = false) { const wrapper = node("label", "opportunities-extension-action-form-field" + (wide ? " opportunities-extension-action-form-wide" : "")); const input = node(type === "textarea" || type === "select" ? type : "input"); if (!["textarea", "select"].includes(type)) input.type = type; if (type === "textarea") input.rows = 4; input.id = `internal-sr-${name}`; input.name = name; fields.set(name, input); input.required = name !== "customerDeadline"; input.setAttribute("aria-required", String(input.required)); wrapper.htmlFor = input.id; wrapper.append(node("span", "", input.required ? `${label} (required)` : label), input); form.append(wrapper); return { input, wrapper }; } const titleInput = field("title", "Title", "text", true).input; const deadline = field("customerDeadline", "Customer Deadline", "text").input; deadline.readOnly = true; deadline.value = formatDate(parent.CustomerDeadline_c) || "N/A"; field("internalDeadline", "Internal Deadline", "date"); const platform = field("platform", "Platform", "select", true); field("description", "Description", "textarea", true); const services = field("services", "Services", "select", true); const footer = node("footer", "opportunities-extension-action-form-footer"); const cancel = node("button", "", "Cancel"); const save = node("button", "opportunities-extension-action-form-save", "Save"); save.type = "submit"; save.disabled = true; const feedback = node("p", "opportunities-extension-action-form-status opportunities-extension-action-form-wide"); feedback.setAttribute("role", "status"); feedback.hidden = true; cancel.type = "button"; cancel.addEventListener("click", () => close()); footer.append(save, cancel); form.append(feedback, footer); dialog.append(header, form); modal.append(dialog); host.append(modal); const entry = { modal, trigger, saving: false }; active = entry; const validDate = value => /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(`${value}T00:00:00Z`)) && new Date(`${value}T00:00:00Z`).toISOString().slice(0, 10) === value; form.addEventListener("submit", async event => { event.preventDefault(); if (active !== entry || entry.saving || ready.size !== 2) return; let firstInvalid = null; for (const [name, input] of fields) { if (!input.required) continue; const value = String(input.value || "").trim(); const valid = !!value && (name !== "internalDeadline" || validDate(value)) && (!combos.has(name) || Array.from(input.children).some(option => option.value === value)); input.setAttribute("aria-invalid", String(!valid)); const visible = combos.get(name)?.input || input; visible.setAttribute("aria-invalid", String(!valid)); if (!valid && !firstInvalid) firstInvalid = visible; } feedback.hidden = false; if (firstInvalid) { feedback.setAttribute("role", "alert"); feedback.textContent = "Complete all required fields with valid values."; firstInvalid.focus(); return; } const value = name => String(fields.get(name).value || "").trim(); const dateParts = value("customerDeadline").match(/^(\d{2})\/(\d{2})\/(\d{4})$/); const payload = { Title: value("title"), ServiceNew_Id_c: value("services"), ProblemDescription: value("description"), CustomerDeadline_c: dateParts ? `${dateParts[3]}-${dateParts[2]}-${dateParts[1]}` : "", TechPlatformLookUp_c: value("platform"), ServiceRequest_Id_SRToInternalSR: String(parent.SrId ?? ""), SvcQuestionsCollection_c: [], InternalDeadline_c: value("internalDeadline") }; entry.saving = true; form.setAttribute("aria-busy", "true"); save.disabled = cancel.disabled = true; save.textContent = "Saving..."; for (const [name, input] of fields) { input.disabled = true; combos.get(name)?.sync(); } feedback.setAttribute("role", "status"); feedback.textContent = "Creating Internal Service Request..."; try { await requestSave(payload); entry.saving = false; close(); notify("Internal Service Request created successfully."); } catch (error) { if (active !== entry) return; feedback.setAttribute("role", "alert"); feedback.textContent = error.message || "Unable to create Internal Service Request."; notify(feedback.textContent, true); } finally { entry.saving = false; if (active === entry) { form.setAttribute("aria-busy", "false"); save.disabled = cancel.disabled = false; save.textContent = "Save"; for (const [name, input] of fields) { input.disabled = false; combos.get(name)?.sync(); } } } }); function load(target, request, service) { const { input, wrapper } = target; input.disabled = true; const combo = searchableSelect(input, wrapper); combos.set(input.name, combo); const status = node("p", "opportunities-extension-action-form-status", "Loading..."); status.setAttribute("role", "status"); wrapper.append(status); wrapper.setAttribute("aria-busy", "true"); Promise.resolve().then(request).then(items => { if (active !== entry) return; const placeholder = node("option", "", items.length ? "Select an option" : "No options available"); placeholder.value = ""; input.append(placeholder); const seen = new Set(); for (const item of items) { const value = String(service ? item.Id : item.code); if (seen.has(value)) continue; seen.add(value); const option = node("option", "", service ? item.RecordName : item.name); option.value = value; if (service && item.Description_c) option.setAttribute("data-description", item.Description_c); input.append(option); } input.value = ""; input.disabled = !items.length; if (items.length) ready.add(input.name); save.disabled = ready.size !== 2; status.textContent = items.length ? "" : "No options available."; status.hidden = !!items.length; combo.sync(); }).catch(error => { if (active !== entry) return; status.setAttribute("role", "alert"); status.textContent = error.message || "Unable to load options. Close and reopen the form to retry."; }).finally(() => { if (active === entry) wrapper.setAttribute("aria-busy", "false"); }); } load(platform, requestPlatform, false); load(services, () => requestServices(parent.SrId), true); modal.addEventListener("keydown", event => { if (event.key === "Escape") { event.preventDefault(); event.stopPropagation(); close(); } if (event.key === "Tab") { const focusable = Array.from(form.querySelectorAll("input, textarea, button")).filter(el => !el.disabled && !el.hidden); const first = focusable[0], last = focusable.at(-1); if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); } } }); titleInput.focus(); } return { open, close }; }