@agent-native/core-corpus 0.210.2 → 0.211.0
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 +1 -1
- package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
- package/corpus/templates/analytics/CHANGELOG.md +18 -0
- package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
- package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
- package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
- package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
- package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
- package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
- package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
- package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
- package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
- package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
- package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
- package/corpus/templates/analytics/server/db/schema.ts +1 -0
- package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
- package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
- package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
- package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
- package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
- package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
- package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
- package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
- package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
- package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
- package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
- package/corpus/templates/analytics/server/plugins/db.ts +9 -1
- package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
- package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
- package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/actions/delete-event.ts +1 -3
- package/corpus/templates/calendar/actions/delete-events.ts +1 -2
- package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
- package/corpus/templates/calendar/actions/update-event.ts +2 -6
- package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/CHANGELOG.md +11 -24
- package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
- package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
- package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
- package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
- package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
- package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
- package/corpus/templates/clips/app/root.tsx +22 -10
- package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
- package/corpus/templates/clips/app/routes/import.tsx +115 -110
- package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +29 -186
- package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
- package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
- package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
- package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
- package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
- package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/content/CHANGELOG.md +12 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
- package/corpus/templates/content/actions/_database-navigation.ts +3 -3
- package/corpus/templates/content/actions/_database-utils.ts +10 -22
- package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
- package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
- package/corpus/templates/content/actions/export-document.ts +2 -14
- package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
- package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
- package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
- package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
- package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
- package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
- package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
- package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
- package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
- package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
- package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
- package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
- package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
- package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
- package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
- package/corpus/templates/content/app/global.css +23 -0
- package/corpus/templates/content/app/i18n-data.ts +1 -0
- package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
- package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-edits-made-from-a-block-s-menu-or-a-table-s-row-and-column-m.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
- package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
- package/corpus/templates/content/e2e/helpers.ts +10 -0
- package/corpus/templates/content/shared/api.ts +18 -12
- package/corpus/templates/content/shared/database-query.ts +107 -13
- package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
- package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/CHANGELOG.md +2 -0
- package/corpus/templates/design/actions/delete-file.ts +1 -4
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
- package/corpus/templates/design/changelog/2026-10-19-reply-and-edit-on-a-comment-in-the-sidebar-or-on-a-canvas-pi.md +6 -0
- package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +185 -85
- package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
- package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
- package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
- package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
- package/corpus/templates/mail/AGENTS.md +1 -1
- package/corpus/templates/mail/CHANGELOG.md +14 -0
- package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
- package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
- package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
- package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
- package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
- package/corpus/templates/mail/app/root.tsx +10 -2
- package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +9 -0
- package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/plan/CHANGELOG.md +4 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
- package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
- package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
- package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
- package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
- package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
- package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { agentNativePath } from "@agent-native/core/client/api-path";
|
|
1
|
+
import { agentNativePath, appPath } from "@agent-native/core/client/api-path";
|
|
2
2
|
import {
|
|
3
3
|
useActionMutation,
|
|
4
4
|
useActionQuery,
|
|
5
|
+
useAvatarUrl,
|
|
5
6
|
} from "@agent-native/core/client/hooks";
|
|
6
7
|
import { useT } from "@agent-native/core/client/i18n";
|
|
7
8
|
import { useOrgRole } from "@agent-native/core/client/org";
|
|
@@ -9,7 +10,6 @@ import {
|
|
|
9
10
|
SIGMA_API_BASE_URLS,
|
|
10
11
|
SIGMA_DEFAULT_API_BASE_URL,
|
|
11
12
|
} from "@agent-native/core/connections/sigma-api";
|
|
12
|
-
import { useSendToAgentChat } from "@agent-native/toolkit/app/chat";
|
|
13
13
|
import { useOrgSwitcherAppLinks } from "@agent-native/toolkit/app/org/workspace-app-links";
|
|
14
14
|
import { saveApiKeyValue } from "@agent-native/toolkit/app/settings/api-keys/api-keys-client";
|
|
15
15
|
import {
|
|
@@ -17,18 +17,17 @@ import {
|
|
|
17
17
|
IconCheck,
|
|
18
18
|
IconExternalLink,
|
|
19
19
|
IconLoader2,
|
|
20
|
-
|
|
20
|
+
IconSearch,
|
|
21
21
|
} from "@tabler/icons-react";
|
|
22
22
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
23
23
|
import type { FormEvent, ReactNode } from "react";
|
|
24
|
-
import { Link } from "react-router";
|
|
25
24
|
|
|
25
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
26
26
|
import { Badge } from "@/components/ui/badge";
|
|
27
27
|
import { Button } from "@/components/ui/button";
|
|
28
28
|
import {
|
|
29
29
|
Dialog,
|
|
30
30
|
DialogContent,
|
|
31
|
-
DialogFooter,
|
|
32
31
|
DialogHeader,
|
|
33
32
|
DialogTitle,
|
|
34
33
|
} from "@/components/ui/dialog";
|
|
@@ -42,6 +41,13 @@ import {
|
|
|
42
41
|
SelectValue,
|
|
43
42
|
} from "@/components/ui/select";
|
|
44
43
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
44
|
+
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
|
|
45
|
+
import {
|
|
46
|
+
Tooltip,
|
|
47
|
+
TooltipContent,
|
|
48
|
+
TooltipProvider,
|
|
49
|
+
TooltipTrigger,
|
|
50
|
+
} from "@/components/ui/tooltip";
|
|
45
51
|
|
|
46
52
|
type Connection = {
|
|
47
53
|
id: string;
|
|
@@ -59,11 +65,16 @@ type SigmaWorkbook = {
|
|
|
59
65
|
dashboardId?: string | null;
|
|
60
66
|
dashboardName?: string;
|
|
61
67
|
dashboardUrl?: string;
|
|
68
|
+
canOpen?: boolean;
|
|
69
|
+
migratedOwnerEmail?: string;
|
|
70
|
+
migratedOwnerName?: string | null;
|
|
71
|
+
migratingByEmail?: string;
|
|
72
|
+
migratingByName?: string | null;
|
|
62
73
|
canRecoverMissingDashboard?: boolean;
|
|
63
74
|
canRetryStaleClaim?: boolean;
|
|
75
|
+
canStartImport: boolean;
|
|
64
76
|
flaggedCount?: number;
|
|
65
77
|
migratedByTeammate?: boolean;
|
|
66
|
-
retryableByCurrentUser: boolean;
|
|
67
78
|
};
|
|
68
79
|
|
|
69
80
|
type SigmaWorkbookListResult = {
|
|
@@ -78,59 +89,6 @@ type SigmaWorkbookListResult = {
|
|
|
78
89
|
total?: number;
|
|
79
90
|
};
|
|
80
91
|
|
|
81
|
-
type SigmaElementReviewReason =
|
|
82
|
-
| "unsupported_element"
|
|
83
|
-
| "unsupported_source"
|
|
84
|
-
| "source_unavailable"
|
|
85
|
-
| "source_mapping_required"
|
|
86
|
-
| "query_unavailable"
|
|
87
|
-
| "unsafe_sql"
|
|
88
|
-
| "visualization_review";
|
|
89
|
-
|
|
90
|
-
type SigmaWorkbookElement = {
|
|
91
|
-
id: string;
|
|
92
|
-
name: string;
|
|
93
|
-
type: string;
|
|
94
|
-
importable: boolean;
|
|
95
|
-
visualizationPreserved: boolean;
|
|
96
|
-
reason?: SigmaElementReviewReason;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
type SigmaWorkbookInspection = {
|
|
100
|
-
workbook: { id: string; name: string; updatedAt?: string };
|
|
101
|
-
importableCount: number;
|
|
102
|
-
unsupportedCount: number;
|
|
103
|
-
reviewRequiredCount: number;
|
|
104
|
-
sourceMappingRequiredCount: number;
|
|
105
|
-
elements: SigmaWorkbookElement[];
|
|
106
|
-
betaRepresentationWarning: boolean;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
type SigmaMigrationResult =
|
|
110
|
-
| {
|
|
111
|
-
status: "completed";
|
|
112
|
-
dashboardId: string;
|
|
113
|
-
dashboardName: string;
|
|
114
|
-
dashboardUrl: string;
|
|
115
|
-
importedCount: number;
|
|
116
|
-
unsupportedCount: number;
|
|
117
|
-
reviewRequiredCount: number;
|
|
118
|
-
flaggedCount: number;
|
|
119
|
-
}
|
|
120
|
-
| {
|
|
121
|
-
status: "already_migrated";
|
|
122
|
-
dashboardId?: string;
|
|
123
|
-
dashboardName?: string;
|
|
124
|
-
dashboardUrl?: string;
|
|
125
|
-
}
|
|
126
|
-
| { status: "in_progress" };
|
|
127
|
-
|
|
128
|
-
type MigrationArgs = {
|
|
129
|
-
workbookId: string;
|
|
130
|
-
connectionId?: string;
|
|
131
|
-
recoverMissingDashboard?: boolean;
|
|
132
|
-
};
|
|
133
|
-
|
|
134
92
|
type LoadedWorkbookPage = { cursor?: string; workbooks: SigmaWorkbook[] };
|
|
135
93
|
type SigmaConnectionSaveResult = {
|
|
136
94
|
connectionId: string;
|
|
@@ -150,29 +108,25 @@ function sigmaSetupKey(
|
|
|
150
108
|
|
|
151
109
|
const EMPTY_CONNECTIONS: Connection[] = [];
|
|
152
110
|
const EMPTY_WORKBOOKS: SigmaWorkbook[] = [];
|
|
153
|
-
const SIGMA_PREVIEW_ELEMENT_LIMIT = 100;
|
|
154
111
|
|
|
155
112
|
export function SigmaMigrationDialog({
|
|
156
113
|
open,
|
|
157
114
|
onOpenChange,
|
|
158
|
-
onImported,
|
|
159
115
|
preferredConnectionId,
|
|
160
116
|
}: {
|
|
161
117
|
open: boolean;
|
|
162
118
|
onOpenChange: (open: boolean) => void;
|
|
163
|
-
onImported?: () => void;
|
|
164
119
|
preferredConnectionId?: string | null;
|
|
165
120
|
}) {
|
|
166
121
|
const t = useT();
|
|
167
122
|
const { canManageOrg } = useOrgRole();
|
|
168
123
|
const { dispatchHref, dispatchVaultHref } = useOrgSwitcherAppLinks(open);
|
|
169
|
-
const { send, isGenerating, codeRequiredDialog } = useSendToAgentChat();
|
|
170
124
|
const [connectionId, setConnectionId] = useState<string | null>(null);
|
|
171
125
|
const [sigmaApiBaseUrl, setSigmaApiBaseUrl] = useState(
|
|
172
126
|
SIGMA_DEFAULT_API_BASE_URL,
|
|
173
127
|
);
|
|
174
128
|
const [showSigmaOAuthSetup, setShowSigmaOAuthSetup] = useState(false);
|
|
175
|
-
const [
|
|
129
|
+
const [search, setSearch] = useState("");
|
|
176
130
|
const [pageCursor, setPageCursor] = useState<string | undefined>();
|
|
177
131
|
const [refreshVersion, setRefreshVersion] = useState(0);
|
|
178
132
|
const [loadedPages, setLoadedPages] = useState<{
|
|
@@ -183,12 +137,6 @@ export function SigmaMigrationDialog({
|
|
|
183
137
|
connectionId: string | null;
|
|
184
138
|
result: SigmaWorkbookListResult;
|
|
185
139
|
} | null>(null);
|
|
186
|
-
const [migrationResult, setMigrationResult] =
|
|
187
|
-
useState<SigmaMigrationResult | null>(null);
|
|
188
|
-
const [migrationError, setMigrationError] = useState<string | null>(null);
|
|
189
|
-
const [migrationInProgress, setMigrationInProgress] = useState(false);
|
|
190
|
-
const [awaitingListConfirmation, setAwaitingListConfirmation] =
|
|
191
|
-
useState(false);
|
|
192
140
|
const activePageCursor =
|
|
193
141
|
loadedPages.connectionId === connectionId ? pageCursor : undefined;
|
|
194
142
|
|
|
@@ -288,11 +236,7 @@ export function SigmaMigrationDialog({
|
|
|
288
236
|
|
|
289
237
|
useEffect(() => {
|
|
290
238
|
setPageCursor(undefined);
|
|
291
|
-
|
|
292
|
-
setMigrationResult(null);
|
|
293
|
-
setMigrationError(null);
|
|
294
|
-
setMigrationInProgress(false);
|
|
295
|
-
setAwaitingListConfirmation(false);
|
|
239
|
+
setSearch("");
|
|
296
240
|
setLoadedPages({ connectionId, pages: [] });
|
|
297
241
|
}, [connectionId]);
|
|
298
242
|
|
|
@@ -347,37 +291,13 @@ export function SigmaMigrationDialog({
|
|
|
347
291
|
}
|
|
348
292
|
return [...merged.values()];
|
|
349
293
|
}, [connectionId, loadedPages]);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const selectedWorkbookPage =
|
|
358
|
-
loadedPages.connectionId === connectionId
|
|
359
|
-
? loadedPages.pages.find((page) =>
|
|
360
|
-
page.workbooks.some((workbook) => workbook.id === workbookId),
|
|
361
|
-
)?.cursor
|
|
362
|
-
: undefined;
|
|
363
|
-
const inspection = useActionQuery<
|
|
364
|
-
SigmaWorkbookInspection,
|
|
365
|
-
"inspect-sigma-workbook"
|
|
366
|
-
>(
|
|
367
|
-
"inspect-sigma-workbook",
|
|
368
|
-
selectedWorkbook && connectionId
|
|
369
|
-
? { workbookId: selectedWorkbook.id, connectionId }
|
|
370
|
-
: undefined,
|
|
371
|
-
{
|
|
372
|
-
enabled: open && Boolean(selectedWorkbook && connectionId),
|
|
373
|
-
staleTime: 15_000,
|
|
374
|
-
},
|
|
375
|
-
);
|
|
376
|
-
const migrateWorkbook = useActionMutation<
|
|
377
|
-
SigmaMigrationResult,
|
|
378
|
-
MigrationArgs,
|
|
379
|
-
"migrate-sigma-workbook"
|
|
380
|
-
>("migrate-sigma-workbook");
|
|
294
|
+
const visibleWorkbooks = useMemo(() => {
|
|
295
|
+
const query = search.trim().toLowerCase();
|
|
296
|
+
if (!query) return workbooks;
|
|
297
|
+
return workbooks.filter((workbook) =>
|
|
298
|
+
workbook.name.toLowerCase().includes(query),
|
|
299
|
+
);
|
|
300
|
+
}, [search, workbooks]);
|
|
381
301
|
const saveSigmaConnection = useActionMutation<
|
|
382
302
|
SigmaConnectionSaveResult,
|
|
383
303
|
SigmaConnectionSaveArgs,
|
|
@@ -403,53 +323,9 @@ export function SigmaMigrationDialog({
|
|
|
403
323
|
);
|
|
404
324
|
const showConnectionPicker = connectedConnections.length > 1;
|
|
405
325
|
const needsConnectionSelection = showConnectionPicker && !connectionId;
|
|
406
|
-
const inspectionIsValid = inspection.data
|
|
407
|
-
? isWorkbookInspection(inspection.data)
|
|
408
|
-
: false;
|
|
409
|
-
const canImport =
|
|
410
|
-
Boolean(selectedWorkbook && connectionId && inspectionIsValid) &&
|
|
411
|
-
(inspection.data?.importableCount ?? 0) > 0 &&
|
|
412
|
-
!migrationInProgress &&
|
|
413
|
-
selectedWorkbook?.retryableByCurrentUser !== false &&
|
|
414
|
-
(selectedWorkbook?.status !== "migrated" ||
|
|
415
|
-
selectedWorkbook?.canRecoverMissingDashboard === true) &&
|
|
416
|
-
(selectedWorkbook?.status !== "migrating" ||
|
|
417
|
-
selectedWorkbook?.canRetryStaleClaim === true);
|
|
418
|
-
const isImported =
|
|
419
|
-
migrationResult?.status === "completed" ||
|
|
420
|
-
migrationResult?.status === "already_migrated" ||
|
|
421
|
-
selectedWorkbook?.status === "migrated";
|
|
422
|
-
const accessibleDashboardId =
|
|
423
|
-
dashboardIdFromMigrationResult(migrationResult) ??
|
|
424
|
-
(!awaitingListConfirmation && selectedWorkbook?.status === "migrated"
|
|
425
|
-
? (selectedWorkbook.dashboardId ?? null)
|
|
426
|
-
: null);
|
|
427
|
-
const accessibleDashboardUrl = safeDashboardPath(accessibleDashboardId);
|
|
428
|
-
const reviewTarget =
|
|
429
|
-
selectedWorkbook &&
|
|
430
|
-
isImported &&
|
|
431
|
-
accessibleDashboardId &&
|
|
432
|
-
accessibleDashboardUrl
|
|
433
|
-
? {
|
|
434
|
-
dashboardId: accessibleDashboardId,
|
|
435
|
-
dashboardName:
|
|
436
|
-
(migrationResult?.status === "completed" ||
|
|
437
|
-
migrationResult?.status === "already_migrated"
|
|
438
|
-
? migrationResult.dashboardName
|
|
439
|
-
: undefined) ??
|
|
440
|
-
selectedWorkbook.dashboardName ??
|
|
441
|
-
selectedWorkbook.name,
|
|
442
|
-
}
|
|
443
|
-
: null;
|
|
444
|
-
|
|
445
326
|
const handleConnectionChange = (value: string) => {
|
|
446
327
|
setConnectionId(value || null);
|
|
447
328
|
setPageCursor(undefined);
|
|
448
|
-
setWorkbookId(null);
|
|
449
|
-
setMigrationResult(null);
|
|
450
|
-
setMigrationError(null);
|
|
451
|
-
setMigrationInProgress(false);
|
|
452
|
-
setAwaitingListConfirmation(false);
|
|
453
329
|
};
|
|
454
330
|
|
|
455
331
|
const startPersonalSigmaOAuth = () => {
|
|
@@ -465,48 +341,6 @@ export function SigmaMigrationDialog({
|
|
|
465
341
|
);
|
|
466
342
|
};
|
|
467
343
|
|
|
468
|
-
const handleWorkbookChange = (value: string) => {
|
|
469
|
-
setWorkbookId(value);
|
|
470
|
-
setMigrationResult(null);
|
|
471
|
-
setMigrationError(null);
|
|
472
|
-
setMigrationInProgress(false);
|
|
473
|
-
setAwaitingListConfirmation(false);
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
const handleImport = async () => {
|
|
477
|
-
if (!selectedWorkbook || !connectionId || !canImport) return;
|
|
478
|
-
setMigrationError(null);
|
|
479
|
-
setMigrationResult(null);
|
|
480
|
-
setMigrationInProgress(false);
|
|
481
|
-
setAwaitingListConfirmation(false);
|
|
482
|
-
try {
|
|
483
|
-
const result = await migrateWorkbook.mutateAsync({
|
|
484
|
-
workbookId: selectedWorkbook.id,
|
|
485
|
-
connectionId,
|
|
486
|
-
...(selectedWorkbook.canRecoverMissingDashboard
|
|
487
|
-
? { recoverMissingDashboard: true }
|
|
488
|
-
: {}),
|
|
489
|
-
});
|
|
490
|
-
if (!isMigrationResult(result)) {
|
|
491
|
-
setMigrationError(t("dashboardOverview.sigmaMigrationResultInvalid"));
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
if (result.status === "in_progress") {
|
|
495
|
-
setMigrationInProgress(true);
|
|
496
|
-
setPageCursor(selectedWorkbookPage);
|
|
497
|
-
setRefreshVersion((current) => current + 1);
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
setMigrationResult(result);
|
|
501
|
-
setAwaitingListConfirmation(true);
|
|
502
|
-
setPageCursor(selectedWorkbookPage);
|
|
503
|
-
setRefreshVersion((current) => current + 1);
|
|
504
|
-
onImported?.();
|
|
505
|
-
} catch {
|
|
506
|
-
setMigrationError(t("dashboardOverview.sigmaMigrationFailed"));
|
|
507
|
-
}
|
|
508
|
-
};
|
|
509
|
-
|
|
510
344
|
const handleSigmaConnect = async (event: FormEvent<HTMLFormElement>) => {
|
|
511
345
|
event.preventDefault();
|
|
512
346
|
setSigmaSetupError(null);
|
|
@@ -649,76 +483,6 @@ export function SigmaMigrationDialog({
|
|
|
649
483
|
setSigmaCleanupError(null);
|
|
650
484
|
}, [open]);
|
|
651
485
|
|
|
652
|
-
useEffect(() => {
|
|
653
|
-
if (
|
|
654
|
-
!awaitingListConfirmation ||
|
|
655
|
-
!selectedWorkbook ||
|
|
656
|
-
!connectionId ||
|
|
657
|
-
workbookList.isPending ||
|
|
658
|
-
workbookList.isFetching ||
|
|
659
|
-
listData?.connectionId !== connectionId ||
|
|
660
|
-
!listData.workbooks.some(
|
|
661
|
-
(workbook) => workbook.id === selectedWorkbook.id,
|
|
662
|
-
)
|
|
663
|
-
) {
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
setAwaitingListConfirmation(false);
|
|
667
|
-
}, [
|
|
668
|
-
awaitingListConfirmation,
|
|
669
|
-
connectionId,
|
|
670
|
-
listData,
|
|
671
|
-
selectedWorkbook,
|
|
672
|
-
workbookList.isFetching,
|
|
673
|
-
workbookList.isPending,
|
|
674
|
-
]);
|
|
675
|
-
|
|
676
|
-
useEffect(() => {
|
|
677
|
-
if (
|
|
678
|
-
!migrationInProgress ||
|
|
679
|
-
!selectedWorkbook ||
|
|
680
|
-
workbookList.isFetching ||
|
|
681
|
-
listData?.connectionId !== connectionId
|
|
682
|
-
) {
|
|
683
|
-
return;
|
|
684
|
-
}
|
|
685
|
-
const listedWorkbook = listData.workbooks.find(
|
|
686
|
-
(workbook) => workbook.id === selectedWorkbook.id,
|
|
687
|
-
);
|
|
688
|
-
if (
|
|
689
|
-
listedWorkbook &&
|
|
690
|
-
(listedWorkbook.status !== "migrating" ||
|
|
691
|
-
listedWorkbook.canRetryStaleClaim === true)
|
|
692
|
-
) {
|
|
693
|
-
setMigrationInProgress(false);
|
|
694
|
-
}
|
|
695
|
-
}, [
|
|
696
|
-
connectionId,
|
|
697
|
-
listData,
|
|
698
|
-
migrationInProgress,
|
|
699
|
-
selectedWorkbook,
|
|
700
|
-
workbookList.isFetching,
|
|
701
|
-
]);
|
|
702
|
-
|
|
703
|
-
const handleReview = () => {
|
|
704
|
-
if (!reviewTarget || !selectedWorkbook) return;
|
|
705
|
-
send({
|
|
706
|
-
message: t("dashboardOverview.sigmaReviewPrompt", {
|
|
707
|
-
name: reviewTarget.dashboardName,
|
|
708
|
-
}),
|
|
709
|
-
context: JSON.stringify({
|
|
710
|
-
dashboardId: reviewTarget.dashboardId,
|
|
711
|
-
workbookId: selectedWorkbook.id,
|
|
712
|
-
source: "sigma",
|
|
713
|
-
}),
|
|
714
|
-
contextLabel: t("dashboardOverview.sigmaReviewContext"),
|
|
715
|
-
submit: true,
|
|
716
|
-
openSidebar: true,
|
|
717
|
-
});
|
|
718
|
-
onOpenChange(false);
|
|
719
|
-
};
|
|
720
|
-
|
|
721
|
-
const dashboardPath = reviewTarget ? accessibleDashboardUrl : null;
|
|
722
486
|
const hasMoreWorkbooks =
|
|
723
487
|
!listHasError &&
|
|
724
488
|
listData?.connectionId === connectionId &&
|
|
@@ -862,12 +626,12 @@ export function SigmaMigrationDialog({
|
|
|
862
626
|
open={open}
|
|
863
627
|
onOpenChange={(nextOpen) => void handleDialogOpenChange(nextOpen)}
|
|
864
628
|
>
|
|
865
|
-
<DialogContent className="flex max-h-[90vh] w-[calc(100vw-2rem)] flex-col overflow-hidden p-0 sm:max-w-
|
|
629
|
+
<DialogContent className="flex max-h-[90vh] w-[calc(100vw-2rem)] flex-col overflow-hidden p-0 sm:max-w-2xl">
|
|
866
630
|
<DialogHeader className="px-6 pt-6">
|
|
867
631
|
<DialogTitle>{t("dashboardOverview.importFromSigma")}</DialogTitle>
|
|
868
632
|
</DialogHeader>
|
|
869
633
|
|
|
870
|
-
<div className="min-h-0 flex-1 space-y-5 overflow-y-auto px-6 pb-
|
|
634
|
+
<div className="min-h-0 flex-1 space-y-5 overflow-y-auto px-6 pb-6">
|
|
871
635
|
{sigmaCleanupPending ? (
|
|
872
636
|
<div
|
|
873
637
|
className="flex flex-wrap items-center justify-between gap-3 rounded-md border border-border p-3 text-sm"
|
|
@@ -1012,45 +776,31 @@ export function SigmaMigrationDialog({
|
|
|
1012
776
|
{loadMoreButton}
|
|
1013
777
|
</div>
|
|
1014
778
|
) : workbooks.length > 0 ? (
|
|
1015
|
-
<div className="space-y-
|
|
1016
|
-
<div className="
|
|
1017
|
-
<
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
/>
|
|
1030
|
-
</SelectTrigger>
|
|
1031
|
-
<SelectContent>
|
|
1032
|
-
{workbooks.map((workbook) => (
|
|
1033
|
-
<SelectItem key={workbook.id} value={workbook.id}>
|
|
1034
|
-
{workbook.name}
|
|
1035
|
-
</SelectItem>
|
|
1036
|
-
))}
|
|
1037
|
-
</SelectContent>
|
|
1038
|
-
</Select>
|
|
779
|
+
<div className="space-y-3">
|
|
780
|
+
<div className="relative">
|
|
781
|
+
<IconSearch
|
|
782
|
+
aria-hidden="true"
|
|
783
|
+
className="pointer-events-none absolute start-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"
|
|
784
|
+
/>
|
|
785
|
+
<Input
|
|
786
|
+
type="search"
|
|
787
|
+
className="ps-8"
|
|
788
|
+
value={search}
|
|
789
|
+
onChange={(event) => setSearch(event.currentTarget.value)}
|
|
790
|
+
placeholder={t("dashboardOverview.sigmaSearchWorkbooks")}
|
|
791
|
+
aria-label={t("dashboardOverview.sigmaSearchWorkbooks")}
|
|
792
|
+
/>
|
|
1039
793
|
</div>
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
count: selectedWorkbook.flaggedCount,
|
|
1051
|
-
})}
|
|
1052
|
-
</Badge>
|
|
1053
|
-
) : null}
|
|
794
|
+
<WorkbookTable
|
|
795
|
+
workbooks={visibleWorkbooks}
|
|
796
|
+
emptyMessage={
|
|
797
|
+
hasMoreWorkbooks
|
|
798
|
+
? t(
|
|
799
|
+
"dashboardOverview.sigmaNoMatchingWorkbooksLoadMore",
|
|
800
|
+
)
|
|
801
|
+
: t("dashboardOverview.sigmaNoMatchingWorkbooks")
|
|
802
|
+
}
|
|
803
|
+
/>
|
|
1054
804
|
|
|
1055
805
|
{listHasError ? (
|
|
1056
806
|
<StateMessage
|
|
@@ -1076,300 +826,16 @@ export function SigmaMigrationDialog({
|
|
|
1076
826
|
) : null}
|
|
1077
827
|
|
|
1078
828
|
{loadMoreButton}
|
|
1079
|
-
|
|
1080
|
-
{selectedWorkbook ? (
|
|
1081
|
-
<section className="space-y-3" aria-live="polite">
|
|
1082
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
1083
|
-
<h3 className="min-w-0 flex-1 truncate text-sm font-medium">
|
|
1084
|
-
{selectedWorkbook.name}
|
|
1085
|
-
</h3>
|
|
1086
|
-
<WorkbookStatusBadge status={selectedWorkbook.status} />
|
|
1087
|
-
{selectedWorkbook.isArchived ? (
|
|
1088
|
-
<Badge variant="outline">
|
|
1089
|
-
{t("dashboardOverview.sigmaArchived")}
|
|
1090
|
-
</Badge>
|
|
1091
|
-
) : null}
|
|
1092
|
-
</div>
|
|
1093
|
-
{selectedWorkbook.status === "failed" &&
|
|
1094
|
-
selectedWorkbook.retryableByCurrentUser === false ? (
|
|
1095
|
-
<p className="text-sm text-muted-foreground">
|
|
1096
|
-
{t("dashboardOverview.sigmaRetryOwnerOnly")}
|
|
1097
|
-
</p>
|
|
1098
|
-
) : null}
|
|
1099
|
-
|
|
1100
|
-
{inspection.isPending ? (
|
|
1101
|
-
<PreviewSkeleton />
|
|
1102
|
-
) : inspection.isError ||
|
|
1103
|
-
(inspection.data && !inspectionIsValid) ? (
|
|
1104
|
-
<StateMessage
|
|
1105
|
-
icon={<IconAlertCircle className="size-4" />}
|
|
1106
|
-
title={t("dashboardOverview.sigmaPreviewFailed")}
|
|
1107
|
-
detail={t("dashboardOverview.sigmaRetryPreview")}
|
|
1108
|
-
action={
|
|
1109
|
-
<div className="flex flex-wrap gap-2">
|
|
1110
|
-
{chooseConnectionButton}
|
|
1111
|
-
<Button
|
|
1112
|
-
type="button"
|
|
1113
|
-
variant="outline"
|
|
1114
|
-
size="sm"
|
|
1115
|
-
onClick={() => void inspection.refetch()}
|
|
1116
|
-
>
|
|
1117
|
-
{t("dashboardOverview.sigmaRetry")}
|
|
1118
|
-
</Button>
|
|
1119
|
-
</div>
|
|
1120
|
-
}
|
|
1121
|
-
/>
|
|
1122
|
-
) : inspection.data ? (
|
|
1123
|
-
<>
|
|
1124
|
-
<WorkbookPreview inspection={inspection.data} t={t} />
|
|
1125
|
-
</>
|
|
1126
|
-
) : null}
|
|
1127
|
-
|
|
1128
|
-
{(selectedWorkbook.status === "migrating" &&
|
|
1129
|
-
selectedWorkbook.canRetryStaleClaim !== true) ||
|
|
1130
|
-
migrationInProgress ? (
|
|
1131
|
-
<p
|
|
1132
|
-
className="text-sm text-muted-foreground"
|
|
1133
|
-
role="status"
|
|
1134
|
-
>
|
|
1135
|
-
{t("dashboardOverview.sigmaImportInProgress")}
|
|
1136
|
-
</p>
|
|
1137
|
-
) : null}
|
|
1138
|
-
|
|
1139
|
-
{migrationError ? (
|
|
1140
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
1141
|
-
<p className="text-sm text-destructive" role="alert">
|
|
1142
|
-
{migrationError}
|
|
1143
|
-
</p>
|
|
1144
|
-
{chooseConnectionButton}
|
|
1145
|
-
</div>
|
|
1146
|
-
) : null}
|
|
1147
|
-
|
|
1148
|
-
{isImported ? (
|
|
1149
|
-
<div className="space-y-3 rounded-md border border-border/60 bg-muted/20 p-3">
|
|
1150
|
-
<div className="flex items-start gap-2">
|
|
1151
|
-
<IconCheck className="mt-0.5 size-4 shrink-0 text-primary" />
|
|
1152
|
-
<div className="min-w-0 space-y-1">
|
|
1153
|
-
<p className="text-sm font-medium">
|
|
1154
|
-
{t(
|
|
1155
|
-
migrationResult?.status === "completed"
|
|
1156
|
-
? "dashboardOverview.sigmaImportComplete"
|
|
1157
|
-
: "dashboardOverview.sigmaAlreadyImported",
|
|
1158
|
-
)}
|
|
1159
|
-
</p>
|
|
1160
|
-
{migrationResult?.status === "completed" ? (
|
|
1161
|
-
<p className="text-sm text-muted-foreground">
|
|
1162
|
-
{t("dashboardOverview.sigmaImportSummary", {
|
|
1163
|
-
imported: migrationResult.importedCount,
|
|
1164
|
-
unsupported:
|
|
1165
|
-
migrationResult.unsupportedCount,
|
|
1166
|
-
})}
|
|
1167
|
-
</p>
|
|
1168
|
-
) : null}
|
|
1169
|
-
</div>
|
|
1170
|
-
</div>
|
|
1171
|
-
{migrationResult?.status === "completed" &&
|
|
1172
|
-
migrationResult.flaggedCount > 0 ? (
|
|
1173
|
-
<Badge variant="outline">
|
|
1174
|
-
{t("dashboardOverview.sigmaFlaggedCount", {
|
|
1175
|
-
count: migrationResult.flaggedCount,
|
|
1176
|
-
})}
|
|
1177
|
-
</Badge>
|
|
1178
|
-
) : null}
|
|
1179
|
-
{reviewTarget && dashboardPath ? (
|
|
1180
|
-
<div className="flex flex-wrap gap-2">
|
|
1181
|
-
<Button
|
|
1182
|
-
asChild
|
|
1183
|
-
type="button"
|
|
1184
|
-
variant="outline"
|
|
1185
|
-
size="sm"
|
|
1186
|
-
>
|
|
1187
|
-
<Link to={dashboardPath}>
|
|
1188
|
-
{t("dashboardOverview.sigmaOpenDashboard")}
|
|
1189
|
-
<IconExternalLink />
|
|
1190
|
-
</Link>
|
|
1191
|
-
</Button>
|
|
1192
|
-
<Button
|
|
1193
|
-
type="button"
|
|
1194
|
-
size="sm"
|
|
1195
|
-
onClick={handleReview}
|
|
1196
|
-
disabled={isGenerating}
|
|
1197
|
-
>
|
|
1198
|
-
<IconSparkles />
|
|
1199
|
-
{t("dashboardOverview.sigmaReviewWithAgent")}
|
|
1200
|
-
</Button>
|
|
1201
|
-
</div>
|
|
1202
|
-
) : (
|
|
1203
|
-
<p className="text-sm text-muted-foreground">
|
|
1204
|
-
{t("dashboardOverview.sigmaImportedNoDashboard")}
|
|
1205
|
-
</p>
|
|
1206
|
-
)}
|
|
1207
|
-
</div>
|
|
1208
|
-
) : null}
|
|
1209
|
-
</section>
|
|
1210
|
-
) : null}
|
|
1211
829
|
</div>
|
|
1212
830
|
) : null
|
|
1213
831
|
) : null}
|
|
1214
832
|
</div>
|
|
1215
|
-
|
|
1216
|
-
<DialogFooter className="border-t border-border/60 px-6 py-4">
|
|
1217
|
-
<Button
|
|
1218
|
-
type="button"
|
|
1219
|
-
variant="outline"
|
|
1220
|
-
onClick={() => onOpenChange(false)}
|
|
1221
|
-
>
|
|
1222
|
-
{t("sidebar.cancel")}
|
|
1223
|
-
</Button>
|
|
1224
|
-
{connected &&
|
|
1225
|
-
!needsConnectionSelection &&
|
|
1226
|
-
selectedWorkbook &&
|
|
1227
|
-
(canImport || migrateWorkbook.isPending) ? (
|
|
1228
|
-
<Button
|
|
1229
|
-
type="button"
|
|
1230
|
-
onClick={() => void handleImport()}
|
|
1231
|
-
disabled={
|
|
1232
|
-
!canImport || migrationInProgress || migrateWorkbook.isPending
|
|
1233
|
-
}
|
|
1234
|
-
>
|
|
1235
|
-
{migrateWorkbook.isPending ? (
|
|
1236
|
-
<IconLoader2 className="animate-spin" />
|
|
1237
|
-
) : null}
|
|
1238
|
-
{migrateWorkbook.isPending
|
|
1239
|
-
? t("dashboardOverview.sigmaImporting")
|
|
1240
|
-
: selectedWorkbook.canRetryStaleClaim
|
|
1241
|
-
? t("dashboardOverview.sigmaRetry")
|
|
1242
|
-
: t("dashboardOverview.sigmaImportWorkbook")}
|
|
1243
|
-
</Button>
|
|
1244
|
-
) : null}
|
|
1245
|
-
</DialogFooter>
|
|
1246
833
|
</DialogContent>
|
|
1247
834
|
</Dialog>
|
|
1248
|
-
{codeRequiredDialog}
|
|
1249
835
|
</>
|
|
1250
836
|
);
|
|
1251
837
|
}
|
|
1252
838
|
|
|
1253
|
-
function WorkbookPreview({
|
|
1254
|
-
inspection,
|
|
1255
|
-
t,
|
|
1256
|
-
}: {
|
|
1257
|
-
inspection: SigmaWorkbookInspection;
|
|
1258
|
-
t: ReturnType<typeof useT>;
|
|
1259
|
-
}) {
|
|
1260
|
-
const visibleElements = inspection.elements.slice(
|
|
1261
|
-
0,
|
|
1262
|
-
SIGMA_PREVIEW_ELEMENT_LIMIT,
|
|
1263
|
-
);
|
|
1264
|
-
return (
|
|
1265
|
-
<div className="space-y-3">
|
|
1266
|
-
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
1267
|
-
<h4 className="text-sm font-medium">
|
|
1268
|
-
{t("dashboardOverview.sigmaPreviewTitle")}
|
|
1269
|
-
</h4>
|
|
1270
|
-
<div className="flex flex-wrap gap-1.5">
|
|
1271
|
-
<Badge variant="secondary">
|
|
1272
|
-
{t("dashboardOverview.sigmaImportableCount", {
|
|
1273
|
-
count: inspection.importableCount,
|
|
1274
|
-
})}
|
|
1275
|
-
</Badge>
|
|
1276
|
-
<Badge variant="outline">
|
|
1277
|
-
{t("dashboardOverview.sigmaUnsupportedCount", {
|
|
1278
|
-
count: inspection.unsupportedCount,
|
|
1279
|
-
})}
|
|
1280
|
-
</Badge>
|
|
1281
|
-
{inspection.reviewRequiredCount > 0 ? (
|
|
1282
|
-
<Badge variant="outline">
|
|
1283
|
-
{t("dashboardOverview.sigmaReviewRequiredCount", {
|
|
1284
|
-
count: inspection.reviewRequiredCount,
|
|
1285
|
-
})}
|
|
1286
|
-
</Badge>
|
|
1287
|
-
) : null}
|
|
1288
|
-
</div>
|
|
1289
|
-
</div>
|
|
1290
|
-
{inspection.betaRepresentationWarning ? (
|
|
1291
|
-
<p className="text-sm text-muted-foreground">
|
|
1292
|
-
{t("dashboardOverview.sigmaBetaRepresentationWarning")}
|
|
1293
|
-
</p>
|
|
1294
|
-
) : null}
|
|
1295
|
-
{inspection.sourceMappingRequiredCount > 0 ? (
|
|
1296
|
-
<p className="text-sm text-muted-foreground">
|
|
1297
|
-
{t("dashboardOverview.sigmaPreviewOnly")}
|
|
1298
|
-
</p>
|
|
1299
|
-
) : null}
|
|
1300
|
-
{inspection.elements.length === 0 ? (
|
|
1301
|
-
<p className="text-sm text-muted-foreground">
|
|
1302
|
-
{t("dashboardOverview.sigmaNoElements")}
|
|
1303
|
-
</p>
|
|
1304
|
-
) : (
|
|
1305
|
-
<>
|
|
1306
|
-
<ul className="max-h-56 divide-y overflow-y-auto rounded-md border border-border/60">
|
|
1307
|
-
{visibleElements.map((element) => (
|
|
1308
|
-
<li
|
|
1309
|
-
key={element.id}
|
|
1310
|
-
className="flex min-w-0 items-start justify-between gap-3 px-3 py-2.5"
|
|
1311
|
-
>
|
|
1312
|
-
<div className="min-w-0">
|
|
1313
|
-
<p className="truncate text-sm font-medium">{element.name}</p>
|
|
1314
|
-
<p className="truncate text-xs text-muted-foreground">
|
|
1315
|
-
{element.type}
|
|
1316
|
-
</p>
|
|
1317
|
-
{element.reason ? (
|
|
1318
|
-
<p className="mt-1 text-xs text-muted-foreground">
|
|
1319
|
-
{t(reasonTranslationKey(element.reason))}
|
|
1320
|
-
</p>
|
|
1321
|
-
) : null}
|
|
1322
|
-
</div>
|
|
1323
|
-
<Badge
|
|
1324
|
-
variant={element.importable ? "secondary" : "outline"}
|
|
1325
|
-
className="shrink-0"
|
|
1326
|
-
>
|
|
1327
|
-
{element.importable
|
|
1328
|
-
? t("dashboardOverview.sigmaImportable")
|
|
1329
|
-
: t("dashboardOverview.sigmaUnsupported")}
|
|
1330
|
-
</Badge>
|
|
1331
|
-
</li>
|
|
1332
|
-
))}
|
|
1333
|
-
</ul>
|
|
1334
|
-
{visibleElements.length < inspection.elements.length ? (
|
|
1335
|
-
<p className="text-sm text-muted-foreground">
|
|
1336
|
-
{t("dashboardOverview.sigmaPreviewTruncated", {
|
|
1337
|
-
shown: visibleElements.length,
|
|
1338
|
-
total: inspection.elements.length,
|
|
1339
|
-
})}
|
|
1340
|
-
</p>
|
|
1341
|
-
) : null}
|
|
1342
|
-
</>
|
|
1343
|
-
)}
|
|
1344
|
-
</div>
|
|
1345
|
-
);
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
function reasonTranslationKey(reason: SigmaElementReviewReason) {
|
|
1349
|
-
return {
|
|
1350
|
-
unsupported_element: "dashboardOverview.sigmaReasonUnsupportedElement",
|
|
1351
|
-
unsupported_source: "dashboardOverview.sigmaReasonUnsupportedSource",
|
|
1352
|
-
source_unavailable: "dashboardOverview.sigmaReasonSourceUnavailable",
|
|
1353
|
-
source_mapping_required:
|
|
1354
|
-
"dashboardOverview.sigmaReasonSourceMappingRequired",
|
|
1355
|
-
query_unavailable: "dashboardOverview.sigmaReasonQueryUnavailable",
|
|
1356
|
-
unsafe_sql: "dashboardOverview.sigmaReasonUnsafeSql",
|
|
1357
|
-
visualization_review: "dashboardOverview.sigmaReasonVisualizationReview",
|
|
1358
|
-
}[reason];
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
function WorkbookStatusBadge({ status }: { status: SigmaWorkbook["status"] }) {
|
|
1362
|
-
const t = useT();
|
|
1363
|
-
const labelKey = {
|
|
1364
|
-
not_started: "dashboardOverview.sigmaStatusNotImported",
|
|
1365
|
-
migrating: "dashboardOverview.sigmaStatusImporting",
|
|
1366
|
-
migrated: "dashboardOverview.sigmaStatusImported",
|
|
1367
|
-
failed: "dashboardOverview.sigmaStatusFailed",
|
|
1368
|
-
}[status];
|
|
1369
|
-
const variant = status === "failed" ? "destructive" : "outline";
|
|
1370
|
-
return <Badge variant={variant}>{t(labelKey)}</Badge>;
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
839
|
function StateMessage({
|
|
1374
840
|
icon,
|
|
1375
841
|
title,
|
|
@@ -1397,31 +863,15 @@ function StateMessage({
|
|
|
1397
863
|
|
|
1398
864
|
function WorkbookListSkeleton() {
|
|
1399
865
|
return (
|
|
1400
|
-
<div className="space-y-
|
|
1401
|
-
<Skeleton className="h-
|
|
1402
|
-
<
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
</div>
|
|
1410
|
-
</div>
|
|
1411
|
-
);
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
function PreviewSkeleton() {
|
|
1415
|
-
return (
|
|
1416
|
-
<div className="space-y-2" aria-busy="true">
|
|
1417
|
-
<div className="flex items-center justify-between gap-2">
|
|
1418
|
-
<Skeleton className="h-4 w-32" />
|
|
1419
|
-
<Skeleton className="h-6 w-40" />
|
|
1420
|
-
</div>
|
|
1421
|
-
<div className="space-y-3 rounded-md border border-border/60 p-3">
|
|
1422
|
-
<Skeleton className="h-8 w-full" />
|
|
1423
|
-
<Skeleton className="h-8 w-full" />
|
|
1424
|
-
<Skeleton className="h-8 w-full" />
|
|
866
|
+
<div className="space-y-3" aria-busy="true">
|
|
867
|
+
<Skeleton className="h-9 w-full" />
|
|
868
|
+
<div className="space-y-1 rounded-md border border-border/60 p-2">
|
|
869
|
+
{[0, 1, 2, 3, 4].map((row) => (
|
|
870
|
+
<div key={row} className="flex items-center justify-between gap-3">
|
|
871
|
+
<Skeleton className="h-5 w-2/3" />
|
|
872
|
+
<Skeleton className="h-8 w-20" />
|
|
873
|
+
</div>
|
|
874
|
+
))}
|
|
1425
875
|
</div>
|
|
1426
876
|
</div>
|
|
1427
877
|
);
|
|
@@ -1467,7 +917,15 @@ function isWorkbookListResult(
|
|
|
1467
917
|
workbook.flaggedCount >= 0)) &&
|
|
1468
918
|
(workbook.migratedByTeammate === undefined ||
|
|
1469
919
|
typeof workbook.migratedByTeammate === "boolean") &&
|
|
1470
|
-
|
|
920
|
+
(workbook.canOpen === undefined ||
|
|
921
|
+
typeof workbook.canOpen === "boolean") &&
|
|
922
|
+
(workbook.migratedOwnerEmail === undefined ||
|
|
923
|
+
typeof workbook.migratedOwnerEmail === "string") &&
|
|
924
|
+
(workbook.migratingByEmail === undefined ||
|
|
925
|
+
typeof workbook.migratingByEmail === "string") &&
|
|
926
|
+
isOptionalName(workbook.migratedOwnerName) &&
|
|
927
|
+
isOptionalName(workbook.migratingByName) &&
|
|
928
|
+
typeof workbook.canStartImport === "boolean",
|
|
1471
929
|
) &&
|
|
1472
930
|
(result.nextPage === undefined ||
|
|
1473
931
|
result.nextPage === null ||
|
|
@@ -1493,82 +951,190 @@ function actionErrorCode(error: unknown): string | undefined {
|
|
|
1493
951
|
return typeof errorCode === "string" ? errorCode : undefined;
|
|
1494
952
|
}
|
|
1495
953
|
|
|
1496
|
-
function
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
954
|
+
function safeDashboardPath(id: string | null) {
|
|
955
|
+
if (!id || !/^[A-Za-z0-9_-]{1,200}$/.test(id)) return null;
|
|
956
|
+
return `/dashboards/${encodeURIComponent(id)}`;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function isOptionalName(value: unknown): boolean {
|
|
960
|
+
return value === undefined || value === null || typeof value === "string";
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function WorkbookTable({
|
|
964
|
+
workbooks,
|
|
965
|
+
emptyMessage,
|
|
966
|
+
}: {
|
|
967
|
+
workbooks: SigmaWorkbook[];
|
|
968
|
+
emptyMessage: string;
|
|
969
|
+
}) {
|
|
970
|
+
const t = useT();
|
|
1501
971
|
return (
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
typeof element.id === "string" &&
|
|
1518
|
-
typeof element.name === "string" &&
|
|
1519
|
-
typeof element.type === "string" &&
|
|
1520
|
-
typeof element.importable === "boolean" &&
|
|
1521
|
-
typeof element.visualizationPreserved === "boolean" &&
|
|
1522
|
-
(element.reason === undefined ||
|
|
1523
|
-
[
|
|
1524
|
-
"unsupported_element",
|
|
1525
|
-
"unsupported_source",
|
|
1526
|
-
"source_unavailable",
|
|
1527
|
-
"source_mapping_required",
|
|
1528
|
-
"query_unavailable",
|
|
1529
|
-
"unsafe_sql",
|
|
1530
|
-
"visualization_review",
|
|
1531
|
-
].includes(element.reason)),
|
|
1532
|
-
)
|
|
972
|
+
<TooltipProvider delayDuration={200}>
|
|
973
|
+
{/* A fixed height keeps the dialog still while the search narrows it. */}
|
|
974
|
+
<div className="h-96 overflow-y-auto rounded-md border border-border/60">
|
|
975
|
+
{workbooks.length === 0 ? (
|
|
976
|
+
<p className="p-4 text-sm text-muted-foreground">{emptyMessage}</p>
|
|
977
|
+
) : null}
|
|
978
|
+
<Table>
|
|
979
|
+
<TableBody>
|
|
980
|
+
{workbooks.map((workbook) => (
|
|
981
|
+
<WorkbookRow key={workbook.id} workbook={workbook} t={t} />
|
|
982
|
+
))}
|
|
983
|
+
</TableBody>
|
|
984
|
+
</Table>
|
|
985
|
+
</div>
|
|
986
|
+
</TooltipProvider>
|
|
1533
987
|
);
|
|
1534
988
|
}
|
|
1535
989
|
|
|
1536
|
-
function
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
990
|
+
function WorkbookRow({
|
|
991
|
+
workbook,
|
|
992
|
+
t,
|
|
993
|
+
}: {
|
|
994
|
+
workbook: SigmaWorkbook;
|
|
995
|
+
t: ReturnType<typeof useT>;
|
|
996
|
+
}) {
|
|
997
|
+
// Migrate shows only where the server would let this user start an import,
|
|
998
|
+
// or recover their own import whose dashboard is gone.
|
|
999
|
+
const canMigrate =
|
|
1000
|
+
workbook.canStartImport || Boolean(workbook.canRecoverMissingDashboard);
|
|
1001
|
+
const dashboardPath =
|
|
1002
|
+
workbook.status === "migrated" && !canMigrate
|
|
1003
|
+
? safeDashboardPath(workbook.dashboardId ?? null)
|
|
1004
|
+
: null;
|
|
1005
|
+
const migrating = workbook.status === "migrating" && !canMigrate;
|
|
1006
|
+
const migratingBy = migrating ? (workbook.migratingByEmail ?? null) : null;
|
|
1007
|
+
const ownerEmail =
|
|
1008
|
+
workbook.status === "migrated" && !canMigrate
|
|
1009
|
+
? (workbook.migratedOwnerEmail ?? null)
|
|
1010
|
+
: null;
|
|
1011
|
+
const muted = !canMigrate;
|
|
1550
1012
|
return (
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1013
|
+
<TableRow
|
|
1014
|
+
data-workbook-id={workbook.id}
|
|
1015
|
+
data-state={muted ? "taken" : undefined}
|
|
1016
|
+
>
|
|
1017
|
+
<TableCell>
|
|
1018
|
+
<div
|
|
1019
|
+
className={
|
|
1020
|
+
muted
|
|
1021
|
+
? "flex min-w-0 items-center gap-2 text-muted-foreground"
|
|
1022
|
+
: "flex min-w-0 items-center gap-2"
|
|
1023
|
+
}
|
|
1024
|
+
>
|
|
1025
|
+
{dashboardPath ? (
|
|
1026
|
+
// A real link, so middle-click and copy work. Opening an imported
|
|
1027
|
+
// workbook never starts a second import; the dashboard's own
|
|
1028
|
+
// access check decides what the new tab shows.
|
|
1029
|
+
<a
|
|
1030
|
+
href={appPath(dashboardPath)}
|
|
1031
|
+
target="_blank"
|
|
1032
|
+
rel="noopener"
|
|
1033
|
+
className="inline-flex min-w-0 items-center gap-1.5 hover:underline"
|
|
1034
|
+
aria-label={`${workbook.name} — ${t("dashboardOverview.sigmaOpenMigratedDashboard")}`}
|
|
1035
|
+
>
|
|
1036
|
+
<span className="truncate">{workbook.name}</span>
|
|
1037
|
+
<IconExternalLink
|
|
1038
|
+
aria-hidden="true"
|
|
1039
|
+
className="size-4 shrink-0"
|
|
1040
|
+
/>
|
|
1041
|
+
</a>
|
|
1042
|
+
) : (
|
|
1043
|
+
<span className="truncate">{workbook.name}</span>
|
|
1044
|
+
)}
|
|
1045
|
+
{workbook.isArchived ? (
|
|
1046
|
+
<Badge variant="outline">
|
|
1047
|
+
{t("dashboardOverview.sigmaArchived")}
|
|
1048
|
+
</Badge>
|
|
1049
|
+
) : null}
|
|
1050
|
+
</div>
|
|
1051
|
+
</TableCell>
|
|
1052
|
+
<TableCell className="w-px whitespace-nowrap text-end">
|
|
1053
|
+
{ownerEmail ? (
|
|
1054
|
+
<span className="inline-flex items-center gap-2 text-sm text-muted-foreground">
|
|
1055
|
+
{t("dashboardOverview.sigmaOwnedBy")}
|
|
1056
|
+
<OwnerAvatar
|
|
1057
|
+
email={ownerEmail}
|
|
1058
|
+
name={workbook.migratedOwnerName ?? null}
|
|
1059
|
+
label={ownerLabel(ownerEmail, workbook.migratedOwnerName)}
|
|
1060
|
+
/>
|
|
1061
|
+
</span>
|
|
1062
|
+
) : migrating ? (
|
|
1063
|
+
<span className="inline-flex items-center gap-2 text-sm text-muted-foreground">
|
|
1064
|
+
<IconLoader2 aria-hidden="true" className="size-4 animate-spin" />
|
|
1065
|
+
{migratingBy ? (
|
|
1066
|
+
<OwnerAvatar
|
|
1067
|
+
email={migratingBy}
|
|
1068
|
+
name={workbook.migratingByName ?? null}
|
|
1069
|
+
label={t("dashboardOverview.sigmaMigratingTooltip", {
|
|
1070
|
+
name: ownerLabel(migratingBy, workbook.migratingByName),
|
|
1071
|
+
})}
|
|
1072
|
+
/>
|
|
1073
|
+
) : null}
|
|
1074
|
+
</span>
|
|
1075
|
+
) : canMigrate ? (
|
|
1076
|
+
<Tooltip>
|
|
1077
|
+
<TooltipTrigger asChild>
|
|
1078
|
+
{/* A disabled button gets no pointer events, so the span carries the hover. */}
|
|
1079
|
+
<span className="inline-flex" tabIndex={0}>
|
|
1080
|
+
<Button type="button" size="sm" disabled>
|
|
1081
|
+
{t("dashboardOverview.sigmaMigrate")}
|
|
1082
|
+
</Button>
|
|
1083
|
+
</span>
|
|
1084
|
+
</TooltipTrigger>
|
|
1085
|
+
<TooltipContent>
|
|
1086
|
+
{t("dashboardOverview.sigmaMigrateComingSoon")}
|
|
1087
|
+
</TooltipContent>
|
|
1088
|
+
</Tooltip>
|
|
1089
|
+
) : null}
|
|
1090
|
+
</TableCell>
|
|
1091
|
+
</TableRow>
|
|
1560
1092
|
);
|
|
1561
1093
|
}
|
|
1562
1094
|
|
|
1563
|
-
function
|
|
1564
|
-
|
|
1565
|
-
return `/dashboards/${encodeURIComponent(id)}`;
|
|
1095
|
+
function ownerLabel(email: string, name: string | null | undefined): string {
|
|
1096
|
+
return name ? `${name} · ${email}` : email;
|
|
1566
1097
|
}
|
|
1567
1098
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1099
|
+
/** The account-menu avatar: the person's photo, else two initials. */
|
|
1100
|
+
function OwnerAvatar({
|
|
1101
|
+
email,
|
|
1102
|
+
name,
|
|
1103
|
+
label,
|
|
1104
|
+
}: {
|
|
1105
|
+
email: string;
|
|
1106
|
+
name: string | null;
|
|
1107
|
+
label: string;
|
|
1108
|
+
}) {
|
|
1109
|
+
const avatarUrl = useAvatarUrl(email);
|
|
1110
|
+
return (
|
|
1111
|
+
<Tooltip>
|
|
1112
|
+
<TooltipTrigger asChild>
|
|
1113
|
+
{/* role="img" makes the label the avatar's accessible name. */}
|
|
1114
|
+
<span
|
|
1115
|
+
role="img"
|
|
1116
|
+
tabIndex={0}
|
|
1117
|
+
aria-label={label}
|
|
1118
|
+
className="inline-flex rounded-full border border-border text-xs font-semibold text-muted-foreground"
|
|
1119
|
+
>
|
|
1120
|
+
<Avatar className="size-6">
|
|
1121
|
+
{avatarUrl ? <AvatarImage src={avatarUrl} alt="" /> : null}
|
|
1122
|
+
<AvatarFallback>{initials(name ?? email)}</AvatarFallback>
|
|
1123
|
+
</Avatar>
|
|
1124
|
+
</span>
|
|
1125
|
+
</TooltipTrigger>
|
|
1126
|
+
<TooltipContent>{label}</TooltipContent>
|
|
1127
|
+
</Tooltip>
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function initials(value: string): string {
|
|
1132
|
+
return (
|
|
1133
|
+
value
|
|
1134
|
+
.split(/[ @._-]+/)
|
|
1135
|
+
.filter(Boolean)
|
|
1136
|
+
.slice(0, 2)
|
|
1137
|
+
.map((part) => part[0]?.toUpperCase())
|
|
1138
|
+
.join("") || "?"
|
|
1139
|
+
);
|
|
1574
1140
|
}
|