@yourbright/emdash-analytics-plugin 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.js +242 -115
- package/dist/index.js +2 -6
- package/package.json +1 -1
package/dist/admin.js
CHANGED
|
@@ -19,7 +19,7 @@ var ADMIN_ROUTES = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
// src/admin.tsx
|
|
22
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
23
23
|
var API_BASE = `/_emdash/api/plugins/${PLUGIN_ID}`;
|
|
24
24
|
var EMPTY_CONFIG = {
|
|
25
25
|
siteOrigin: "",
|
|
@@ -138,6 +138,19 @@ function Section({
|
|
|
138
138
|
children
|
|
139
139
|
] });
|
|
140
140
|
}
|
|
141
|
+
function InlineHeader({
|
|
142
|
+
title,
|
|
143
|
+
description,
|
|
144
|
+
actions
|
|
145
|
+
}) {
|
|
146
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 border-b border-border pb-4 md:flex-row md:items-end md:justify-between", children: [
|
|
147
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
148
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title }),
|
|
149
|
+
description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null
|
|
150
|
+
] }),
|
|
151
|
+
actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: actions }) : null
|
|
152
|
+
] });
|
|
153
|
+
}
|
|
141
154
|
function StatCard({
|
|
142
155
|
label,
|
|
143
156
|
value,
|
|
@@ -149,6 +162,55 @@ function StatCard({
|
|
|
149
162
|
note ? /* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: note }) : null
|
|
150
163
|
] });
|
|
151
164
|
}
|
|
165
|
+
function sectionFromLocation() {
|
|
166
|
+
if (typeof window === "undefined") return "dashboard";
|
|
167
|
+
const url = new URL(window.location.href);
|
|
168
|
+
const section = url.searchParams.get("section");
|
|
169
|
+
return section === "pages" || section === "settings" ? section : "dashboard";
|
|
170
|
+
}
|
|
171
|
+
function updateSectionInUrl(section) {
|
|
172
|
+
if (typeof window === "undefined") return;
|
|
173
|
+
const url = new URL(window.location.href);
|
|
174
|
+
if (section === "dashboard") {
|
|
175
|
+
url.searchParams.delete("section");
|
|
176
|
+
} else {
|
|
177
|
+
url.searchParams.set("section", section);
|
|
178
|
+
}
|
|
179
|
+
window.history.replaceState({}, "", `${url.pathname}${url.search}${url.hash}`);
|
|
180
|
+
}
|
|
181
|
+
function AnalyticsTabs({
|
|
182
|
+
section,
|
|
183
|
+
onChange
|
|
184
|
+
}) {
|
|
185
|
+
const tabs = [
|
|
186
|
+
{ key: "dashboard", label: "Dashboard" },
|
|
187
|
+
{ key: "pages", label: "Pages" },
|
|
188
|
+
{ key: "settings", label: "Settings" }
|
|
189
|
+
];
|
|
190
|
+
return /* @__PURE__ */ jsx(
|
|
191
|
+
"div",
|
|
192
|
+
{
|
|
193
|
+
role: "tablist",
|
|
194
|
+
"aria-label": "Analytics sections",
|
|
195
|
+
className: "inline-flex flex-wrap items-center gap-1 rounded-xl border border-border bg-accent/60 p-1 shadow-sm",
|
|
196
|
+
children: tabs.map((tab) => {
|
|
197
|
+
const active = tab.key === section;
|
|
198
|
+
return /* @__PURE__ */ jsx(
|
|
199
|
+
"button",
|
|
200
|
+
{
|
|
201
|
+
type: "button",
|
|
202
|
+
role: "tab",
|
|
203
|
+
"aria-selected": active,
|
|
204
|
+
onClick: () => onChange(tab.key),
|
|
205
|
+
className: `min-h-10 rounded-lg px-4 py-2 text-sm font-semibold transition focus:outline-none focus:ring-2 focus:ring-foreground/20 ${active ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:bg-background/80 hover:text-foreground"}`,
|
|
206
|
+
children: tab.label
|
|
207
|
+
},
|
|
208
|
+
tab.key
|
|
209
|
+
);
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
}
|
|
152
214
|
function ErrorBanner({ message }) {
|
|
153
215
|
if (!message) return null;
|
|
154
216
|
return /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700", children: message });
|
|
@@ -404,7 +466,7 @@ function MoversTable({
|
|
|
404
466
|
] }, item.urlPath)) })
|
|
405
467
|
] }) });
|
|
406
468
|
}
|
|
407
|
-
function OverviewPage() {
|
|
469
|
+
function OverviewPage({ embedded = false }) {
|
|
408
470
|
const [status, setStatus] = React.useState(null);
|
|
409
471
|
const [overview, setOverview] = React.useState(null);
|
|
410
472
|
const [loading, setLoading] = React.useState(true);
|
|
@@ -434,65 +496,79 @@ function OverviewPage() {
|
|
|
434
496
|
}, [load]);
|
|
435
497
|
const summary = overview?.summary ?? status?.summary ?? null;
|
|
436
498
|
const freshness = overview?.freshness ?? status?.freshness ?? idleFreshness();
|
|
437
|
-
|
|
499
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
500
|
+
/* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
501
|
+
!status?.config ? /* @__PURE__ */ jsx(Section, { title: "Not Configured", subtitle: "Save your Google connection settings first.", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "After saving the configuration, run a manual sync to populate this dashboard." }) }) : null,
|
|
502
|
+
/* @__PURE__ */ jsx(Section, { title: "Freshness", subtitle: "Track the latest sync and the effective source dates.", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-4", children: [
|
|
503
|
+
/* @__PURE__ */ jsx(StatCard, { label: "Last Sync", value: formatDateTime(freshness.lastSyncedAt), note: statusLabel(freshness.lastStatus) }),
|
|
504
|
+
/* @__PURE__ */ jsx(StatCard, { label: "GSC Final Date", value: freshness.lastGscDate || "-" }),
|
|
505
|
+
/* @__PURE__ */ jsx(StatCard, { label: "GA Final Date", value: freshness.lastGaDate || "-" }),
|
|
506
|
+
/* @__PURE__ */ jsx(StatCard, { label: "Service Account", value: status?.config?.serviceAccountEmail || "-" })
|
|
507
|
+
] }) }),
|
|
508
|
+
/* @__PURE__ */ jsx(Section, { title: "KPI Snapshot", subtitle: "Current 28 days versus the previous 28 days across all tracked public pages.", children: /* @__PURE__ */ jsx("div", { className: "grid gap-4 md:grid-cols-2 xl:grid-cols-5", children: (overview?.kpiDeltas ?? []).map((metric) => /* @__PURE__ */ jsx(KpiDeltaCard, { metric }, metric.key)) }) }),
|
|
509
|
+
summary ? /* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
510
|
+
/* @__PURE__ */ jsx(
|
|
511
|
+
TrendPanel,
|
|
512
|
+
{
|
|
513
|
+
title: "Search Trend",
|
|
514
|
+
subtitle: "Daily search demand and click capture for the current 28-day window.",
|
|
515
|
+
metrics: (overview?.kpiDeltas ?? []).filter(
|
|
516
|
+
(metric) => metric.key === "gscClicks" || metric.key === "gscImpressions"
|
|
517
|
+
),
|
|
518
|
+
trend: summary.trend
|
|
519
|
+
}
|
|
520
|
+
),
|
|
521
|
+
/* @__PURE__ */ jsx(
|
|
522
|
+
TrendPanel,
|
|
523
|
+
{
|
|
524
|
+
title: "Traffic Trend",
|
|
525
|
+
subtitle: "Daily traffic movement from GA4 for the current 28-day window.",
|
|
526
|
+
metrics: (overview?.kpiDeltas ?? []).filter(
|
|
527
|
+
(metric) => metric.key === "gaViews" || metric.key === "gaSessions"
|
|
528
|
+
),
|
|
529
|
+
trend: summary.trend
|
|
530
|
+
}
|
|
531
|
+
)
|
|
532
|
+
] }) : null,
|
|
533
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
534
|
+
/* @__PURE__ */ jsx(Section, { title: "Page Mix", subtitle: "Compare page groups by current volume and change from the previous window.", children: /* @__PURE__ */ jsx(BreakdownTable, { rows: overview?.pageKindBreakdown ?? [], emptyMessage: "No tracked pages yet." }) }),
|
|
535
|
+
/* @__PURE__ */ jsx(Section, { title: "Managed Coverage", subtitle: "See whether growth is coming from EmDash-managed content or unmanaged pages.", children: /* @__PURE__ */ jsx(BreakdownTable, { rows: overview?.managedBreakdown ?? [], emptyMessage: "No tracked pages yet." }) })
|
|
536
|
+
] }),
|
|
537
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
538
|
+
/* @__PURE__ */ jsx(Section, { title: "Top Gainers", subtitle: "Pages with the strongest positive movement in the last 28 days.", children: /* @__PURE__ */ jsx(MoversTable, { items: overview?.topGainers ?? [], emptyMessage: "No gaining pages yet." }) }),
|
|
539
|
+
/* @__PURE__ */ jsx(Section, { title: "Top Decliners", subtitle: "Pages with the sharpest drop and the clearest candidates for investigation.", children: /* @__PURE__ */ jsx(MoversTable, { items: overview?.topDecliners ?? [], emptyMessage: "No declining pages yet." }) })
|
|
540
|
+
] }),
|
|
541
|
+
/* @__PURE__ */ jsx(Section, { title: "Reporting Windows", subtitle: "The agent API returns the same windows.", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
542
|
+
/* @__PURE__ */ jsx(WindowCard, { label: "GSC Current", value: summary?.window.gscCurrent }),
|
|
543
|
+
/* @__PURE__ */ jsx(WindowCard, { label: "GSC Previous", value: summary?.window.gscPrevious }),
|
|
544
|
+
/* @__PURE__ */ jsx(WindowCard, { label: "GA Current", value: summary?.window.gaCurrent }),
|
|
545
|
+
/* @__PURE__ */ jsx(WindowCard, { label: "GA Previous", value: summary?.window.gaPrevious })
|
|
546
|
+
] }) })
|
|
547
|
+
] });
|
|
548
|
+
if (embedded) {
|
|
549
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
550
|
+
/* @__PURE__ */ jsx(
|
|
551
|
+
InlineHeader,
|
|
552
|
+
{
|
|
553
|
+
title: "Dashboard",
|
|
554
|
+
description: "Monitor site health, compare the last 28 days to the previous window, and spot pages that changed fastest.",
|
|
555
|
+
actions: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void load(), disabled: loading, children: "Reload" })
|
|
556
|
+
}
|
|
557
|
+
),
|
|
558
|
+
content
|
|
559
|
+
] });
|
|
560
|
+
}
|
|
561
|
+
return /* @__PURE__ */ jsx(
|
|
438
562
|
Shell,
|
|
439
563
|
{
|
|
440
564
|
title: "Content Insights",
|
|
441
565
|
description: "Monitor site health, compare the last 28 days to the previous window, and spot pages that changed fastest.",
|
|
442
566
|
actions: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void load(), disabled: loading, children: "Reload" }),
|
|
443
|
-
children:
|
|
444
|
-
/* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
445
|
-
!status?.config ? /* @__PURE__ */ jsx(Section, { title: "Not Configured", subtitle: "Save your Google connection settings first.", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "After saving the configuration, run a manual sync to populate this dashboard." }) }) : null,
|
|
446
|
-
/* @__PURE__ */ jsx(Section, { title: "Freshness", subtitle: "Track the latest sync and the effective source dates.", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-4", children: [
|
|
447
|
-
/* @__PURE__ */ jsx(StatCard, { label: "Last Sync", value: formatDateTime(freshness.lastSyncedAt), note: statusLabel(freshness.lastStatus) }),
|
|
448
|
-
/* @__PURE__ */ jsx(StatCard, { label: "GSC Final Date", value: freshness.lastGscDate || "-" }),
|
|
449
|
-
/* @__PURE__ */ jsx(StatCard, { label: "GA Final Date", value: freshness.lastGaDate || "-" }),
|
|
450
|
-
/* @__PURE__ */ jsx(StatCard, { label: "Service Account", value: status?.config?.serviceAccountEmail || "-" })
|
|
451
|
-
] }) }),
|
|
452
|
-
/* @__PURE__ */ jsx(Section, { title: "KPI Snapshot", subtitle: "Current 28 days versus the previous 28 days across all tracked public pages.", children: /* @__PURE__ */ jsx("div", { className: "grid gap-4 md:grid-cols-2 xl:grid-cols-5", children: (overview?.kpiDeltas ?? []).map((metric) => /* @__PURE__ */ jsx(KpiDeltaCard, { metric }, metric.key)) }) }),
|
|
453
|
-
summary ? /* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
454
|
-
/* @__PURE__ */ jsx(
|
|
455
|
-
TrendPanel,
|
|
456
|
-
{
|
|
457
|
-
title: "Search Trend",
|
|
458
|
-
subtitle: "Daily search demand and click capture for the current 28-day window.",
|
|
459
|
-
metrics: (overview?.kpiDeltas ?? []).filter(
|
|
460
|
-
(metric) => metric.key === "gscClicks" || metric.key === "gscImpressions"
|
|
461
|
-
),
|
|
462
|
-
trend: summary.trend
|
|
463
|
-
}
|
|
464
|
-
),
|
|
465
|
-
/* @__PURE__ */ jsx(
|
|
466
|
-
TrendPanel,
|
|
467
|
-
{
|
|
468
|
-
title: "Traffic Trend",
|
|
469
|
-
subtitle: "Daily traffic movement from GA4 for the current 28-day window.",
|
|
470
|
-
metrics: (overview?.kpiDeltas ?? []).filter(
|
|
471
|
-
(metric) => metric.key === "gaViews" || metric.key === "gaSessions"
|
|
472
|
-
),
|
|
473
|
-
trend: summary.trend
|
|
474
|
-
}
|
|
475
|
-
)
|
|
476
|
-
] }) : null,
|
|
477
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
478
|
-
/* @__PURE__ */ jsx(Section, { title: "Page Mix", subtitle: "Compare page groups by current volume and change from the previous window.", children: /* @__PURE__ */ jsx(BreakdownTable, { rows: overview?.pageKindBreakdown ?? [], emptyMessage: "No tracked pages yet." }) }),
|
|
479
|
-
/* @__PURE__ */ jsx(Section, { title: "Managed Coverage", subtitle: "See whether growth is coming from EmDash-managed content or unmanaged pages.", children: /* @__PURE__ */ jsx(BreakdownTable, { rows: overview?.managedBreakdown ?? [], emptyMessage: "No tracked pages yet." }) })
|
|
480
|
-
] }),
|
|
481
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
482
|
-
/* @__PURE__ */ jsx(Section, { title: "Top Gainers", subtitle: "Pages with the strongest positive movement in the last 28 days.", children: /* @__PURE__ */ jsx(MoversTable, { items: overview?.topGainers ?? [], emptyMessage: "No gaining pages yet." }) }),
|
|
483
|
-
/* @__PURE__ */ jsx(Section, { title: "Top Decliners", subtitle: "Pages with the sharpest drop and the clearest candidates for investigation.", children: /* @__PURE__ */ jsx(MoversTable, { items: overview?.topDecliners ?? [], emptyMessage: "No declining pages yet." }) })
|
|
484
|
-
] }),
|
|
485
|
-
/* @__PURE__ */ jsx(Section, { title: "Reporting Windows", subtitle: "The agent API returns the same windows.", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
486
|
-
/* @__PURE__ */ jsx(WindowCard, { label: "GSC Current", value: summary?.window.gscCurrent }),
|
|
487
|
-
/* @__PURE__ */ jsx(WindowCard, { label: "GSC Previous", value: summary?.window.gscPrevious }),
|
|
488
|
-
/* @__PURE__ */ jsx(WindowCard, { label: "GA Current", value: summary?.window.gaCurrent }),
|
|
489
|
-
/* @__PURE__ */ jsx(WindowCard, { label: "GA Previous", value: summary?.window.gaPrevious })
|
|
490
|
-
] }) })
|
|
491
|
-
]
|
|
567
|
+
children: content
|
|
492
568
|
}
|
|
493
569
|
);
|
|
494
570
|
}
|
|
495
|
-
function PagesPage() {
|
|
571
|
+
function PagesPage({ embedded = false }) {
|
|
496
572
|
const [managed, setManaged] = React.useState("all");
|
|
497
573
|
const [pageKind, setPageKind] = React.useState("all");
|
|
498
574
|
const [hasOpportunity, setHasOpportunity] = React.useState(false);
|
|
@@ -548,7 +624,7 @@ function PagesPage() {
|
|
|
548
624
|
cancelled = true;
|
|
549
625
|
};
|
|
550
626
|
}, [selected]);
|
|
551
|
-
|
|
627
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
552
628
|
/* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
553
629
|
/* @__PURE__ */ jsx(Section, { title: "Filters", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-4", children: [
|
|
554
630
|
/* @__PURE__ */ jsx(Field, { label: "Scope", children: /* @__PURE__ */ jsx(
|
|
@@ -637,8 +713,21 @@ function PagesPage() {
|
|
|
637
713
|
] }) })
|
|
638
714
|
] })
|
|
639
715
|
] });
|
|
716
|
+
if (embedded) {
|
|
717
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
718
|
+
/* @__PURE__ */ jsx(
|
|
719
|
+
InlineHeader,
|
|
720
|
+
{
|
|
721
|
+
title: "Pages",
|
|
722
|
+
description: "Explore all public pages and filter down to the content that needs attention."
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
content
|
|
726
|
+
] });
|
|
727
|
+
}
|
|
728
|
+
return /* @__PURE__ */ jsx(Shell, { title: "Pages", description: "Explore all public pages and filter down to the content that needs attention.", children: content });
|
|
640
729
|
}
|
|
641
|
-
function SettingsPage() {
|
|
730
|
+
function SettingsPage({ embedded = false }) {
|
|
642
731
|
const [draft, setDraft] = React.useState(EMPTY_CONFIG);
|
|
643
732
|
const [storedConfig, setStoredConfig] = React.useState({
|
|
644
733
|
siteOrigin: "",
|
|
@@ -792,70 +881,108 @@ function SettingsPage() {
|
|
|
792
881
|
setBusy(null);
|
|
793
882
|
}
|
|
794
883
|
};
|
|
795
|
-
|
|
884
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
885
|
+
/* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
886
|
+
/* @__PURE__ */ jsx(SuccessBanner, { message: success }),
|
|
887
|
+
/* @__PURE__ */ jsxs(Section, { title: "Google Connection", children: [
|
|
888
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
889
|
+
/* @__PURE__ */ jsx(Field, { label: "Canonical Site Origin", hint: "Example: https://www.yourbright.co.jp", children: /* @__PURE__ */ jsx(Input, { value: draft.siteOrigin, onChange: (value) => setDraft((current) => ({ ...current, siteOrigin: value })) }) }),
|
|
890
|
+
/* @__PURE__ */ jsx(Field, { label: "GA4 Property ID", hint: "Enter the numeric property ID.", children: /* @__PURE__ */ jsx(Input, { value: draft.ga4PropertyId, onChange: (value) => setDraft((current) => ({ ...current, ga4PropertyId: value })) }) }),
|
|
891
|
+
/* @__PURE__ */ jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsx(Field, { label: "Search Console Property", hint: "Example: https://www.yourbright.co.jp/ or sc-domain:yourbright.co.jp", children: /* @__PURE__ */ jsx(Input, { value: draft.gscSiteUrl, onChange: (value) => setDraft((current) => ({ ...current, gscSiteUrl: value })) }) }) }),
|
|
892
|
+
/* @__PURE__ */ jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsx(
|
|
893
|
+
Field,
|
|
894
|
+
{
|
|
895
|
+
label: "Service Account JSON",
|
|
896
|
+
hint: hasStoredServiceAccount ? `Current: ${storedServiceAccountEmail || "configured"}. Leave blank to keep the current secret.` : "Required on the first save.",
|
|
897
|
+
children: /* @__PURE__ */ jsx(
|
|
898
|
+
TextArea,
|
|
899
|
+
{
|
|
900
|
+
value: draft.serviceAccountJson,
|
|
901
|
+
onChange: (value) => setDraft((current) => ({ ...current, serviceAccountJson: value })),
|
|
902
|
+
placeholder: '{"client_email":"...","private_key":"..."}',
|
|
903
|
+
rows: 12
|
|
904
|
+
}
|
|
905
|
+
)
|
|
906
|
+
}
|
|
907
|
+
) })
|
|
908
|
+
] }),
|
|
909
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex flex-wrap gap-3 border-t border-border pt-4", children: [
|
|
910
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void save(), disabled: !!busy, children: busy === "save" ? "Saving..." : "Save Settings" }),
|
|
911
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void testConnection(), disabled: !!busy, children: busy === "test" ? "Testing..." : "Test Connection" }),
|
|
912
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void syncNow(), disabled: !!busy, children: busy === "sync" ? "Syncing..." : "Run Manual Sync" })
|
|
913
|
+
] })
|
|
914
|
+
] }),
|
|
915
|
+
/* @__PURE__ */ jsxs(Section, { title: "Agent API Keys", subtitle: "Use these with Authorization: AgentKey yb_ins_... or X-Emdash-Agent-Key. Raw keys are shown only once.", children: [
|
|
916
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]", children: [
|
|
917
|
+
/* @__PURE__ */ jsx(Field, { label: "New key label", children: /* @__PURE__ */ jsx(Input, { value: newKeyLabel, onChange: setNewKeyLabel, placeholder: "content-feedback-agent" }) }),
|
|
918
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-end", children: /* @__PURE__ */ jsx(Button, { onClick: () => void createKey(), disabled: !!busy || !newKeyLabel.trim(), children: busy === "create-key" ? "Creating..." : "Create Key" }) })
|
|
919
|
+
] }),
|
|
920
|
+
generatedKey ? /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-lg border border-amber-200 bg-amber-50 p-4", children: [
|
|
921
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-amber-900", children: "Generated Key" }),
|
|
922
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 break-all font-mono text-sm text-amber-900", children: generatedKey })
|
|
923
|
+
] }) : null,
|
|
924
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full text-sm", children: [
|
|
925
|
+
/* @__PURE__ */ jsx("thead", { className: "text-left text-xs uppercase tracking-[0.16em] text-muted-foreground", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
926
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Prefix" }),
|
|
927
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Label" }),
|
|
928
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Created" }),
|
|
929
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Last Used" }),
|
|
930
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Status" }),
|
|
931
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4" })
|
|
932
|
+
] }) }),
|
|
933
|
+
/* @__PURE__ */ jsx("tbody", { children: keys.map((key) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/80", children: [
|
|
934
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4 font-mono text-xs", children: key.prefix }),
|
|
935
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: key.label }),
|
|
936
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: formatDateTime(key.createdAt) }),
|
|
937
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: formatDateTime(key.lastUsedAt) }),
|
|
938
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: key.revokedAt ? "Revoked" : "Active" }),
|
|
939
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: key.revokedAt ? null : /* @__PURE__ */ jsx(Button, { variant: "danger", onClick: () => void revokeKey(key.prefix), disabled: busy === key.prefix, children: "Revoke" }) })
|
|
940
|
+
] }, key.prefix)) })
|
|
941
|
+
] }) })
|
|
942
|
+
] })
|
|
943
|
+
] });
|
|
944
|
+
if (embedded) {
|
|
945
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
946
|
+
/* @__PURE__ */ jsx(
|
|
947
|
+
InlineHeader,
|
|
948
|
+
{
|
|
949
|
+
title: "Settings",
|
|
950
|
+
description: "Manage Google connection settings, manual sync, and agent API keys."
|
|
951
|
+
}
|
|
952
|
+
),
|
|
953
|
+
content
|
|
954
|
+
] });
|
|
955
|
+
}
|
|
956
|
+
return /* @__PURE__ */ jsx(
|
|
796
957
|
Shell,
|
|
797
958
|
{
|
|
798
959
|
title: "Analytics",
|
|
799
960
|
description: "Manage Google connection settings, manual sync, and agent API keys.",
|
|
961
|
+
children: content
|
|
962
|
+
}
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
function AnalyticsPage() {
|
|
966
|
+
const [section, setSection] = React.useState(sectionFromLocation);
|
|
967
|
+
React.useEffect(() => {
|
|
968
|
+
const handlePopstate = () => setSection(sectionFromLocation());
|
|
969
|
+
window.addEventListener("popstate", handlePopstate);
|
|
970
|
+
return () => window.removeEventListener("popstate", handlePopstate);
|
|
971
|
+
}, []);
|
|
972
|
+
const selectSection = (nextSection) => {
|
|
973
|
+
setSection(nextSection);
|
|
974
|
+
updateSectionInUrl(nextSection);
|
|
975
|
+
};
|
|
976
|
+
return /* @__PURE__ */ jsxs(
|
|
977
|
+
Shell,
|
|
978
|
+
{
|
|
979
|
+
title: "Analytics",
|
|
980
|
+
description: "Use one workspace for dashboard review, page drilldown, and connection management.",
|
|
981
|
+
actions: /* @__PURE__ */ jsx(AnalyticsTabs, { section, onChange: selectSection }),
|
|
800
982
|
children: [
|
|
801
|
-
/* @__PURE__ */ jsx(
|
|
802
|
-
/* @__PURE__ */ jsx(
|
|
803
|
-
/* @__PURE__ */
|
|
804
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
805
|
-
/* @__PURE__ */ jsx(Field, { label: "Canonical Site Origin", hint: "Example: https://www.yourbright.co.jp", children: /* @__PURE__ */ jsx(Input, { value: draft.siteOrigin, onChange: (value) => setDraft((current) => ({ ...current, siteOrigin: value })) }) }),
|
|
806
|
-
/* @__PURE__ */ jsx(Field, { label: "GA4 Property ID", hint: "Enter the numeric property ID.", children: /* @__PURE__ */ jsx(Input, { value: draft.ga4PropertyId, onChange: (value) => setDraft((current) => ({ ...current, ga4PropertyId: value })) }) }),
|
|
807
|
-
/* @__PURE__ */ jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsx(Field, { label: "Search Console Property", hint: "Example: https://www.yourbright.co.jp/ or sc-domain:yourbright.co.jp", children: /* @__PURE__ */ jsx(Input, { value: draft.gscSiteUrl, onChange: (value) => setDraft((current) => ({ ...current, gscSiteUrl: value })) }) }) }),
|
|
808
|
-
/* @__PURE__ */ jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsx(
|
|
809
|
-
Field,
|
|
810
|
-
{
|
|
811
|
-
label: "Service Account JSON",
|
|
812
|
-
hint: hasStoredServiceAccount ? `Current: ${storedServiceAccountEmail || "configured"}. Leave blank to keep the current secret.` : "Required on the first save.",
|
|
813
|
-
children: /* @__PURE__ */ jsx(
|
|
814
|
-
TextArea,
|
|
815
|
-
{
|
|
816
|
-
value: draft.serviceAccountJson,
|
|
817
|
-
onChange: (value) => setDraft((current) => ({ ...current, serviceAccountJson: value })),
|
|
818
|
-
placeholder: '{"client_email":"...","private_key":"..."}',
|
|
819
|
-
rows: 12
|
|
820
|
-
}
|
|
821
|
-
)
|
|
822
|
-
}
|
|
823
|
-
) })
|
|
824
|
-
] }),
|
|
825
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex flex-wrap gap-3 border-t border-border pt-4", children: [
|
|
826
|
-
/* @__PURE__ */ jsx(Button, { onClick: () => void save(), disabled: !!busy, children: busy === "save" ? "Saving..." : "Save Settings" }),
|
|
827
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void testConnection(), disabled: !!busy, children: busy === "test" ? "Testing..." : "Test Connection" }),
|
|
828
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void syncNow(), disabled: !!busy, children: busy === "sync" ? "Syncing..." : "Run Manual Sync" })
|
|
829
|
-
] })
|
|
830
|
-
] }),
|
|
831
|
-
/* @__PURE__ */ jsxs(Section, { title: "Agent API Keys", subtitle: "Use these with Authorization: AgentKey yb_ins_... or X-Emdash-Agent-Key. Raw keys are shown only once.", children: [
|
|
832
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]", children: [
|
|
833
|
-
/* @__PURE__ */ jsx(Field, { label: "New key label", children: /* @__PURE__ */ jsx(Input, { value: newKeyLabel, onChange: setNewKeyLabel, placeholder: "content-feedback-agent" }) }),
|
|
834
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-end", children: /* @__PURE__ */ jsx(Button, { onClick: () => void createKey(), disabled: !!busy || !newKeyLabel.trim(), children: busy === "create-key" ? "Creating..." : "Create Key" }) })
|
|
835
|
-
] }),
|
|
836
|
-
generatedKey ? /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-lg border border-amber-200 bg-amber-50 p-4", children: [
|
|
837
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-amber-900", children: "Generated Key" }),
|
|
838
|
-
/* @__PURE__ */ jsx("div", { className: "mt-2 break-all font-mono text-sm text-amber-900", children: generatedKey })
|
|
839
|
-
] }) : null,
|
|
840
|
-
/* @__PURE__ */ jsx("div", { className: "mt-4 overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full text-sm", children: [
|
|
841
|
-
/* @__PURE__ */ jsx("thead", { className: "text-left text-xs uppercase tracking-[0.16em] text-muted-foreground", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
842
|
-
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Prefix" }),
|
|
843
|
-
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Label" }),
|
|
844
|
-
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Created" }),
|
|
845
|
-
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Last Used" }),
|
|
846
|
-
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Status" }),
|
|
847
|
-
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4" })
|
|
848
|
-
] }) }),
|
|
849
|
-
/* @__PURE__ */ jsx("tbody", { children: keys.map((key) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/80", children: [
|
|
850
|
-
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4 font-mono text-xs", children: key.prefix }),
|
|
851
|
-
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: key.label }),
|
|
852
|
-
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: formatDateTime(key.createdAt) }),
|
|
853
|
-
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: formatDateTime(key.lastUsedAt) }),
|
|
854
|
-
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: key.revokedAt ? "Revoked" : "Active" }),
|
|
855
|
-
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: key.revokedAt ? null : /* @__PURE__ */ jsx(Button, { variant: "danger", onClick: () => void revokeKey(key.prefix), disabled: busy === key.prefix, children: "Revoke" }) })
|
|
856
|
-
] }, key.prefix)) })
|
|
857
|
-
] }) })
|
|
858
|
-
] })
|
|
983
|
+
section === "dashboard" ? /* @__PURE__ */ jsx(OverviewPage, { embedded: true }) : null,
|
|
984
|
+
section === "pages" ? /* @__PURE__ */ jsx(PagesPage, { embedded: true }) : null,
|
|
985
|
+
section === "settings" ? /* @__PURE__ */ jsx(SettingsPage, { embedded: true }) : null
|
|
859
986
|
]
|
|
860
987
|
}
|
|
861
988
|
);
|
|
@@ -1004,7 +1131,7 @@ function numberValue(value) {
|
|
|
1004
1131
|
return typeof value === "number" ? value : 0;
|
|
1005
1132
|
}
|
|
1006
1133
|
var pages = {
|
|
1007
|
-
"/":
|
|
1134
|
+
"/": AnalyticsPage,
|
|
1008
1135
|
"/pages": PagesPage,
|
|
1009
1136
|
"/settings": SettingsPage
|
|
1010
1137
|
};
|
package/dist/index.js
CHANGED
|
@@ -1513,9 +1513,7 @@ function contentInsightsPlugin() {
|
|
|
1513
1513
|
"www.googleapis.com"
|
|
1514
1514
|
],
|
|
1515
1515
|
adminPages: [
|
|
1516
|
-
{ path: "/", label: "
|
|
1517
|
-
{ path: "/pages", label: "Pages", icon: "list" },
|
|
1518
|
-
{ path: "/settings", label: "Analytics", icon: "gear" }
|
|
1516
|
+
{ path: "/", label: "Analytics", icon: "chart-bar" }
|
|
1519
1517
|
],
|
|
1520
1518
|
adminWidgets: [
|
|
1521
1519
|
{ id: "content-opportunities", title: "Content Opportunities", size: "full" }
|
|
@@ -1696,9 +1694,7 @@ function createPlugin() {
|
|
|
1696
1694
|
},
|
|
1697
1695
|
admin: {
|
|
1698
1696
|
pages: [
|
|
1699
|
-
{ path: "/", label: "
|
|
1700
|
-
{ path: "/pages", label: "Pages", icon: "list" },
|
|
1701
|
-
{ path: "/settings", label: "Analytics", icon: "gear" }
|
|
1697
|
+
{ path: "/", label: "Analytics", icon: "chart-bar" }
|
|
1702
1698
|
],
|
|
1703
1699
|
widgets: [
|
|
1704
1700
|
{ id: "content-opportunities", title: "Content Opportunities", size: "full" }
|