openEditPanel function
Last updated: 2026-03-04T23:21:38.431Z
Location
Metrics
LOC: 201
Complexity: 30
Params: 1
Signature
openEditPanel(form: SavedForm): : void
Architecture violations
- [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)
| Target | Type |
|---|---|
| legacyFieldsToTemplate | calls |
| buildTemplateFieldRow | calls |
| upgradeRowSearchableSelects | calls |
| fieldTypeLabel | calls |
| loadSavedForms | calls |
| change | dynamic_call |
| click | dynamic_call |
Impact (Incoming)
| Source | Type |
|---|---|
| loadSavedForms | calls |