@yourbright/emdash-analytics-plugin 0.1.3 → 0.1.4
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 +232 -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,45 @@ 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("div", { className: "flex flex-wrap gap-2", children: tabs.map((tab) => {
|
|
191
|
+
const active = tab.key === section;
|
|
192
|
+
return /* @__PURE__ */ jsx(
|
|
193
|
+
"button",
|
|
194
|
+
{
|
|
195
|
+
type: "button",
|
|
196
|
+
onClick: () => onChange(tab.key),
|
|
197
|
+
className: `rounded-full border px-3 py-2 text-sm font-medium transition ${active ? "border-foreground bg-foreground text-background" : "border-border bg-background text-foreground hover:bg-accent"}`,
|
|
198
|
+
children: tab.label
|
|
199
|
+
},
|
|
200
|
+
tab.key
|
|
201
|
+
);
|
|
202
|
+
}) });
|
|
203
|
+
}
|
|
152
204
|
function ErrorBanner({ message }) {
|
|
153
205
|
if (!message) return null;
|
|
154
206
|
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 +456,7 @@ function MoversTable({
|
|
|
404
456
|
] }, item.urlPath)) })
|
|
405
457
|
] }) });
|
|
406
458
|
}
|
|
407
|
-
function OverviewPage() {
|
|
459
|
+
function OverviewPage({ embedded = false }) {
|
|
408
460
|
const [status, setStatus] = React.useState(null);
|
|
409
461
|
const [overview, setOverview] = React.useState(null);
|
|
410
462
|
const [loading, setLoading] = React.useState(true);
|
|
@@ -434,65 +486,79 @@ function OverviewPage() {
|
|
|
434
486
|
}, [load]);
|
|
435
487
|
const summary = overview?.summary ?? status?.summary ?? null;
|
|
436
488
|
const freshness = overview?.freshness ?? status?.freshness ?? idleFreshness();
|
|
437
|
-
|
|
489
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
490
|
+
/* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
491
|
+
!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,
|
|
492
|
+
/* @__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: [
|
|
493
|
+
/* @__PURE__ */ jsx(StatCard, { label: "Last Sync", value: formatDateTime(freshness.lastSyncedAt), note: statusLabel(freshness.lastStatus) }),
|
|
494
|
+
/* @__PURE__ */ jsx(StatCard, { label: "GSC Final Date", value: freshness.lastGscDate || "-" }),
|
|
495
|
+
/* @__PURE__ */ jsx(StatCard, { label: "GA Final Date", value: freshness.lastGaDate || "-" }),
|
|
496
|
+
/* @__PURE__ */ jsx(StatCard, { label: "Service Account", value: status?.config?.serviceAccountEmail || "-" })
|
|
497
|
+
] }) }),
|
|
498
|
+
/* @__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)) }) }),
|
|
499
|
+
summary ? /* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
500
|
+
/* @__PURE__ */ jsx(
|
|
501
|
+
TrendPanel,
|
|
502
|
+
{
|
|
503
|
+
title: "Search Trend",
|
|
504
|
+
subtitle: "Daily search demand and click capture for the current 28-day window.",
|
|
505
|
+
metrics: (overview?.kpiDeltas ?? []).filter(
|
|
506
|
+
(metric) => metric.key === "gscClicks" || metric.key === "gscImpressions"
|
|
507
|
+
),
|
|
508
|
+
trend: summary.trend
|
|
509
|
+
}
|
|
510
|
+
),
|
|
511
|
+
/* @__PURE__ */ jsx(
|
|
512
|
+
TrendPanel,
|
|
513
|
+
{
|
|
514
|
+
title: "Traffic Trend",
|
|
515
|
+
subtitle: "Daily traffic movement from GA4 for the current 28-day window.",
|
|
516
|
+
metrics: (overview?.kpiDeltas ?? []).filter(
|
|
517
|
+
(metric) => metric.key === "gaViews" || metric.key === "gaSessions"
|
|
518
|
+
),
|
|
519
|
+
trend: summary.trend
|
|
520
|
+
}
|
|
521
|
+
)
|
|
522
|
+
] }) : null,
|
|
523
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
524
|
+
/* @__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." }) }),
|
|
525
|
+
/* @__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." }) })
|
|
526
|
+
] }),
|
|
527
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-2", children: [
|
|
528
|
+
/* @__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." }) }),
|
|
529
|
+
/* @__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." }) })
|
|
530
|
+
] }),
|
|
531
|
+
/* @__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: [
|
|
532
|
+
/* @__PURE__ */ jsx(WindowCard, { label: "GSC Current", value: summary?.window.gscCurrent }),
|
|
533
|
+
/* @__PURE__ */ jsx(WindowCard, { label: "GSC Previous", value: summary?.window.gscPrevious }),
|
|
534
|
+
/* @__PURE__ */ jsx(WindowCard, { label: "GA Current", value: summary?.window.gaCurrent }),
|
|
535
|
+
/* @__PURE__ */ jsx(WindowCard, { label: "GA Previous", value: summary?.window.gaPrevious })
|
|
536
|
+
] }) })
|
|
537
|
+
] });
|
|
538
|
+
if (embedded) {
|
|
539
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
540
|
+
/* @__PURE__ */ jsx(
|
|
541
|
+
InlineHeader,
|
|
542
|
+
{
|
|
543
|
+
title: "Dashboard",
|
|
544
|
+
description: "Monitor site health, compare the last 28 days to the previous window, and spot pages that changed fastest.",
|
|
545
|
+
actions: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void load(), disabled: loading, children: "Reload" })
|
|
546
|
+
}
|
|
547
|
+
),
|
|
548
|
+
content
|
|
549
|
+
] });
|
|
550
|
+
}
|
|
551
|
+
return /* @__PURE__ */ jsx(
|
|
438
552
|
Shell,
|
|
439
553
|
{
|
|
440
554
|
title: "Content Insights",
|
|
441
555
|
description: "Monitor site health, compare the last 28 days to the previous window, and spot pages that changed fastest.",
|
|
442
556
|
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
|
-
]
|
|
557
|
+
children: content
|
|
492
558
|
}
|
|
493
559
|
);
|
|
494
560
|
}
|
|
495
|
-
function PagesPage() {
|
|
561
|
+
function PagesPage({ embedded = false }) {
|
|
496
562
|
const [managed, setManaged] = React.useState("all");
|
|
497
563
|
const [pageKind, setPageKind] = React.useState("all");
|
|
498
564
|
const [hasOpportunity, setHasOpportunity] = React.useState(false);
|
|
@@ -548,7 +614,7 @@ function PagesPage() {
|
|
|
548
614
|
cancelled = true;
|
|
549
615
|
};
|
|
550
616
|
}, [selected]);
|
|
551
|
-
|
|
617
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
552
618
|
/* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
553
619
|
/* @__PURE__ */ jsx(Section, { title: "Filters", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-4", children: [
|
|
554
620
|
/* @__PURE__ */ jsx(Field, { label: "Scope", children: /* @__PURE__ */ jsx(
|
|
@@ -637,8 +703,21 @@ function PagesPage() {
|
|
|
637
703
|
] }) })
|
|
638
704
|
] })
|
|
639
705
|
] });
|
|
706
|
+
if (embedded) {
|
|
707
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
708
|
+
/* @__PURE__ */ jsx(
|
|
709
|
+
InlineHeader,
|
|
710
|
+
{
|
|
711
|
+
title: "Pages",
|
|
712
|
+
description: "Explore all public pages and filter down to the content that needs attention."
|
|
713
|
+
}
|
|
714
|
+
),
|
|
715
|
+
content
|
|
716
|
+
] });
|
|
717
|
+
}
|
|
718
|
+
return /* @__PURE__ */ jsx(Shell, { title: "Pages", description: "Explore all public pages and filter down to the content that needs attention.", children: content });
|
|
640
719
|
}
|
|
641
|
-
function SettingsPage() {
|
|
720
|
+
function SettingsPage({ embedded = false }) {
|
|
642
721
|
const [draft, setDraft] = React.useState(EMPTY_CONFIG);
|
|
643
722
|
const [storedConfig, setStoredConfig] = React.useState({
|
|
644
723
|
siteOrigin: "",
|
|
@@ -792,70 +871,108 @@ function SettingsPage() {
|
|
|
792
871
|
setBusy(null);
|
|
793
872
|
}
|
|
794
873
|
};
|
|
795
|
-
|
|
874
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
875
|
+
/* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
876
|
+
/* @__PURE__ */ jsx(SuccessBanner, { message: success }),
|
|
877
|
+
/* @__PURE__ */ jsxs(Section, { title: "Google Connection", children: [
|
|
878
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
879
|
+
/* @__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 })) }) }),
|
|
880
|
+
/* @__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 })) }) }),
|
|
881
|
+
/* @__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 })) }) }) }),
|
|
882
|
+
/* @__PURE__ */ jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsx(
|
|
883
|
+
Field,
|
|
884
|
+
{
|
|
885
|
+
label: "Service Account JSON",
|
|
886
|
+
hint: hasStoredServiceAccount ? `Current: ${storedServiceAccountEmail || "configured"}. Leave blank to keep the current secret.` : "Required on the first save.",
|
|
887
|
+
children: /* @__PURE__ */ jsx(
|
|
888
|
+
TextArea,
|
|
889
|
+
{
|
|
890
|
+
value: draft.serviceAccountJson,
|
|
891
|
+
onChange: (value) => setDraft((current) => ({ ...current, serviceAccountJson: value })),
|
|
892
|
+
placeholder: '{"client_email":"...","private_key":"..."}',
|
|
893
|
+
rows: 12
|
|
894
|
+
}
|
|
895
|
+
)
|
|
896
|
+
}
|
|
897
|
+
) })
|
|
898
|
+
] }),
|
|
899
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex flex-wrap gap-3 border-t border-border pt-4", children: [
|
|
900
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void save(), disabled: !!busy, children: busy === "save" ? "Saving..." : "Save Settings" }),
|
|
901
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void testConnection(), disabled: !!busy, children: busy === "test" ? "Testing..." : "Test Connection" }),
|
|
902
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => void syncNow(), disabled: !!busy, children: busy === "sync" ? "Syncing..." : "Run Manual Sync" })
|
|
903
|
+
] })
|
|
904
|
+
] }),
|
|
905
|
+
/* @__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: [
|
|
906
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]", children: [
|
|
907
|
+
/* @__PURE__ */ jsx(Field, { label: "New key label", children: /* @__PURE__ */ jsx(Input, { value: newKeyLabel, onChange: setNewKeyLabel, placeholder: "content-feedback-agent" }) }),
|
|
908
|
+
/* @__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" }) })
|
|
909
|
+
] }),
|
|
910
|
+
generatedKey ? /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-lg border border-amber-200 bg-amber-50 p-4", children: [
|
|
911
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-amber-900", children: "Generated Key" }),
|
|
912
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 break-all font-mono text-sm text-amber-900", children: generatedKey })
|
|
913
|
+
] }) : null,
|
|
914
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full text-sm", children: [
|
|
915
|
+
/* @__PURE__ */ jsx("thead", { className: "text-left text-xs uppercase tracking-[0.16em] text-muted-foreground", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
916
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Prefix" }),
|
|
917
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Label" }),
|
|
918
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Created" }),
|
|
919
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Last Used" }),
|
|
920
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4", children: "Status" }),
|
|
921
|
+
/* @__PURE__ */ jsx("th", { className: "pb-3 pr-4" })
|
|
922
|
+
] }) }),
|
|
923
|
+
/* @__PURE__ */ jsx("tbody", { children: keys.map((key) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/80", children: [
|
|
924
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4 font-mono text-xs", children: key.prefix }),
|
|
925
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: key.label }),
|
|
926
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: formatDateTime(key.createdAt) }),
|
|
927
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: formatDateTime(key.lastUsedAt) }),
|
|
928
|
+
/* @__PURE__ */ jsx("td", { className: "py-3 pr-4", children: key.revokedAt ? "Revoked" : "Active" }),
|
|
929
|
+
/* @__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" }) })
|
|
930
|
+
] }, key.prefix)) })
|
|
931
|
+
] }) })
|
|
932
|
+
] })
|
|
933
|
+
] });
|
|
934
|
+
if (embedded) {
|
|
935
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
936
|
+
/* @__PURE__ */ jsx(
|
|
937
|
+
InlineHeader,
|
|
938
|
+
{
|
|
939
|
+
title: "Settings",
|
|
940
|
+
description: "Manage Google connection settings, manual sync, and agent API keys."
|
|
941
|
+
}
|
|
942
|
+
),
|
|
943
|
+
content
|
|
944
|
+
] });
|
|
945
|
+
}
|
|
946
|
+
return /* @__PURE__ */ jsx(
|
|
796
947
|
Shell,
|
|
797
948
|
{
|
|
798
949
|
title: "Analytics",
|
|
799
950
|
description: "Manage Google connection settings, manual sync, and agent API keys.",
|
|
951
|
+
children: content
|
|
952
|
+
}
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
function AnalyticsPage() {
|
|
956
|
+
const [section, setSection] = React.useState(sectionFromLocation);
|
|
957
|
+
React.useEffect(() => {
|
|
958
|
+
const handlePopstate = () => setSection(sectionFromLocation());
|
|
959
|
+
window.addEventListener("popstate", handlePopstate);
|
|
960
|
+
return () => window.removeEventListener("popstate", handlePopstate);
|
|
961
|
+
}, []);
|
|
962
|
+
const selectSection = (nextSection) => {
|
|
963
|
+
setSection(nextSection);
|
|
964
|
+
updateSectionInUrl(nextSection);
|
|
965
|
+
};
|
|
966
|
+
return /* @__PURE__ */ jsxs(
|
|
967
|
+
Shell,
|
|
968
|
+
{
|
|
969
|
+
title: "Analytics",
|
|
970
|
+
description: "Use one workspace for dashboard review, page drilldown, and connection management.",
|
|
971
|
+
actions: /* @__PURE__ */ jsx(AnalyticsTabs, { section, onChange: selectSection }),
|
|
800
972
|
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
|
-
] })
|
|
973
|
+
section === "dashboard" ? /* @__PURE__ */ jsx(OverviewPage, { embedded: true }) : null,
|
|
974
|
+
section === "pages" ? /* @__PURE__ */ jsx(PagesPage, { embedded: true }) : null,
|
|
975
|
+
section === "settings" ? /* @__PURE__ */ jsx(SettingsPage, { embedded: true }) : null
|
|
859
976
|
]
|
|
860
977
|
}
|
|
861
978
|
);
|
|
@@ -1004,7 +1121,7 @@ function numberValue(value) {
|
|
|
1004
1121
|
return typeof value === "number" ? value : 0;
|
|
1005
1122
|
}
|
|
1006
1123
|
var pages = {
|
|
1007
|
-
"/":
|
|
1124
|
+
"/": AnalyticsPage,
|
|
1008
1125
|
"/pages": PagesPage,
|
|
1009
1126
|
"/settings": SettingsPage
|
|
1010
1127
|
};
|
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" }
|