← back to Open Seo

src/client/components/table/ExportToSheetsButton.tsx

58 lines

import { Sheet } from "lucide-react";
import { useState } from "react";
import type { CsvValue } from "@/client/lib/csv";
import { exportTableToSheets } from "@/client/lib/exportToSheets";

type Props = {
  headers: string[];
  /**
   * Full filtered/sorted dataset (not a UI-paginated slice). Emit raw numeric
   * values (not formatted strings) so Sheets parses them as numbers.
   */
  rows: CsvValue[][];
  /** PostHog `source_feature` for the `data:export_sheets` event. */
  feature: string;
  disabled?: boolean;
  label?: string;
  /** Render the icon without a text label (icon-only mode for tight rows). */
  iconOnly?: boolean;
  /** Extra classes appended to the default button classes. */
  className?: string;
};

export function ExportToSheetsButton({
  headers,
  rows,
  feature,
  disabled,
  label = "Export to Sheets",
  iconOnly,
  className,
}: Props) {
  const [busy, setBusy] = useState(false);

  const handleClick = async () => {
    if (busy) return;
    setBusy(true);
    try {
      await exportTableToSheets({ headers, rows, feature });
    } finally {
      setBusy(false);
    }
  };

  return (
    <button
      type="button"
      className={`btn btn-ghost btn-xs gap-1 ${className ?? ""}`}
      onClick={handleClick}
      disabled={disabled || rows.length === 0 || busy}
      title="Copy table and open a new Google Sheet"
      aria-label={iconOnly ? "Export to Sheets" : undefined}
    >
      <Sheet className="size-3.5" />
      {iconOnly ? null : label}
    </button>
  );
}