src/lib/ui/components/fields-tab-view.tsx

Total Symbols
6
Lines of Code
270
Avg Complexity
1.0
Symbol Types
2

Symbols by Kind

interface 3
method 3

All Symbols

Name Kind Visibility Status Lines Signature
FieldsTabCallbacks interface exported- 17-31 interface FieldsTabCallbacks
FieldsTabViewProps interface exported- 33-39 interface FieldsTabViewProps
FieldRowProps interface - 43-51 interface FieldRowProps
onFill method - 84-84 onFill(field.selector)
ignored method - 104-105 ignored("actionReactivate"): : t("actionIgnore")} onClick
has method - 232-232 has(field.selector)

Full Source

/**
 * FieldsTabView — Preact component for the DevTools Fields tab.
 *
 * Supports both the initial loading state (during streaming detection)
 * and the static idle state (after detection is complete).
 */

import { h } from "preact";
import { t } from "@/lib/i18n";
import type { DetectedFieldSummary } from "@/types";
import { TypeBadge, MethodBadge, ConfidenceBadge } from "./badges";
import type { FieldEditorSavePayload } from "./field-editor-modal";
import { FieldEditorModal } from "./field-editor-modal";

// ── Types ─────────────────────────────────────────────────────────────────────

export interface FieldsTabCallbacks {
  onDetect: () => void;
  onFillAll: () => void;
  onFillEmpty: () => void;
  onClearDetected: () => void;
  onClearForm: () => void;
  onFillField: (selector: string) => void;
  onInspectField: (selector: string) => void;
  onToggleIgnore: (selector: string, label: string) => void;
  onEditField: (field: DetectedFieldSummary) => void;
  onSaveFieldRule: (payload: FieldEditorSavePayload) => void;
  onDeleteFieldRule: () => void;
  onCloseEditor: () => void;
  onRedetectField?: (selector: string) => Promise<void>;
}

export interface FieldsTabViewProps extends FieldsTabCallbacks {
  fields: DetectedFieldSummary[];
  ignoredSelectors: Set<string>;
  detecting: boolean;
  editingField: DetectedFieldSummary | null;
  editingFieldExistingRule: FieldEditorSavePayload | null;
}

// ── FieldRow ──────────────────────────────────────────────────────────────────

interface FieldRowProps {
  field: DetectedFieldSummary;
  index: number;
  ignored: boolean;
  onFill: (selector: string) => void;
  onInspect: (selector: string) => void;
  onToggleIgnore: (selector: string, label: string) => void;
  onEdit: (field: DetectedFieldSummary) => void;
}

function FieldRow({
  field,
  index,
  ignored,
  onFill,
  onInspect,
  onToggleIgnore,
  onEdit,
}: FieldRowProps) {
  const displayType = field.contextualType || field.fieldType;
  const method = field.detectionMethod || "-";
  const label = field.label || field.name || field.id || field.selector;

  return (
    <tr class={ignored ? "row-ignored" : ""}>
      <td class="cell-num">{index}</td>
      <td>
        <TypeBadge type={displayType} />
      </td>
      <td>
        <MethodBadge method={method} />
      </td>
      <td>
        <ConfidenceBadge confidence={field.detectionConfidence} />
      </td>
      <td class="cell-mono">{field.id || field.name || "-"}</td>
      <td>{field.label || "-"}</td>
      <td class="cell-actions">
        <button
          class="icon-btn"
          title={t("actionFill")}
          onClick={() => onFill(field.selector)}
        >
          ⚡
        </button>
        <button
          class="icon-btn"
          title={t("actionInspect")}
          onClick={() => onInspect(field.selector)}
        >
          🔎
        </button>
        <button
          class="icon-btn"
          title={t("actionEditField")}
          onClick={() => onEdit(field)}
        >
          ✏️
        </button>
        <button
          class={`icon-btn${ignored ? " icon-btn-off" : ""}`}
          title={ignored ? t("actionReactivate") : t("actionIgnore")}
          onClick={() => onToggleIgnore(field.selector, label)}
        >
          {ignored ? "🚫" : "👁️"}
        </button>
      </td>
    </tr>
  );
}

// ── FieldsTabView ─────────────────────────────────────────────────────────────

export function FieldsTabView({
  fields,
  ignoredSelectors,
  detecting,
  onDetect,
  onFillAll,
  onFillEmpty,
  onClearDetected,
  onClearForm,
  onFillField,
  onInspectField,
  onToggleIgnore,
  onEditField,
  editingField,
  editingFieldExistingRule,
  onSaveFieldRule,
  onDeleteFieldRule,
  onCloseEditor,
  onRedetectField,
}: FieldsTabViewProps) {
  const hasFields = fields.length > 0;

  return (
    <div>
      <div class="fields-toolbar">
        <button
          class="btn"
          id="btn-detect-fields"
          disabled={detecting}
          onClick={onDetect}
        >
          {detecting
            ? `🔄 ${t("detectFields")} ...`
            : `🔍 ${t("detectFields")}`}
        </button>
        <button
          class="btn"
          id="btn-fill-all-fields"
          disabled={detecting && !hasFields}
          onClick={onFillAll}
        >
          ⚡ {t("fillAll")}
        </button>
        <button
          class="btn"
          id="btn-fill-empty-fields"
          disabled={detecting && !hasFields}
          onClick={onFillEmpty}
        >
          🟦 {t("fillOnlyEmpty")}
        </button>
        <button
          class="btn btn-danger"
          id="btn-clear-fields"
          disabled={!hasFields}
          onClick={onClearDetected}
        >
          🗑️ Limpar Detectados
        </button>
        <button
          class="btn btn-danger"
          id="btn-clear-form"
          onClick={onClearForm}
        >
          🧹 Limpar Form
        </button>
        <span class="fields-count">
          {fields.length} {t("fieldCount")}
        </span>
      </div>
      <div class="table-wrap">
        {detecting && !hasFields ? (
          <table class="fields-table">
            <thead>
              <tr>
                <th>#</th>
                <th>{t("columnType")}</th>
                <th>{t("columnMethod")}</th>
                <th>{t("columnConf")}</th>
                <th>{t("columnIdName")}</th>
                <th>{t("columnLabel")}</th>
                <th>{t("columnActions")}</th>
              </tr>
            </thead>
            <tbody>
              <tr class="row-loading">
                <td
                  colspan={7}
                  style={{ textAlign: "center", padding: "20px" }}
                >
                  ⏳ Detectando campos...
                </td>
              </tr>
            </tbody>
          </table>
        ) : !hasFields ? (
          <div class="empty">{t("clickToDetect")}</div>
        ) : (
          <table class="fields-table">
            <thead>
              <tr>
                <th>#</th>
                <th>{t("columnType")}</th>
                <th>{t("columnMethod")}</th>
                <th>{t("columnConf")}</th>
                <th>{t("columnIdName")}</th>
                <th>{t("columnLabel")}</th>
                <th>{t("columnActions")}</th>
              </tr>
            </thead>
            <tbody>
              {fields.map((field, i) => (
                <FieldRow
                  key={field.selector}
                  field={field}
                  index={i + 1}
                  ignored={ignoredSelectors.has(field.selector)}
                  onFill={onFillField}
                  onInspect={onInspectField}
                  onToggleIgnore={onToggleIgnore}
                  onEdit={onEditField}
                />
              ))}
              {detecting && (
                <tr class="row-loading">
                  <td
                    colspan={7}
                    style={{ textAlign: "center", padding: "10px" }}
                  >
                    ⏳ {t("detectFields")}...
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        )}
      </div>
      {editingField && (
        <FieldEditorModal
          field={editingField}
          existingRule={editingFieldExistingRule}
          onSave={onSaveFieldRule}
          onDelete={onDeleteFieldRule}
          onClose={onCloseEditor}
          onRedetect={
            onRedetectField
              ? () => onRedetectField(editingField.selector)
              : undefined
          }
        />
      )}
    </div>
  );
}