openEditPanel function

Last updated: 2026-03-04T23:21:38.431Z

Metrics

LOC: 201 Complexity: 30 Params: 1

Signature

openEditPanel(form: SavedForm): : void

Architecture violations

View all

  • [warning] max-cyclomatic-complexity: 'openEditPanel' has cyclomatic complexity 30 (max 10)
  • [warning] max-lines: 'openEditPanel' has 201 lines (max 80)

Source Code

function openEditPanel(form: SavedForm): void {
  let panel = document.getElementById("form-edit-panel");
  if (!panel) {
    panel = document.createElement("div");
    panel.id = "form-edit-panel";
    panel.className = "edit-panel";
    document.getElementById("saved-forms-list")?.after(panel);
  }

  const templateFields =
    form.templateFields && form.templateFields.length > 0
      ? form.templateFields
      : legacyFieldsToTemplate(form.fields);

  // Determina se este template usa match por tipo (type-based) ou por seletor
  const isTypeBased = templateFields.some((f) => f.matchByFieldType);

  panel.innerHTML = `
    <h3>${t("editTemplateFor")} ${escapeHtml(form.name)}</h3>
    <div class="form-group">
      <label>${t("nameLabel")}</label>
      <input type="text" id="edit-form-name" value="${escapeHtml(form.name)}" />
    </div>
    <div class="form-group">
      <label>${t("urlPatternLabel")}</label>
      <input type="text" id="edit-form-url" value="${escapeHtml(form.urlPattern)}" />
      <div class="description" style="margin-top:4px;font-size:11px;color:var(--text-muted);">
        ${t("editUrlPatternDesc")}
      </div>
    </div>
    <div style="margin-bottom:8px;">
      <strong style="font-size:13px;">${t("fieldsTitle")}</strong>
    </div>
    <table class="template-fields-table">
      <thead>
        <tr>
          <th>${isTypeBased ? t("fieldDetectedTypeHeader") : t("fieldColumnHeader")}</th>
          <th>${t("fieldModeHeader")}</th>
          <th>${t("fieldValueHeader")}</th>
          <th></th>
        </tr>
      </thead>
      <tbody id="edit-fields-tbody">
        ${templateFields
          .map((field) =>
            isTypeBased
              ? buildTemplateFieldRow(field)
              : `
          <tr data-key="${escapeHtml(field.key)}" class="template-field-row template-field-row--legacy">
            <td class="field-label-cell">${escapeHtml(field.label || field.key)}</td>
            <td>
              <select class="field-mode-select">
                <option value="fixed"${field.mode === "fixed" ? " selected" : ""}>${t("modeFixed")}</option>
                <option value="generator"${field.mode === "generator" ? " selected" : ""}>${t("modeGenerator")}</option>
              </select>
            </td>
            <td>
              <input
                type="text"
                class="field-fixed-value"
                value="${escapeHtml(field.fixedValue ?? "")}"
                style="display:${field.mode === "fixed" ? "inline-block" : "none"}"
              />
              <div
                class="field-generator-container"
                data-generator-type="${escapeHtml(field.generatorType ?? "name")}"
                style="display:${field.mode === "generator" ? "inline-block" : "none"}"
              ></div>
            </td>
            <td>
              <button class="btn btn-sm btn-delete btn-remove-row" title="${t("removeFieldTitle")}">✕</button>
            </td>
          </tr>
        `,
          )
          .join("")}
      </tbody>
    </table>
    <div style="margin-bottom:12px;">
      <button class="btn btn-secondary btn-sm" id="edit-add-field-row">${t("btnAddField")}</button>
    </div>
    <div class="edit-panel-actions">
      <button class="btn btn-primary" id="edit-panel-save">${t("btnSave")}</button>
      <button class="btn btn-secondary" id="edit-panel-cancel">${t("btnCancel")}</button>
    </div>
  `;

  // Upgrade all type-based rows to SearchableSelect
  panel.querySelectorAll("tr.template-field-row").forEach((row) => {
    upgradeRowSearchableSelects(row);
  });

  // Toggle fixed/generator visibility on change
  panel.addEventListener("change", (e) => {
    const target = e.target as HTMLSelectElement;
    if (!target.classList.contains("field-mode-select")) return;
    const row = target.closest("tr")!;
    const isFixed = target.value === "fixed";
    (row.querySelector(".field-fixed-value") as HTMLElement).style.display =
      isFixed ? "inline-block" : "none";
    (
      row.querySelector(".field-generator-container") as HTMLElement
    ).style.display = isFixed ? "none" : "inline-block";
  });

  // Remove row (type-based only)
  panel.addEventListener("click", (e) => {
    const target = e.target as HTMLElement;
    if (!target.classList.contains("btn-remove-row")) return;
    target.closest("tr")?.remove();
  });

  // Add field row (always type-based for new rows)
  panel.querySelector("#edit-add-field-row")?.addEventListener("click", () => {
    const tbody = panel!.querySelector("#edit-fields-tbody");
    if (!tbody) return;
    const wrapper = document.createElement("tbody");
    wrapper.innerHTML = buildTemplateFieldRow();
    const newRow = wrapper.querySelector("tr");
    if (newRow) {
      tbody.appendChild(newRow);
      upgradeRowSearchableSelects(newRow);
    }
  });

  panel.querySelector("#edit-panel-cancel")?.addEventListener("click", () => {
    panel!.remove();
  });

  panel
    .querySelector("#edit-panel-save")
    ?.addEventListener("click", async () => {
      const nameInput = panel!.querySelector(
        "#edit-form-name",
      ) as HTMLInputElement;
      const urlInput = panel!.querySelector(
        "#edit-form-url",
      ) as HTMLInputElement;

      const updatedFields: FormTemplateField[] = [];
      panel!.querySelectorAll("tr.template-field-row").forEach((row) => {
        const mode = (
          row.querySelector(".field-mode-select") as HTMLSelectElement
        ).value as "fixed" | "generator";
        const fixedValue = (
          row.querySelector(".field-fixed-value") as HTMLInputElement
        ).value;
        const generatorType =
          ((
            row.querySelector(
              ".field-generator-container .fa-ss__value",
            ) as HTMLInputElement
          )?.value as FieldType) ?? ("name" as FieldType);

        const typeMatchInput = row.querySelector(
          ".field-type-match-container .fa-ss__value",
        ) as HTMLInputElement | null;

        if (typeMatchInput) {
          // Type-based row (template row with matchByFieldType)
          const matchType = typeMatchInput.value as FieldType;
          updatedFields.push({
            key: matchType,
            label: fieldTypeLabel(matchType),
            mode,
            matchByFieldType: matchType,
            fixedValue: mode === "fixed" ? fixedValue : undefined,
            generatorType: mode === "generator" ? generatorType : undefined,
          });
        } else {
          // Legacy row (captured from DOM, key is CSS selector/name)
          const key = (row as HTMLElement).dataset.key ?? "";
          const label =
            row.querySelector(".field-label-cell")?.textContent?.trim() ?? key;
          updatedFields.push({
            key,
            label,
            mode,
            fixedValue: mode === "fixed" ? fixedValue : undefined,
            generatorType: mode === "generator" ? generatorType : undefined,
          });
        }
      });

      const updatedForm: SavedForm = {
        ...form,
        name: nameInput.value || form.name,
        urlPattern: urlInput.value || form.urlPattern,
        templateFields: updatedFields,
        updatedAt: Date.now(),
      };

      await chrome.runtime.sendMessage({
        type: "UPDATE_FORM",
        payload: updatedForm,
      });
      panel!.remove();
      await loadSavedForms();
      showToast(t("toastTemplateUpdated"));
    });
}

Dependencies (Outgoing)

graph LR openEditPanel["openEditPanel"] legacyFieldsToTemplate["legacyFieldsToTemplate"] buildTemplateFieldRow["buildTemplateFieldRow"] upgradeRowSearchableSelects["upgradeRowSearchableSelects"] fieldTypeLabel["fieldTypeLabel"] loadSavedForms["loadSavedForms"] openEditPanel -->|calls| legacyFieldsToTemplate openEditPanel -->|calls| buildTemplateFieldRow openEditPanel -->|calls| upgradeRowSearchableSelects openEditPanel -->|calls| fieldTypeLabel openEditPanel -->|calls| loadSavedForms style openEditPanel fill:#dbeafe,stroke:#2563eb,stroke-width:2px click openEditPanel "452da4897e90f5ca.html" click legacyFieldsToTemplate "0a335e33c2aaef7e.html" click buildTemplateFieldRow "6e5eee1aa17b57cc.html" click upgradeRowSearchableSelects "90e9f01206f5f2c7.html" click fieldTypeLabel "861a0ec11bc0b27b.html" click loadSavedForms "cf1ee6d5c3ab3b9b.html"
TargetType
legacyFieldsToTemplate calls
buildTemplateFieldRow calls
upgradeRowSearchableSelects calls
fieldTypeLabel calls
loadSavedForms calls
change dynamic_call
click dynamic_call

Impact (Incoming)

graph LR openEditPanel["openEditPanel"] loadSavedForms["loadSavedForms"] loadSavedForms -->|calls| openEditPanel style openEditPanel fill:#dbeafe,stroke:#2563eb,stroke-width:2px click openEditPanel "452da4897e90f5ca.html" click loadSavedForms "cf1ee6d5c3ab3b9b.html"
SourceType
loadSavedForms calls