128 lines
3.6 KiB
TypeScript
128 lines
3.6 KiB
TypeScript
import { CalendarDays, Loader2, SlidersHorizontal, Table } from "lucide-react";
|
|
import { SegmentedToggle } from "@/client/components/SegmentedToggle";
|
|
import { ExportMenu, MoreMenu } from "./ToolbarMenus";
|
|
|
|
export function RankTrackingTableToolbar({
|
|
showFilters,
|
|
onToggleFilters,
|
|
activeFilterCount,
|
|
isRunning,
|
|
latestRun,
|
|
keywordCount,
|
|
viewMode,
|
|
onViewModeChange,
|
|
historyAvailable,
|
|
onExport,
|
|
onExportToSheets,
|
|
onCopyKeywords,
|
|
onCheckNow,
|
|
onRefreshMetrics,
|
|
metricsRefreshing,
|
|
checkBusy,
|
|
checkDisabled,
|
|
hasData,
|
|
}: {
|
|
showFilters: boolean;
|
|
onToggleFilters: () => void;
|
|
activeFilterCount: number;
|
|
isRunning: boolean;
|
|
latestRun:
|
|
| { status: string; keywordsChecked: number; keywordsTotal: number }
|
|
| null
|
|
| undefined;
|
|
keywordCount: number;
|
|
viewMode: "table" | "history";
|
|
onViewModeChange: (v: "table" | "history") => void;
|
|
historyAvailable: boolean;
|
|
onExport: () => void;
|
|
onExportToSheets: () => void;
|
|
onCopyKeywords: () => void;
|
|
onCheckNow: () => void;
|
|
onRefreshMetrics: () => void;
|
|
metricsRefreshing: boolean;
|
|
checkBusy: boolean;
|
|
checkDisabled: boolean;
|
|
hasData: boolean;
|
|
}) {
|
|
return (
|
|
<div className="shrink-0 flex flex-wrap items-center gap-2 px-4 py-2 border-y border-base-300">
|
|
{/* History needs at least two checks to compare; until then the toggle
|
|
would only offer a worse copy of the Latest table. */}
|
|
{historyAvailable && (
|
|
<SegmentedToggle
|
|
showLabels
|
|
items={[
|
|
{
|
|
value: "table" as const,
|
|
icon: <Table className="size-3.5" />,
|
|
label: "Latest",
|
|
},
|
|
{
|
|
value: "history" as const,
|
|
icon: <CalendarDays className="size-3.5" />,
|
|
label: "History",
|
|
},
|
|
]}
|
|
value={viewMode}
|
|
onChange={onViewModeChange}
|
|
/>
|
|
)}
|
|
|
|
<button
|
|
className={`btn btn-ghost btn-sm gap-1.5 ${showFilters ? "btn-active" : ""}`}
|
|
onClick={onToggleFilters}
|
|
title="Toggle table filters"
|
|
>
|
|
<SlidersHorizontal className="size-3.5" />
|
|
Filters
|
|
{activeFilterCount > 0 && (
|
|
<span className="badge badge-xs badge-primary border-0 text-primary-content">
|
|
{activeFilterCount}
|
|
</span>
|
|
)}
|
|
</button>
|
|
|
|
{isRunning && latestRun ? (
|
|
<div className="flex items-center gap-2 text-sm text-base-content/70">
|
|
<Loader2 className="size-3.5 animate-spin text-primary" />
|
|
<span>
|
|
{latestRun.status === "pending"
|
|
? "Preparing..."
|
|
: `Getting rankings for ${latestRun.keywordsTotal || "?"} keyword${latestRun.keywordsTotal !== 1 ? "s" : ""}...`}{" "}
|
|
{latestRun.keywordsChecked}/{latestRun.keywordsTotal || "?"}
|
|
</span>
|
|
{latestRun.keywordsTotal > 0 && (
|
|
<progress
|
|
className="progress progress-primary w-24"
|
|
value={latestRun.keywordsChecked}
|
|
max={latestRun.keywordsTotal}
|
|
/>
|
|
)}
|
|
</div>
|
|
) : (
|
|
<span className="text-sm text-base-content/60">
|
|
{keywordCount} keywords
|
|
</span>
|
|
)}
|
|
|
|
<div className="flex-1" />
|
|
|
|
<ExportMenu
|
|
onExport={onExport}
|
|
onExportToSheets={onExportToSheets}
|
|
onCopyKeywords={onCopyKeywords}
|
|
hasData={hasData}
|
|
/>
|
|
|
|
<MoreMenu
|
|
onCheckNow={onCheckNow}
|
|
checkBusy={checkBusy}
|
|
checkDisabled={checkDisabled}
|
|
onRefreshMetrics={onRefreshMetrics}
|
|
metricsRefreshing={metricsRefreshing}
|
|
hasData={hasData}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|