keywords: consolidate export buttons into dropdown (#147)
This commit is contained in:
parent
c1a9c1f57d
commit
a42fe4d350
@ -1,12 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
|
ChevronDown,
|
||||||
|
Download,
|
||||||
FileDown,
|
FileDown,
|
||||||
Globe,
|
Globe,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
Save,
|
Save,
|
||||||
|
Sheet,
|
||||||
SlidersHorizontal,
|
SlidersHorizontal,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { ExportToSheetsButton } from "@/client/components/table/ExportToSheetsButton";
|
|
||||||
import { KEYWORD_RESEARCH_HEADERS } from "@/client/features/keywords/state/keywordControllerActions";
|
import { KEYWORD_RESEARCH_HEADERS } from "@/client/features/keywords/state/keywordControllerActions";
|
||||||
|
import { exportTableToSheets } from "@/client/lib/exportToSheets";
|
||||||
import {
|
import {
|
||||||
AreaTrendChart,
|
AreaTrendChart,
|
||||||
KeywordRow,
|
KeywordRow,
|
||||||
@ -121,6 +124,15 @@ function DesktopTableCard({ controller }: Props) {
|
|||||||
? `Showing ${filteredRows.length} of ${rows.length} keywords`
|
? `Showing ${filteredRows.length} of ${rows.length} keywords`
|
||||||
: `Showing ${filteredRows.length} keywords`;
|
: `Showing ${filteredRows.length} keywords`;
|
||||||
|
|
||||||
|
const canExport = filteredRows.length > 0;
|
||||||
|
const handleExportToSheets = () => {
|
||||||
|
void exportTableToSheets({
|
||||||
|
headers: KEYWORD_RESEARCH_HEADERS,
|
||||||
|
rows: sheetsExportRows,
|
||||||
|
feature: "keyword_research",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col min-w-0 border border-base-300 rounded-xl bg-base-100 overflow-hidden">
|
<div className="flex-1 flex flex-col min-w-0 border border-base-300 rounded-xl bg-base-100 overflow-hidden">
|
||||||
<div className="shrink-0 flex items-center gap-2 px-4 py-2 border-b border-base-300">
|
<div className="shrink-0 flex items-center gap-2 px-4 py-2 border-b border-base-300">
|
||||||
@ -149,20 +161,34 @@ function DesktopTableCard({ controller }: Props) {
|
|||||||
<Save className="size-3.5" />
|
<Save className="size-3.5" />
|
||||||
<span className="hidden lg:inline">Save Keywords</span>
|
<span className="hidden lg:inline">Save Keywords</span>
|
||||||
</button>
|
</button>
|
||||||
<ExportToSheetsButton
|
<div className="dropdown dropdown-end">
|
||||||
headers={KEYWORD_RESEARCH_HEADERS}
|
<div
|
||||||
rows={sheetsExportRows}
|
tabIndex={0}
|
||||||
feature="keyword_research"
|
role="button"
|
||||||
className="btn-sm"
|
className={`btn btn-ghost btn-sm gap-1 ${!canExport ? "btn-disabled" : ""}`}
|
||||||
/>
|
>
|
||||||
<button
|
<Download className="size-3.5" />
|
||||||
className="btn btn-ghost btn-sm gap-1"
|
<span className="hidden lg:inline">Export</span>
|
||||||
onClick={controller.exportCsv}
|
<ChevronDown className="size-3 opacity-60" />
|
||||||
disabled={filteredRows.length === 0}
|
</div>
|
||||||
>
|
<ul
|
||||||
<FileDown className="size-3.5" />
|
tabIndex={0}
|
||||||
<span className="hidden lg:inline">Export CSV</span>
|
className="dropdown-content z-10 menu p-2 shadow-lg bg-base-100 border border-base-300 rounded-box w-56"
|
||||||
</button>
|
>
|
||||||
|
<li>
|
||||||
|
<button onClick={handleExportToSheets} disabled={!canExport}>
|
||||||
|
<Sheet className="size-4" />
|
||||||
|
Export to Google Sheets
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onClick={controller.exportCsv} disabled={!canExport}>
|
||||||
|
<FileDown className="size-4" />
|
||||||
|
Export CSV
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showFilters ? <DesktopFilters controller={controller} /> : null}
|
{showFilters ? <DesktopFilters controller={controller} /> : null}
|
||||||
@ -330,9 +356,9 @@ function DesktopSerpPanel({ controller }: Props) {
|
|||||||
: "Last 12 available months";
|
: "Last 12 available months";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="order-1 xl:order-2 flex flex-col min-w-0 gap-2 xl:flex-1">
|
<div className="order-1 xl:order-2 flex flex-col min-w-0 gap-2 xl:flex-1 xl:overflow-y-auto">
|
||||||
{overviewKeyword && overviewKeyword.trend.length > 0 ? (
|
{overviewKeyword && overviewKeyword.trend.length > 0 ? (
|
||||||
<div className="shrink-0 border border-base-300 rounded-xl bg-base-100 px-4 py-3">
|
<div className="shrink-0 overflow-hidden border border-base-300 rounded-xl bg-base-100 px-4 py-3">
|
||||||
<h4 className="text-sm font-semibold mb-1">
|
<h4 className="text-sm font-semibold mb-1">
|
||||||
Search Trends{" "}
|
Search Trends{" "}
|
||||||
<span className="font-normal text-base-content/50">
|
<span className="font-normal text-base-content/50">
|
||||||
@ -343,7 +369,7 @@ function DesktopSerpPanel({ controller }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="flex-1 flex flex-col overflow-hidden border border-base-300 rounded-xl bg-base-100">
|
<div className="flex flex-col overflow-hidden border border-base-300 rounded-xl bg-base-100">
|
||||||
<div className="shrink-0 px-4 py-3 border-b border-base-300">
|
<div className="shrink-0 px-4 py-3 border-b border-base-300">
|
||||||
<h3 className="text-sm font-semibold flex items-center gap-1.5">
|
<h3 className="text-sm font-semibold flex items-center gap-1.5">
|
||||||
<Globe className="size-3.5" />
|
<Globe className="size-3.5" />
|
||||||
@ -355,7 +381,7 @@ function DesktopSerpPanel({ controller }: Props) {
|
|||||||
) : null}
|
) : null}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-y-auto p-4">
|
<div className="p-4">
|
||||||
<SerpAnalysisCard
|
<SerpAnalysisCard
|
||||||
items={controller.serpResults}
|
items={controller.serpResults}
|
||||||
keyword={controller.activeSerpKeyword}
|
keyword={controller.activeSerpKeyword}
|
||||||
|
|||||||
@ -1,6 +1,14 @@
|
|||||||
import { FileDown, RotateCcw, Save, SlidersHorizontal } from "lucide-react";
|
import {
|
||||||
import { ExportToSheetsButton } from "@/client/components/table/ExportToSheetsButton";
|
ChevronDown,
|
||||||
|
Download,
|
||||||
|
FileDown,
|
||||||
|
RotateCcw,
|
||||||
|
Save,
|
||||||
|
Sheet,
|
||||||
|
SlidersHorizontal,
|
||||||
|
} from "lucide-react";
|
||||||
import { KEYWORD_RESEARCH_HEADERS } from "@/client/features/keywords/state/keywordControllerActions";
|
import { KEYWORD_RESEARCH_HEADERS } from "@/client/features/keywords/state/keywordControllerActions";
|
||||||
|
import { exportTableToSheets } from "@/client/lib/exportToSheets";
|
||||||
import {
|
import {
|
||||||
KeywordCard,
|
KeywordCard,
|
||||||
SerpAnalysisCard,
|
SerpAnalysisCard,
|
||||||
@ -76,6 +84,15 @@ function MobileKeywordCards({ controller }: Props) {
|
|||||||
? `Showing ${filteredRows.length} of ${rows.length}`
|
? `Showing ${filteredRows.length} of ${rows.length}`
|
||||||
: `Showing ${filteredRows.length} keywords`;
|
: `Showing ${filteredRows.length} keywords`;
|
||||||
|
|
||||||
|
const canExport = filteredRows.length > 0;
|
||||||
|
const handleExportToSheets = () => {
|
||||||
|
void exportTableToSheets({
|
||||||
|
headers: KEYWORD_RESEARCH_HEADERS,
|
||||||
|
rows: sheetsExportRows,
|
||||||
|
feature: "keyword_research",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col overflow-hidden">
|
<div className="flex-1 flex flex-col overflow-hidden">
|
||||||
{controller.showApproximateMatchNotice ? (
|
{controller.showApproximateMatchNotice ? (
|
||||||
@ -113,20 +130,34 @@ function MobileKeywordCards({ controller }: Props) {
|
|||||||
>
|
>
|
||||||
<Save className="size-3.5" />
|
<Save className="size-3.5" />
|
||||||
</button>
|
</button>
|
||||||
<ExportToSheetsButton
|
<div className="dropdown dropdown-end">
|
||||||
headers={KEYWORD_RESEARCH_HEADERS}
|
<div
|
||||||
rows={sheetsExportRows}
|
tabIndex={0}
|
||||||
feature="keyword_research"
|
role="button"
|
||||||
iconOnly
|
className={`btn btn-ghost btn-xs gap-1 ${!canExport ? "btn-disabled" : ""}`}
|
||||||
/>
|
aria-label="Export"
|
||||||
<button
|
>
|
||||||
className="btn btn-ghost btn-xs"
|
<Download className="size-3.5" />
|
||||||
onClick={controller.exportCsv}
|
<ChevronDown className="size-3 opacity-60" />
|
||||||
disabled={filteredRows.length === 0}
|
</div>
|
||||||
title="Download CSV"
|
<ul
|
||||||
>
|
tabIndex={0}
|
||||||
<FileDown className="size-3.5" />
|
className="dropdown-content z-10 menu p-2 shadow-lg bg-base-100 border border-base-300 rounded-box w-56"
|
||||||
</button>
|
>
|
||||||
|
<li>
|
||||||
|
<button onClick={handleExportToSheets} disabled={!canExport}>
|
||||||
|
<Sheet className="size-4" />
|
||||||
|
Export to Google Sheets
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button onClick={controller.exportCsv} disabled={!canExport}>
|
||||||
|
<FileDown className="size-4" />
|
||||||
|
Export CSV
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showFilters ? <MobileFilters controller={controller} /> : null}
|
{showFilters ? <MobileFilters controller={controller} /> : null}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user