@agent-native/core 0.77.24 → 0.78.1
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +20 -0
- package/corpus/core/docs/content/template-analytics.md +5 -0
- package/corpus/core/docs/content/tracking.md +45 -0
- package/corpus/core/package.json +3 -1
- package/corpus/core/src/agent/types.ts +22 -0
- package/corpus/core/src/client/AgentPanel.tsx +34 -18
- package/corpus/core/src/client/AssistantChat.tsx +40 -0
- package/corpus/core/src/client/agent-chat.ts +173 -0
- package/corpus/core/src/client/analytics.ts +141 -5
- package/corpus/core/src/client/composer/TiptapComposer.tsx +316 -60
- package/corpus/core/src/client/composer/extensions/MentionReference.tsx +3 -0
- package/corpus/core/src/client/composer/types.ts +22 -0
- package/corpus/core/src/client/index.ts +14 -0
- package/corpus/core/src/client/session-replay.ts +800 -0
- package/corpus/core/src/db/schema.ts +12 -0
- package/corpus/core/src/deploy/build.ts +75 -0
- package/corpus/core/src/private-blob/index.ts +18 -0
- package/corpus/core/src/private-blob/registry.ts +241 -0
- package/corpus/core/src/private-blob/types.ts +55 -0
- package/corpus/core/src/scripts/utils.ts +34 -6
- package/corpus/core/src/server/agent-chat-plugin.ts +10 -0
- package/corpus/core/src/server/email.ts +5 -0
- package/corpus/core/src/server/index.ts +1 -0
- package/corpus/core/src/styles/agent-native.css +24 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +1 -1
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +43 -0
- package/corpus/templates/analytics/AGENTS.md +32 -1
- package/corpus/templates/analytics/DEVELOPING.md +9 -1
- package/corpus/templates/analytics/actions/delete-dashboard-report-subscription.ts +24 -0
- package/corpus/templates/analytics/actions/get-session-replay-events.ts +50 -0
- package/corpus/templates/analytics/actions/get-session-replay-summary.ts +27 -0
- package/corpus/templates/analytics/actions/list-dashboard-report-subscriptions.ts +24 -0
- package/corpus/templates/analytics/actions/list-session-recordings.ts +59 -0
- package/corpus/templates/analytics/actions/navigate.ts +15 -5
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -2
- package/corpus/templates/analytics/actions/save-dashboard-report-subscription.ts +36 -0
- package/corpus/templates/analytics/actions/send-dashboard-report-now.ts +60 -0
- package/corpus/templates/analytics/actions/view-screen.ts +50 -2
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +51 -2
- package/corpus/templates/analytics/app/components/layout/Header.tsx +4 -1
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +14 -2
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +6 -6
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +35 -6
- package/corpus/templates/analytics/app/i18n-data.ts +1119 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +437 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +11 -12
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +43 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +136 -43
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-filters.ts +26 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +450 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +419 -0
- package/corpus/templates/analytics/app/routes/overview.tsx +13 -6
- package/corpus/templates/analytics/app/routes/sessions.$recordingId.tsx +10 -0
- package/corpus/templates/analytics/app/routes/sessions._index.tsx +10 -0
- package/corpus/templates/analytics/changelog/2026-06-25-analytics-now-opens-directly-to-ask-instead-of-the-old-overv.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-25-dashboard-charts-now-show-reliable-blue-placement-lines-whil.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboards-can-now-send-scheduled-daily-email-reports-from-t.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-session-replay-browser-sessions-can-now-be-reviewed-from.md +6 -0
- package/corpus/templates/analytics/package.json +5 -2
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +131 -0
- package/corpus/templates/analytics/scripts/seed-session-replay.ts +198 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +140 -27
- package/corpus/templates/analytics/server/db/index.ts +12 -0
- package/corpus/templates/analytics/server/db/schema.ts +131 -0
- package/corpus/templates/analytics/server/handlers/session-replay.ts +281 -0
- package/corpus/templates/analytics/server/handlers/sql-query.ts +8 -348
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +66 -0
- package/corpus/templates/analytics/server/jobs/session-replay-retention.ts +29 -0
- package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +365 -0
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +473 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +405 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +63 -18
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +98 -26
- package/corpus/templates/analytics/server/lib/session-replay.ts +1645 -0
- package/corpus/templates/analytics/server/plugins/auth.ts +1 -1
- package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +47 -0
- package/corpus/templates/analytics/server/plugins/db.ts +200 -0
- package/corpus/templates/analytics/server/plugins/session-replay-retention-jobs.ts +41 -0
- package/corpus/templates/analytics/server/routes/api/analytics/replay.options.ts +1 -0
- package/corpus/templates/analytics/server/routes/api/analytics/replay.post.ts +1 -0
- package/corpus/templates/analytics/server/routes/api/dashboard-reports/run.post.ts +61 -0
- package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks/[seq].get.ts +1 -0
- package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/events.get.ts +1 -0
- package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/manifest.get.ts +1 -0
- package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId].get.ts +1 -0
- package/corpus/templates/analytics/server/routes/api/session-replay/recordings.get.ts +1 -0
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +10 -2
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +14 -7
- package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +12 -0
- package/corpus/templates/assets/AGENTS.md +18 -3
- package/corpus/templates/assets/actions/_image-model-default.ts +24 -0
- package/corpus/templates/assets/actions/dismiss-variant-slots.ts +15 -22
- package/corpus/templates/assets/actions/generate-asset.ts +30 -23
- package/corpus/templates/assets/actions/generate-image-batch.ts +76 -29
- package/corpus/templates/assets/actions/generate-image.ts +46 -30
- package/corpus/templates/assets/actions/generate-video.ts +18 -3
- package/corpus/templates/assets/actions/list-assets.ts +39 -8
- package/corpus/templates/assets/actions/list-libraries.ts +24 -2
- package/corpus/templates/assets/actions/navigate.ts +1 -1
- package/corpus/templates/assets/actions/open-asset-picker.ts +38 -1
- package/corpus/templates/assets/actions/refresh-generation-run.ts +10 -0
- package/corpus/templates/assets/actions/save-generated-image.ts +21 -19
- package/corpus/templates/assets/actions/variant-slots.ts +131 -28
- package/corpus/templates/assets/actions/view-screen.ts +9 -0
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +425 -0
- package/corpus/templates/assets/app/components/layout/Header.tsx +3 -1
- package/corpus/templates/assets/app/components/layout/Layout.tsx +5 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -6
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +47 -21
- package/corpus/templates/assets/app/i18n-data.ts +1815 -0
- package/corpus/templates/assets/app/lib/libraries.ts +6 -0
- package/corpus/templates/assets/app/lib/picker-chat-handoff.ts +71 -0
- package/corpus/templates/assets/app/routes/_index.tsx +4 -0
- package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
- package/corpus/templates/assets/app/routes/audit.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1557 -2164
- package/corpus/templates/assets/app/routes/brand-kits._index.tsx +7 -191
- package/corpus/templates/assets/app/routes/brand-kits.tsx +1 -5
- package/corpus/templates/assets/app/routes/libraries.tsx +4 -4
- package/corpus/templates/assets/app/routes/library.$id.tsx +9 -7
- package/corpus/templates/assets/app/routes/library.tsx +1454 -175
- package/corpus/templates/assets/server/db/index.ts +1 -1
- package/corpus/templates/assets/server/lib/generation.ts +86 -15
- package/corpus/templates/assets/server/plugins/agent-chat.ts +196 -0
- package/corpus/templates/assets/server/plugins/db.ts +5 -0
- package/corpus/templates/assets/shared/api.ts +6 -3
- package/dist/agent/types.d.ts +21 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +6 -2
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +10 -4
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +15 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +32 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +113 -0
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/analytics.d.ts +22 -3
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +96 -4
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +2 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +228 -56
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/extensions/MentionReference.d.ts.map +1 -1
- package/dist/client/composer/extensions/MentionReference.js +3 -0
- package/dist/client/composer/extensions/MentionReference.js.map +1 -1
- package/dist/client/composer/types.d.ts +21 -0
- package/dist/client/composer/types.d.ts.map +1 -1
- package/dist/client/composer/types.js.map +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/session-replay.d.ts +41 -0
- package/dist/client/session-replay.d.ts.map +1 -0
- package/dist/client/session-replay.js +595 -0
- package/dist/client/session-replay.js.map +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/schema.d.ts +3 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +6 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/deploy/build.d.ts +8 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +66 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +8 -8
- package/dist/private-blob/index.d.ts +3 -0
- package/dist/private-blob/index.d.ts.map +1 -0
- package/dist/private-blob/index.js +2 -0
- package/dist/private-blob/index.js.map +1 -0
- package/dist/private-blob/registry.d.ts +10 -0
- package/dist/private-blob/registry.d.ts.map +1 -0
- package/dist/private-blob/registry.js +152 -0
- package/dist/private-blob/registry.js.map +1 -0
- package/dist/private-blob/types.d.ts +51 -0
- package/dist/private-blob/types.d.ts.map +1 -0
- package/dist/private-blob/types.js +2 -0
- package/dist/private-blob/types.js.map +1 -0
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/scripts/utils.d.ts +4 -4
- package/dist/scripts/utils.d.ts.map +1 -1
- package/dist/scripts/utils.js +30 -6
- package/dist/scripts/utils.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +5 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/server/email.d.ts +2 -0
- package/dist/server/email.d.ts.map +1 -1
- package/dist/server/email.js +3 -0
- package/dist/server/email.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +24 -0
- package/docs/content/template-analytics.md +5 -0
- package/docs/content/tracking.md +45 -0
- package/package.json +3 -1
- package/corpus/templates/analytics/app/pages/overview/OverviewPage.tsx +0 -209
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type CollabUser,
|
|
15
15
|
} from "@agent-native/core/client";
|
|
16
16
|
import {
|
|
17
|
+
closestCenter,
|
|
17
18
|
useDroppable,
|
|
18
19
|
DndContext,
|
|
19
20
|
pointerWithin,
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
useSensors,
|
|
23
24
|
type DragEndEvent,
|
|
24
25
|
type DragOverEvent,
|
|
25
|
-
type
|
|
26
|
+
type CollisionDetection,
|
|
26
27
|
} from "@dnd-kit/core";
|
|
27
28
|
import {
|
|
28
29
|
IconArchive,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
IconEye,
|
|
32
33
|
IconEyeOff,
|
|
33
34
|
IconInfoCircle,
|
|
35
|
+
IconMail,
|
|
34
36
|
IconPencil,
|
|
35
37
|
IconPlus,
|
|
36
38
|
IconTrash,
|
|
@@ -101,6 +103,7 @@ import { DashboardSkeleton } from "../DashboardSkeleton";
|
|
|
101
103
|
import {
|
|
102
104
|
buildDashboardPanelGroups,
|
|
103
105
|
dropSlotId,
|
|
106
|
+
isDropSlotAvailable,
|
|
104
107
|
movePanelToDropSlot,
|
|
105
108
|
readDropSlot,
|
|
106
109
|
removePanelFromLayout,
|
|
@@ -110,8 +113,10 @@ import {
|
|
|
110
113
|
import {
|
|
111
114
|
DashboardFilterBar,
|
|
112
115
|
FILTER_PARAM_PREFIX,
|
|
116
|
+
extractFilterParams,
|
|
113
117
|
resolveFilterVars,
|
|
114
118
|
} from "./DashboardFilterBar";
|
|
119
|
+
import { EmailReportDialog } from "./EmailReportDialog";
|
|
115
120
|
import { interpolate } from "./interpolate";
|
|
116
121
|
import { serializePanelSql } from "./panel-sql";
|
|
117
122
|
import { AddPanelPopover, PanelEditorDialog } from "./PanelEditorDialog";
|
|
@@ -289,6 +294,7 @@ export default function SqlDashboardPage() {
|
|
|
289
294
|
const queryClient = useQueryClient();
|
|
290
295
|
const navigate = useNavigate();
|
|
291
296
|
const dashboardId = searchParams.get("id") || routeId;
|
|
297
|
+
const reportScreenshot = searchParams.get("reportScreenshot") === "1";
|
|
292
298
|
|
|
293
299
|
const [dashboard, setDashboard] = useState<SqlDashboardConfig | null>(null);
|
|
294
300
|
const [archivedAt, setArchivedAt] = useState<string | null>(null);
|
|
@@ -310,12 +316,12 @@ export default function SqlDashboardPage() {
|
|
|
310
316
|
const [descriptionInput, setDescriptionInput] = useState("");
|
|
311
317
|
const [loaded, setLoaded] = useState(false);
|
|
312
318
|
const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);
|
|
313
|
-
const [
|
|
319
|
+
const [emailReportOpen, setEmailReportOpen] = useState(false);
|
|
314
320
|
const [activeDropSlot, setActiveDropSlot] =
|
|
315
321
|
useState<DashboardDropSlot | null>(null);
|
|
316
322
|
const viewedDashboardIdRef = useRef<string | null>(null);
|
|
317
|
-
const canEdit = resourceCanEdit(resourceAccess);
|
|
318
|
-
const canManage = resourceCanManage(resourceAccess);
|
|
323
|
+
const canEdit = !reportScreenshot && resourceCanEdit(resourceAccess);
|
|
324
|
+
const canManage = !reportScreenshot && resourceCanManage(resourceAccess);
|
|
319
325
|
const dashboardColumns = clampDashboardColumns(
|
|
320
326
|
dashboard?.columns ?? DEFAULT_DASHBOARD_COLUMNS,
|
|
321
327
|
);
|
|
@@ -378,15 +384,17 @@ export default function SqlDashboardPage() {
|
|
|
378
384
|
|
|
379
385
|
// ── Collaborative editing ──────────────────────────────────────────
|
|
380
386
|
const { session } = useSession();
|
|
381
|
-
const currentUser: CollabUser | undefined =
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
387
|
+
const currentUser: CollabUser | undefined =
|
|
388
|
+
!reportScreenshot && session?.email
|
|
389
|
+
? {
|
|
390
|
+
name: emailToName(session.email),
|
|
391
|
+
email: session.email,
|
|
392
|
+
color: emailToColor(session.email),
|
|
393
|
+
}
|
|
394
|
+
: undefined;
|
|
388
395
|
|
|
389
|
-
const collabDocId =
|
|
396
|
+
const collabDocId =
|
|
397
|
+
!reportScreenshot && dashboardId ? `dash-${dashboardId}` : null;
|
|
390
398
|
const {
|
|
391
399
|
ydoc,
|
|
392
400
|
awareness,
|
|
@@ -506,15 +514,31 @@ export default function SqlDashboardPage() {
|
|
|
506
514
|
: null,
|
|
507
515
|
);
|
|
508
516
|
setLoaded(true);
|
|
509
|
-
if (
|
|
517
|
+
if (
|
|
518
|
+
fetched &&
|
|
519
|
+
!reportScreenshot &&
|
|
520
|
+
viewedDashboardIdRef.current !== dashboardId
|
|
521
|
+
) {
|
|
510
522
|
viewedDashboardIdRef.current = dashboardId;
|
|
511
523
|
incrementItemView("dashboard", dashboardId);
|
|
512
524
|
}
|
|
513
|
-
}, [
|
|
525
|
+
}, [
|
|
526
|
+
dashboardId,
|
|
527
|
+
dashboardQuery.data,
|
|
528
|
+
dashboardQuery.isSuccess,
|
|
529
|
+
reportScreenshot,
|
|
530
|
+
]);
|
|
514
531
|
|
|
515
532
|
// Apply saved filters on initial load if no filter URL params are present
|
|
516
533
|
useEffect(() => {
|
|
517
|
-
if (
|
|
534
|
+
if (
|
|
535
|
+
reportScreenshot ||
|
|
536
|
+
appliedSaved.current ||
|
|
537
|
+
filtersLoading ||
|
|
538
|
+
!loaded ||
|
|
539
|
+
!dashboard
|
|
540
|
+
)
|
|
541
|
+
return;
|
|
518
542
|
appliedSaved.current = true;
|
|
519
543
|
|
|
520
544
|
// Check if there's a view param — if so, load view filters
|
|
@@ -558,6 +582,7 @@ export default function SqlDashboardPage() {
|
|
|
558
582
|
loaded,
|
|
559
583
|
dashboard,
|
|
560
584
|
savedFilters,
|
|
585
|
+
reportScreenshot,
|
|
561
586
|
searchParams,
|
|
562
587
|
setSearchParams,
|
|
563
588
|
]);
|
|
@@ -565,7 +590,13 @@ export default function SqlDashboardPage() {
|
|
|
565
590
|
// Auto-save filter state when URL params change (debounced)
|
|
566
591
|
const saveTimer = useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
567
592
|
useEffect(() => {
|
|
568
|
-
if (
|
|
593
|
+
if (
|
|
594
|
+
reportScreenshot ||
|
|
595
|
+
!loaded ||
|
|
596
|
+
!dashboard?.filters?.length ||
|
|
597
|
+
!dashboardId
|
|
598
|
+
)
|
|
599
|
+
return;
|
|
569
600
|
clearTimeout(saveTimer.current);
|
|
570
601
|
saveTimer.current = setTimeout(() => {
|
|
571
602
|
const currentFilters: Record<string, string> = {};
|
|
@@ -577,7 +608,14 @@ export default function SqlDashboardPage() {
|
|
|
577
608
|
saveFilterPref({ filters: currentFilters });
|
|
578
609
|
}, 1500);
|
|
579
610
|
return () => clearTimeout(saveTimer.current);
|
|
580
|
-
}, [
|
|
611
|
+
}, [
|
|
612
|
+
searchParams,
|
|
613
|
+
loaded,
|
|
614
|
+
dashboard?.filters,
|
|
615
|
+
dashboardId,
|
|
616
|
+
saveFilterPref,
|
|
617
|
+
reportScreenshot,
|
|
618
|
+
]);
|
|
581
619
|
|
|
582
620
|
/**
|
|
583
621
|
* Push a config update through the collab layer so other tabs/users
|
|
@@ -718,24 +756,20 @@ export default function SqlDashboardPage() {
|
|
|
718
756
|
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
|
719
757
|
);
|
|
720
758
|
|
|
721
|
-
const handleDragStart = useCallback((event: DragStartEvent) => {
|
|
722
|
-
setDraggingPanelId(String(event.active.id));
|
|
723
|
-
}, []);
|
|
724
|
-
|
|
725
759
|
const handleDragOver = useCallback((event: DragOverEvent) => {
|
|
726
760
|
setActiveDropSlot(readDropSlot(event.over?.data.current));
|
|
727
761
|
}, []);
|
|
728
762
|
|
|
729
763
|
const handleDragEnd = useCallback(
|
|
730
764
|
(event: DragEndEvent) => {
|
|
731
|
-
setDraggingPanelId(null);
|
|
732
765
|
setActiveDropSlot(null);
|
|
733
766
|
if (!dashboard) return;
|
|
734
767
|
const slot = readDropSlot(event.over?.data.current);
|
|
735
768
|
if (!slot) return;
|
|
769
|
+
const panelId = String(event.active.id);
|
|
736
770
|
const nextPanels = movePanelToDropSlot(
|
|
737
771
|
dashboard.panels,
|
|
738
|
-
|
|
772
|
+
panelId,
|
|
739
773
|
slot,
|
|
740
774
|
dashboardColumns,
|
|
741
775
|
);
|
|
@@ -749,7 +783,6 @@ export default function SqlDashboardPage() {
|
|
|
749
783
|
);
|
|
750
784
|
|
|
751
785
|
const handleDragCancel = useCallback(() => {
|
|
752
|
-
setDraggingPanelId(null);
|
|
753
786
|
setActiveDropSlot(null);
|
|
754
787
|
}, []);
|
|
755
788
|
|
|
@@ -780,6 +813,15 @@ export default function SqlDashboardPage() {
|
|
|
780
813
|
return { ...(dashboard?.variables ?? {}), ...filterValues };
|
|
781
814
|
}, [dashboard?.variables, dashboard?.filters, searchParams]);
|
|
782
815
|
|
|
816
|
+
const currentReportFilters = useMemo<Record<string, string>>(() => {
|
|
817
|
+
const out = dashboard?.filters
|
|
818
|
+
? extractFilterParams(dashboard.filters, searchParams)
|
|
819
|
+
: {};
|
|
820
|
+
const tab = searchParams.get("tab");
|
|
821
|
+
if (tab) out.tab = tab;
|
|
822
|
+
return out;
|
|
823
|
+
}, [dashboard?.filters, searchParams]);
|
|
824
|
+
|
|
783
825
|
// Distinct tab values across panels in declaration order. When this is
|
|
784
826
|
// non-empty the dashboard renders a tab strip and filters panels by tab.
|
|
785
827
|
const tabs = useMemo<string[]>(() => {
|
|
@@ -848,6 +890,34 @@ export default function SqlDashboardPage() {
|
|
|
848
890
|
return buildDashboardPanelGroups(visiblePanels, dashboardColumns);
|
|
849
891
|
}, [visiblePanels, dashboardColumns]);
|
|
850
892
|
|
|
893
|
+
const dashboardCollisionDetection = useCallback<CollisionDetection>(
|
|
894
|
+
(args) => {
|
|
895
|
+
const activePanelId = String(args.active.id);
|
|
896
|
+
const droppableContainers = args.droppableContainers.filter(
|
|
897
|
+
(container) => {
|
|
898
|
+
const slot = readDropSlot(container.data.current);
|
|
899
|
+
return (
|
|
900
|
+
!!slot && isDropSlotAvailable(panelGroups, activePanelId, slot)
|
|
901
|
+
);
|
|
902
|
+
},
|
|
903
|
+
);
|
|
904
|
+
|
|
905
|
+
if (droppableContainers.length === 0) return [];
|
|
906
|
+
|
|
907
|
+
const exactCollisions = pointerWithin({
|
|
908
|
+
...args,
|
|
909
|
+
droppableContainers,
|
|
910
|
+
});
|
|
911
|
+
if (exactCollisions.length > 0) return exactCollisions;
|
|
912
|
+
|
|
913
|
+
return closestCenter({
|
|
914
|
+
...args,
|
|
915
|
+
droppableContainers,
|
|
916
|
+
});
|
|
917
|
+
},
|
|
918
|
+
[panelGroups],
|
|
919
|
+
);
|
|
920
|
+
|
|
851
921
|
const handleDelete = useCallback(async () => {
|
|
852
922
|
if (!dashboardId) return;
|
|
853
923
|
if (!canManage) return;
|
|
@@ -945,7 +1015,7 @@ export default function SqlDashboardPage() {
|
|
|
945
1015
|
);
|
|
946
1016
|
|
|
947
1017
|
useSetPageTitle(
|
|
948
|
-
dashboard ? (
|
|
1018
|
+
reportScreenshot ? null : dashboard ? (
|
|
949
1019
|
<div className="flex items-center gap-2 min-w-0">
|
|
950
1020
|
{editingName && canEdit ? (
|
|
951
1021
|
<Input
|
|
@@ -979,7 +1049,7 @@ export default function SqlDashboardPage() {
|
|
|
979
1049
|
);
|
|
980
1050
|
|
|
981
1051
|
useSetHeaderActions(
|
|
982
|
-
dashboard ? (
|
|
1052
|
+
reportScreenshot ? null : dashboard ? (
|
|
983
1053
|
<>
|
|
984
1054
|
<PresenceBar
|
|
985
1055
|
activeUsers={activeUsers}
|
|
@@ -1088,6 +1158,20 @@ export default function SqlDashboardPage() {
|
|
|
1088
1158
|
{(canEdit && !archivedAt) || canManage ? (
|
|
1089
1159
|
<DropdownMenuSeparator />
|
|
1090
1160
|
) : null}
|
|
1161
|
+
{dashboardId ? (
|
|
1162
|
+
<>
|
|
1163
|
+
<DropdownMenuSeparator />
|
|
1164
|
+
<DropdownMenuItem
|
|
1165
|
+
onSelect={(event) => {
|
|
1166
|
+
event.preventDefault();
|
|
1167
|
+
setEmailReportOpen(true);
|
|
1168
|
+
}}
|
|
1169
|
+
>
|
|
1170
|
+
<IconMail className="mr-2 h-3.5 w-3.5" />
|
|
1171
|
+
{t("sqlDashboard.emailReports")}
|
|
1172
|
+
</DropdownMenuItem>
|
|
1173
|
+
</>
|
|
1174
|
+
) : null}
|
|
1091
1175
|
{canEdit && !archivedAt ? (
|
|
1092
1176
|
<DropdownMenuItem
|
|
1093
1177
|
onSelect={(event) => {
|
|
@@ -1116,6 +1200,15 @@ export default function SqlDashboardPage() {
|
|
|
1116
1200
|
) : null}
|
|
1117
1201
|
</DropdownMenuContent>
|
|
1118
1202
|
</DropdownMenu>
|
|
1203
|
+
{dashboardId ? (
|
|
1204
|
+
<EmailReportDialog
|
|
1205
|
+
open={emailReportOpen}
|
|
1206
|
+
onOpenChange={setEmailReportOpen}
|
|
1207
|
+
dashboardId={dashboardId}
|
|
1208
|
+
dashboardName={dashboard.name}
|
|
1209
|
+
filters={currentReportFilters}
|
|
1210
|
+
/>
|
|
1211
|
+
) : null}
|
|
1119
1212
|
{canManage ? (
|
|
1120
1213
|
<AlertDialog
|
|
1121
1214
|
open={confirmDeleteOpen}
|
|
@@ -1163,7 +1256,11 @@ export default function SqlDashboardPage() {
|
|
|
1163
1256
|
if (!dashboard) return <BlankDashboard />;
|
|
1164
1257
|
|
|
1165
1258
|
return (
|
|
1166
|
-
<div
|
|
1259
|
+
<div
|
|
1260
|
+
className="space-y-4"
|
|
1261
|
+
data-dashboard-report-capture
|
|
1262
|
+
data-dashboard-report-ready={loaded && dashboard ? "true" : "false"}
|
|
1263
|
+
>
|
|
1167
1264
|
{hiddenAt ? (
|
|
1168
1265
|
<div className="flex flex-wrap items-center gap-3 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:border-amber-900/70 dark:bg-amber-950/40 dark:text-amber-200">
|
|
1169
1266
|
<IconEyeOff className="h-4 w-4 shrink-0 text-amber-600 dark:text-amber-400" />
|
|
@@ -1320,8 +1417,7 @@ export default function SqlDashboardPage() {
|
|
|
1320
1417
|
) : (
|
|
1321
1418
|
<DndContext
|
|
1322
1419
|
sensors={sensors}
|
|
1323
|
-
collisionDetection={
|
|
1324
|
-
onDragStart={canEdit ? handleDragStart : undefined}
|
|
1420
|
+
collisionDetection={dashboardCollisionDetection}
|
|
1325
1421
|
onDragOver={canEdit ? handleDragOver : undefined}
|
|
1326
1422
|
onDragEnd={canEdit ? handleDragEnd : undefined}
|
|
1327
1423
|
onDragCancel={handleDragCancel}
|
|
@@ -1341,7 +1437,9 @@ export default function SqlDashboardPage() {
|
|
|
1341
1437
|
},
|
|
1342
1438
|
}
|
|
1343
1439
|
: panel;
|
|
1344
|
-
const remoteEditor =
|
|
1440
|
+
const remoteEditor = reportScreenshot
|
|
1441
|
+
? undefined
|
|
1442
|
+
: remoteEditingPanels.get(panel.id);
|
|
1345
1443
|
return (
|
|
1346
1444
|
<div
|
|
1347
1445
|
key={panel.id}
|
|
@@ -1379,13 +1477,16 @@ export default function SqlDashboardPage() {
|
|
|
1379
1477
|
onEdit={() => openEditPanel(panel)}
|
|
1380
1478
|
onSaveSql={(sql) => handleSavePanel({ ...panel, sql })}
|
|
1381
1479
|
editable={canEdit}
|
|
1480
|
+
eagerLoad={reportScreenshot}
|
|
1382
1481
|
/>
|
|
1383
1482
|
</div>
|
|
1384
1483
|
);
|
|
1385
1484
|
};
|
|
1386
1485
|
|
|
1387
1486
|
const renderSection = (section: SqlPanel) => {
|
|
1388
|
-
const remoteEditor =
|
|
1487
|
+
const remoteEditor = reportScreenshot
|
|
1488
|
+
? undefined
|
|
1489
|
+
: remoteEditingPanels.get(section.id);
|
|
1389
1490
|
const resolved = section.config?.description
|
|
1390
1491
|
? {
|
|
1391
1492
|
...section,
|
|
@@ -1428,6 +1529,7 @@ export default function SqlDashboardPage() {
|
|
|
1428
1529
|
onRemove={() => removePanel(section.id)}
|
|
1429
1530
|
onEdit={() => openEditPanel(section)}
|
|
1430
1531
|
editable={canEdit}
|
|
1532
|
+
eagerLoad={reportScreenshot}
|
|
1431
1533
|
/>
|
|
1432
1534
|
</div>
|
|
1433
1535
|
);
|
|
@@ -1438,7 +1540,7 @@ export default function SqlDashboardPage() {
|
|
|
1438
1540
|
key={`row-slot-${group.key}-${rowIndex}`}
|
|
1439
1541
|
slot={{ type: "row", groupKey: group.key, rowIndex }}
|
|
1440
1542
|
activeSlot={activeDropSlot}
|
|
1441
|
-
disabled={!canEdit
|
|
1543
|
+
disabled={!canEdit}
|
|
1442
1544
|
/>
|
|
1443
1545
|
);
|
|
1444
1546
|
|
|
@@ -1447,15 +1549,6 @@ export default function SqlDashboardPage() {
|
|
|
1447
1549
|
{group.section && renderSection(group.section)}
|
|
1448
1550
|
{renderRowDropLine(0)}
|
|
1449
1551
|
{group.rows.map((row, rowIndex) => {
|
|
1450
|
-
const rowContainsDragging =
|
|
1451
|
-
!!draggingPanelId &&
|
|
1452
|
-
row.panels.some((panel) => panel.id === draggingPanelId);
|
|
1453
|
-
const columnDropDisabled =
|
|
1454
|
-
!canEdit ||
|
|
1455
|
-
!draggingPanelId ||
|
|
1456
|
-
(row.panels.length >= group.columns &&
|
|
1457
|
-
!rowContainsDragging);
|
|
1458
|
-
|
|
1459
1552
|
return (
|
|
1460
1553
|
<Fragment key={row.key}>
|
|
1461
1554
|
<div
|
|
@@ -1474,7 +1567,7 @@ export default function SqlDashboardPage() {
|
|
|
1474
1567
|
columnIndex: 0,
|
|
1475
1568
|
}}
|
|
1476
1569
|
activeSlot={activeDropSlot}
|
|
1477
|
-
disabled={
|
|
1570
|
+
disabled={!canEdit}
|
|
1478
1571
|
/>
|
|
1479
1572
|
{row.panels.map((panel, columnIndex) => (
|
|
1480
1573
|
<Fragment key={panel.id}>
|
|
@@ -1487,7 +1580,7 @@ export default function SqlDashboardPage() {
|
|
|
1487
1580
|
columnIndex: columnIndex + 1,
|
|
1488
1581
|
}}
|
|
1489
1582
|
activeSlot={activeDropSlot}
|
|
1490
|
-
disabled={
|
|
1583
|
+
disabled={!canEdit}
|
|
1491
1584
|
/>
|
|
1492
1585
|
</Fragment>
|
|
1493
1586
|
))}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function normalizeReportFilterSnapshot(
|
|
2
|
+
filters: Record<string, string>,
|
|
3
|
+
): Record<string, string> {
|
|
4
|
+
const out: Record<string, string> = {};
|
|
5
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
6
|
+
const k = key.trim();
|
|
7
|
+
const v = String(value ?? "").trim();
|
|
8
|
+
if (k && v) out[k] = v;
|
|
9
|
+
}
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function reportFilterSnapshotKey(
|
|
14
|
+
filters: Record<string, string>,
|
|
15
|
+
): string {
|
|
16
|
+
return Object.keys(filters)
|
|
17
|
+
.sort()
|
|
18
|
+
.map((key) => `${key}:${filters[key]}`)
|
|
19
|
+
.join("\n");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function savedReportFiltersForEdit(
|
|
23
|
+
savedFilters: Record<string, string>,
|
|
24
|
+
): Record<string, string> {
|
|
25
|
+
return normalizeReportFilterSnapshot(savedFilters);
|
|
26
|
+
}
|