@agent-native/core 0.120.1 → 0.120.2
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 +9 -0
- package/corpus/core/docs/content/extensions.mdx +23 -4
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +4 -0
- package/corpus/core/src/agent/run-manager.ts +36 -10
- package/corpus/core/src/agent/types.ts +2 -0
- package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +16 -3
- package/corpus/core/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
- package/corpus/core/src/client/settings/SettingsPanel.tsx +10 -0
- package/corpus/core/src/deploy/build.ts +3 -0
- package/corpus/core/src/server/analytics.ts +154 -25
- package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/corpus/core/src/templates/chat/netlify.toml +1 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/corpus/core/src/vite/client.ts +6 -0
- package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +27 -43
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -8
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +2 -2
- package/corpus/templates/analytics/AGENTS.md +20 -8
- package/corpus/templates/analytics/actions/get-explorer-dashboard.ts +3 -0
- package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +140 -46
- package/corpus/templates/analytics/actions/migrate-analytics-artifacts.ts +48 -0
- package/corpus/templates/analytics/actions/save-analysis.ts +2 -3
- package/corpus/templates/analytics/actions/view-screen.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/DashboardMetadata.tsx +77 -0
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +103 -502
- package/corpus/templates/analytics/app/global.css +30 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -2
- package/corpus/templates/analytics/app/i18n-data.ts +80 -0
- package/corpus/templates/analytics/app/pages/Agents.tsx +49 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +54 -11
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -53
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +23 -25
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
- package/corpus/templates/analytics/app/routes/analyses._index.tsx +3 -2
- package/corpus/templates/analytics/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/analytics/changelog/2026-07-23-analytics-now-groups-saved-analyses-and-embedded-extensions-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-menus-show-who-created-and-last-updated-each-dashb.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-usage-now-loads-reliably-for-organizations-with-ma.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-long-dashboard-building-requests-now-continue-without-stoppi.md +6 -0
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/analytics/server/db/schema.ts +4 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -1
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +1 -0
- package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +1062 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -52
- package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -1
- package/corpus/templates/analytics/server/plugins/db.ts +10 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +36 -1
- package/corpus/templates/assets/actions/generate-image.ts +28 -1
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +46 -8
- package/corpus/templates/assets/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/assets/changelog/2026-07-23-assets-image-generation-now-recovers-after-a-completed-tool-.md +6 -0
- package/corpus/templates/assets/netlify.toml +1 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -8
- package/corpus/templates/brain/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/brain/netlify.toml +1 -0
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +43 -5
- package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +2 -16
- package/corpus/templates/calendar/netlify.toml +1 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/corpus/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/chat/netlify.toml +1 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +52 -6
- package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +2 -16
- package/corpus/templates/clips/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +1 -1
- package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +2 -0
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +1 -3
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +86 -0
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +24 -0
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +5 -69
- package/corpus/templates/clips/desktop/src/styles.css +35 -3
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +13 -44
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +119 -17
- package/corpus/templates/clips/netlify.toml +1 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +3 -7
- package/corpus/templates/content/app/routes/_app.extensions._index.tsx +2 -2
- package/corpus/templates/content/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/content/netlify.toml +1 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +55 -17
- package/corpus/templates/design/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/design/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/design/netlify.toml +1 -0
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -1
- package/corpus/templates/dispatch/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/dispatch/netlify.toml +1 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +41 -45
- package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +2 -2
- package/corpus/templates/forms/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/forms/netlify.toml +1 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +36 -5
- package/corpus/templates/macros/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/macros/netlify.toml +1 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +0 -9
- package/corpus/templates/mail/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/mail/netlify.toml +1 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +38 -5
- package/corpus/templates/plan/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/plan/netlify.toml +1 -0
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +47 -4
- package/corpus/templates/slides/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/slides/netlify.toml +1 -0
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +37 -6
- package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/tasks/app/routes/settings.tsx +31 -0
- package/corpus/templates/tasks/netlify.toml +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +9 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +25 -10
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/types.d.ts +2 -2
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts +5 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.js +5 -3
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
- package/dist/client/settings/ExtensionsSettingsContent.d.ts +2 -0
- package/dist/client/settings/ExtensionsSettingsContent.d.ts.map +1 -0
- package/dist/client/settings/ExtensionsSettingsContent.js +10 -0
- package/dist/client/settings/ExtensionsSettingsContent.js.map +1 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +10 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/deploy/build.js +1 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/server/analytics.d.ts +9 -4
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +111 -22
- package/dist/server/analytics.js.map +1 -1
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/dist/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/dist/templates/chat/netlify.toml +1 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/extensions.mdx +23 -4
- package/package.json +1 -1
- package/src/agent/production-agent.ts +4 -0
- package/src/agent/run-manager.ts +36 -10
- package/src/agent/types.ts +2 -0
- package/src/client/extensions/ExtensionsListPage.tsx +16 -3
- package/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
- package/src/client/settings/SettingsPanel.tsx +10 -0
- package/src/deploy/build.ts +3 -0
- package/src/server/analytics.ts +154 -25
- package/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/src/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/src/templates/chat/netlify.toml +1 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/src/vite/client.ts +6 -0
- package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +0 -165
- package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +0 -175
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
IconGripVertical,
|
|
12
12
|
IconBook2,
|
|
13
13
|
IconDatabase,
|
|
14
|
-
IconReportAnalytics,
|
|
15
14
|
IconSearch,
|
|
16
15
|
IconArchive,
|
|
17
16
|
IconActivity,
|
|
@@ -62,7 +61,8 @@ type SidebarDashboard = {
|
|
|
62
61
|
id: string;
|
|
63
62
|
name: string;
|
|
64
63
|
subviews?: DashboardSubview[];
|
|
65
|
-
source: "static" | "sql";
|
|
64
|
+
source: "static" | "sql" | "analysis";
|
|
65
|
+
resourceId?: string;
|
|
66
66
|
visibility?: Visibility;
|
|
67
67
|
/** Id of the dashboard this one nests under in the sidebar, if any. */
|
|
68
68
|
parentId?: string;
|
|
@@ -88,7 +88,6 @@ import {
|
|
|
88
88
|
} from "@agent-native/core/client/agent-chat";
|
|
89
89
|
import { appApiPath, appPath } from "@agent-native/core/client/api-path";
|
|
90
90
|
import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
|
|
91
|
-
import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
|
|
92
91
|
import {
|
|
93
92
|
callAction,
|
|
94
93
|
useActionMutation,
|
|
@@ -143,24 +142,18 @@ import { useUserPref } from "@/hooks/use-user-pref";
|
|
|
143
142
|
import { shouldRenderDashboardList } from "@/lib/dashboard-list-loading";
|
|
144
143
|
import { usePopularity, popularityOf } from "@/lib/item-popularity";
|
|
145
144
|
import {
|
|
146
|
-
analysisDetailPrefetchKey,
|
|
147
145
|
sqlDashboardPrefetchKey,
|
|
148
146
|
type PrefetchSnapshot,
|
|
149
147
|
} from "@/lib/prefetch-keys";
|
|
150
148
|
import type { ResourceAccess } from "@/lib/resource-access";
|
|
151
149
|
|
|
152
|
-
import { NewAnalysisDialog } from "./NewAnalysisDialog";
|
|
153
150
|
import { NewDashboardDialog } from "./NewDashboardDialog";
|
|
154
151
|
import { SidebarLoadError } from "./SidebarLoadError";
|
|
155
152
|
|
|
156
|
-
type AnalysisHiddenFilter = "visible" | "hidden";
|
|
157
|
-
|
|
158
153
|
const SIDEBAR_PREVIEW_COUNT = 5;
|
|
159
154
|
const ASK_OPEN_KEY = "analytics-sidebar-ask-open";
|
|
160
155
|
const DASHBOARD_SORT_MODE_KEY = "dashboard-sort-mode";
|
|
161
|
-
const ANALYSIS_SORT_MODE_KEY = "analysis-sort-mode";
|
|
162
156
|
const DASHBOARDS_OPEN_KEY = "analytics-sidebar-dashboards-open";
|
|
163
|
-
const ANALYSES_OPEN_KEY = "analytics-sidebar-analyses-open";
|
|
164
157
|
const SIDEBAR_COLLAPSE_KEY = "analytics.sidebar.collapsed";
|
|
165
158
|
const SIDEBAR_SKELETON_CLASS =
|
|
166
159
|
"bg-sidebar-foreground/12 dark:bg-sidebar-foreground/10";
|
|
@@ -867,7 +860,10 @@ function SortableDashboardItem({
|
|
|
867
860
|
visibility: Visibility,
|
|
868
861
|
) => Promise<void>;
|
|
869
862
|
}) {
|
|
870
|
-
const
|
|
863
|
+
const resourceId = d.resourceId ?? d.id;
|
|
864
|
+
const href =
|
|
865
|
+
d.source === "analysis" ? `/analyses/${resourceId}` : `/dashboards/${d.id}`;
|
|
866
|
+
const favoriteKey = d.source === "analysis" ? `analysis:${resourceId}` : d.id;
|
|
871
867
|
const t = useT();
|
|
872
868
|
const { mutateAsync: deleteView } = useDeleteDashboardView();
|
|
873
869
|
const [deletingViewId, setDeletingViewId] = useState<string | null>(null);
|
|
@@ -908,7 +904,7 @@ function SortableDashboardItem({
|
|
|
908
904
|
return (
|
|
909
905
|
<SortableRow
|
|
910
906
|
id={d.id}
|
|
911
|
-
favoriteKey={
|
|
907
|
+
favoriteKey={favoriteKey}
|
|
912
908
|
name={d.name}
|
|
913
909
|
href={href}
|
|
914
910
|
isActive={isActive}
|
|
@@ -916,7 +912,9 @@ function SortableDashboardItem({
|
|
|
916
912
|
onToggleFavorite={onToggleFavorite}
|
|
917
913
|
onDelete={() => onDelete(d)}
|
|
918
914
|
onRename={(name) => onRename(d, name)}
|
|
919
|
-
onArchive={
|
|
915
|
+
onArchive={
|
|
916
|
+
d.source === "analysis" || !onArchive ? undefined : () => onArchive(d)
|
|
917
|
+
}
|
|
920
918
|
onPrefetch={() => onPrefetch?.(d)}
|
|
921
919
|
visibility={d.visibility}
|
|
922
920
|
onSetVisibility={
|
|
@@ -1068,33 +1066,6 @@ function SortableDashboardItem({
|
|
|
1068
1066
|
);
|
|
1069
1067
|
}
|
|
1070
1068
|
|
|
1071
|
-
// Analyses reuse SortableRow directly — no wrapper component needed.
|
|
1072
|
-
|
|
1073
|
-
const ANALYSIS_ORDER_KEY = "analysis-order";
|
|
1074
|
-
|
|
1075
|
-
function getAnalysisOrder(): string[] {
|
|
1076
|
-
if (typeof window === "undefined") return [];
|
|
1077
|
-
try {
|
|
1078
|
-
const raw = window.localStorage.getItem(ANALYSIS_ORDER_KEY);
|
|
1079
|
-
if (!raw) return [];
|
|
1080
|
-
const parsed = JSON.parse(raw);
|
|
1081
|
-
return Array.isArray(parsed)
|
|
1082
|
-
? parsed.filter((x) => typeof x === "string")
|
|
1083
|
-
: [];
|
|
1084
|
-
} catch {
|
|
1085
|
-
return [];
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
function setAnalysisOrder(order: string[]): void {
|
|
1090
|
-
if (typeof window === "undefined") return;
|
|
1091
|
-
try {
|
|
1092
|
-
window.localStorage.setItem(ANALYSIS_ORDER_KEY, JSON.stringify(order));
|
|
1093
|
-
} catch {
|
|
1094
|
-
// localStorage unavailable / quota — ignore, order is best-effort
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
1069
|
const STATIC_DASHBOARD_RENAMES_KEY = "dashboard-name-overrides";
|
|
1099
1070
|
|
|
1100
1071
|
function getStaticDashboardRenames(): Record<string, string> {
|
|
@@ -1161,10 +1132,7 @@ async function fetchSqlDashboards(
|
|
|
1161
1132
|
}));
|
|
1162
1133
|
}
|
|
1163
1134
|
|
|
1164
|
-
async function fetchSidebarAnalyses(
|
|
1165
|
-
t: (key: string) => string,
|
|
1166
|
-
hidden: AnalysisHiddenFilter = "visible",
|
|
1167
|
-
): Promise<
|
|
1135
|
+
async function fetchSidebarAnalyses(t: (key: string) => string): Promise<
|
|
1168
1136
|
{
|
|
1169
1137
|
id: string;
|
|
1170
1138
|
name: string;
|
|
@@ -1172,13 +1140,7 @@ async function fetchSidebarAnalyses(
|
|
|
1172
1140
|
hiddenAt: string | null;
|
|
1173
1141
|
}[]
|
|
1174
1142
|
> {
|
|
1175
|
-
const rows = await callAction(
|
|
1176
|
-
"list-analyses",
|
|
1177
|
-
{
|
|
1178
|
-
...(hidden === "hidden" ? { hidden: "hidden" } : {}),
|
|
1179
|
-
} as Record<string, unknown>,
|
|
1180
|
-
{ method: "GET" },
|
|
1181
|
-
);
|
|
1143
|
+
const rows = await callAction("list-analyses", {}, { method: "GET" });
|
|
1182
1144
|
return (Array.isArray(rows) ? rows : [])
|
|
1183
1145
|
.filter((a: any) => a && typeof a.id === "string" && a.id.length > 0)
|
|
1184
1146
|
.map((a: any) => ({
|
|
@@ -1256,16 +1218,6 @@ async function fetchSqlDashboardForPrefetch(
|
|
|
1256
1218
|
}
|
|
1257
1219
|
}
|
|
1258
1220
|
|
|
1259
|
-
async function fetchAnalysisDetailForPrefetch(id: string): Promise<unknown> {
|
|
1260
|
-
try {
|
|
1261
|
-
const data = await callAction("get-analysis", { id }, { method: "GET" });
|
|
1262
|
-
if (!data || (data as Record<string, unknown>).error) return null;
|
|
1263
|
-
return data;
|
|
1264
|
-
} catch {
|
|
1265
|
-
return null;
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
1221
|
const ANALYTICS_ACTIVE_THREAD_KEY = `agent-chat-active-thread:${ANALYTICS_CHAT_STORAGE_KEY}`;
|
|
1270
1222
|
|
|
1271
1223
|
function formatThreadAge(updatedAt: number) {
|
|
@@ -1510,20 +1462,8 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1510
1462
|
);
|
|
1511
1463
|
const [dashShowAll, setDashShowAll] = useState(false);
|
|
1512
1464
|
const [dashFilter, setDashFilter] = useState<SidebarVisibilityFilter>("all");
|
|
1513
|
-
const [analysesOpen, setAnalysesOpen] = useState(
|
|
1514
|
-
() =>
|
|
1515
|
-
getStoredBooleanPreference(ANALYSES_OPEN_KEY) ??
|
|
1516
|
-
activeAnalysisId !== null,
|
|
1517
|
-
);
|
|
1518
|
-
const [analysesShowAll, setAnalysesShowAll] = useState(false);
|
|
1519
|
-
const [analysisFilter, setAnalysisFilter] =
|
|
1520
|
-
useState<SidebarVisibilityFilter>("all");
|
|
1521
|
-
const [analysisHiddenFilter, setAnalysisHiddenFilter] =
|
|
1522
|
-
useState<AnalysisHiddenFilter>("visible");
|
|
1523
1465
|
const [dashboardSortMode, setDashboardSortModeState] =
|
|
1524
1466
|
useState<SidebarSortMode>(() => getStoredSortMode(DASHBOARD_SORT_MODE_KEY));
|
|
1525
|
-
const [analysisSortMode, setAnalysisSortModeState] =
|
|
1526
|
-
useState<SidebarSortMode>(() => getStoredSortMode(ANALYSIS_SORT_MODE_KEY));
|
|
1527
1467
|
const { data: popularity, isReady: popularityReady } = usePopularity();
|
|
1528
1468
|
|
|
1529
1469
|
useEffect(() => {
|
|
@@ -1542,7 +1482,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1542
1482
|
const { mutateAsync: renameDashboard } =
|
|
1543
1483
|
useActionMutation("rename-dashboard");
|
|
1544
1484
|
const { mutateAsync: renameAnalysis } = useActionMutation("rename-analysis");
|
|
1545
|
-
const { mutateAsync: hideAnalysisMut } = useActionMutation("hide-analysis");
|
|
1546
1485
|
const { mutateAsync: setResourceVisibility } = useActionMutation(
|
|
1547
1486
|
"set-resource-visibility",
|
|
1548
1487
|
);
|
|
@@ -1580,10 +1519,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1580
1519
|
const [dashboardOrderState, setDashboardOrderState] = useState(() =>
|
|
1581
1520
|
typeof window === "undefined" ? [] : getDashboardOrder(),
|
|
1582
1521
|
);
|
|
1583
|
-
const [analysisOrderState, setAnalysisOrderState] = useState(() =>
|
|
1584
|
-
typeof window === "undefined" ? [] : getAnalysisOrder(),
|
|
1585
|
-
);
|
|
1586
|
-
|
|
1587
1522
|
// Server-backed favorites
|
|
1588
1523
|
const {
|
|
1589
1524
|
data: favoritesData,
|
|
@@ -1614,11 +1549,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1614
1549
|
setDashboardSortModeState(mode);
|
|
1615
1550
|
}, []);
|
|
1616
1551
|
|
|
1617
|
-
const setAnalysisSortMode = useCallback((mode: SidebarSortMode) => {
|
|
1618
|
-
setStoredSortMode(ANALYSIS_SORT_MODE_KEY, mode);
|
|
1619
|
-
setAnalysisSortModeState(mode);
|
|
1620
|
-
}, []);
|
|
1621
|
-
|
|
1622
1552
|
useEffect(() => {
|
|
1623
1553
|
if (getStoredBooleanPreference(ASK_OPEN_KEY) === null) {
|
|
1624
1554
|
setAskOpen(isAskRoute);
|
|
@@ -1631,12 +1561,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1631
1561
|
}
|
|
1632
1562
|
}, [activeDashboardId]);
|
|
1633
1563
|
|
|
1634
|
-
useEffect(() => {
|
|
1635
|
-
if (getStoredBooleanPreference(ANALYSES_OPEN_KEY) === null) {
|
|
1636
|
-
setAnalysesOpen(activeAnalysisId !== null);
|
|
1637
|
-
}
|
|
1638
|
-
}, [activeAnalysisId]);
|
|
1639
|
-
|
|
1640
1564
|
const toggleAskOpen = useCallback(() => {
|
|
1641
1565
|
setAskOpen((current) => {
|
|
1642
1566
|
const next = !current;
|
|
@@ -1653,14 +1577,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1653
1577
|
});
|
|
1654
1578
|
}, []);
|
|
1655
1579
|
|
|
1656
|
-
const toggleAnalysesOpen = useCallback(() => {
|
|
1657
|
-
setAnalysesOpen((current) => {
|
|
1658
|
-
const next = !current;
|
|
1659
|
-
setStoredBoolean(ANALYSES_OPEN_KEY, next);
|
|
1660
|
-
return next;
|
|
1661
|
-
});
|
|
1662
|
-
}, []);
|
|
1663
|
-
|
|
1664
1580
|
// Fold per-source counters into sidebar list query keys so agent-driven
|
|
1665
1581
|
// create/rename/archive/delete shows up without a manual refresh. We
|
|
1666
1582
|
// Domain counters keep these lists targeted. Folding the generic `action`
|
|
@@ -1671,9 +1587,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1671
1587
|
);
|
|
1672
1588
|
const analysesSync = useSettledSyncVersion(useChangeVersions(["analyses"]));
|
|
1673
1589
|
const dashboardsSyncRef = useRef(dashboardsSync);
|
|
1674
|
-
const analysesSyncRef = useRef(analysesSync);
|
|
1675
1590
|
dashboardsSyncRef.current = dashboardsSync;
|
|
1676
|
-
analysesSyncRef.current = analysesSync;
|
|
1677
1591
|
|
|
1678
1592
|
const {
|
|
1679
1593
|
data: sqlDashboards = [],
|
|
@@ -1694,52 +1608,12 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1694
1608
|
isError: analysesError,
|
|
1695
1609
|
refetch: refetchAnalyses,
|
|
1696
1610
|
} = useQuery({
|
|
1697
|
-
queryKey: ["analyses-sidebar", analysesSync
|
|
1698
|
-
queryFn: () => fetchSidebarAnalyses(t
|
|
1611
|
+
queryKey: ["analyses-sidebar", analysesSync],
|
|
1612
|
+
queryFn: () => fetchSidebarAnalyses(t),
|
|
1699
1613
|
staleTime: 30_000,
|
|
1700
1614
|
placeholderData: (prev) => prev,
|
|
1701
1615
|
});
|
|
1702
1616
|
|
|
1703
|
-
const sortedAnalyses = useMemo(() => {
|
|
1704
|
-
if (analysisSortMode === "alphabetical") {
|
|
1705
|
-
return sortByName(analysesList);
|
|
1706
|
-
}
|
|
1707
|
-
if (analysisSortMode === "manual" && analysisOrderState.length > 0) {
|
|
1708
|
-
return applyOrder(analysesList, analysisOrderState);
|
|
1709
|
-
}
|
|
1710
|
-
return [...analysesList].sort((a, b) => {
|
|
1711
|
-
const aFav = favoriteIds.has(`analysis:${a.id}`) ? 0 : 1;
|
|
1712
|
-
const bFav = favoriteIds.has(`analysis:${b.id}`) ? 0 : 1;
|
|
1713
|
-
if (aFav !== bFav) return aFav - bFav;
|
|
1714
|
-
const aPop = popularityOf(popularity, "analysis", a.id);
|
|
1715
|
-
const bPop = popularityOf(popularity, "analysis", b.id);
|
|
1716
|
-
if (aPop !== bPop) return bPop - aPop;
|
|
1717
|
-
return a.name.localeCompare(b.name);
|
|
1718
|
-
});
|
|
1719
|
-
}, [
|
|
1720
|
-
analysesList,
|
|
1721
|
-
analysisSortMode,
|
|
1722
|
-
analysisOrderState,
|
|
1723
|
-
popularity,
|
|
1724
|
-
favoriteIds,
|
|
1725
|
-
]);
|
|
1726
|
-
|
|
1727
|
-
const filteredAnalyses = useMemo(
|
|
1728
|
-
() =>
|
|
1729
|
-
sortedAnalyses.filter((analysis) =>
|
|
1730
|
-
matchesVisibilityFilter(analysis, analysisFilter),
|
|
1731
|
-
),
|
|
1732
|
-
[sortedAnalyses, analysisFilter],
|
|
1733
|
-
);
|
|
1734
|
-
|
|
1735
|
-
const displayedAnalyses = useMemo(
|
|
1736
|
-
() =>
|
|
1737
|
-
analysesShowAll
|
|
1738
|
-
? filteredAnalyses
|
|
1739
|
-
: filteredAnalyses.slice(0, SIDEBAR_PREVIEW_COUNT),
|
|
1740
|
-
[filteredAnalyses, analysesShowAll],
|
|
1741
|
-
);
|
|
1742
|
-
|
|
1743
1617
|
// Only the active dashboard can display saved views in the sidebar, so avoid
|
|
1744
1618
|
// issuing one request per dashboard on every sidebar mount.
|
|
1745
1619
|
const { views: activeDashboardViews = [] } = useDashboardViews(
|
|
@@ -1772,24 +1646,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1772
1646
|
[dashboardsSync, queryClient, t],
|
|
1773
1647
|
);
|
|
1774
1648
|
|
|
1775
|
-
const prefetchAnalysis = useCallback(
|
|
1776
|
-
(id: string) => {
|
|
1777
|
-
const queryKey = analysisDetailPrefetchKey(id);
|
|
1778
|
-
const cached =
|
|
1779
|
-
queryClient.getQueryData<PrefetchSnapshot<unknown | null>>(queryKey);
|
|
1780
|
-
void import("@/pages/analyses/AnalysisDetail");
|
|
1781
|
-
void queryClient.prefetchQuery({
|
|
1782
|
-
queryKey,
|
|
1783
|
-
queryFn: async () => ({
|
|
1784
|
-
data: await fetchAnalysisDetailForPrefetch(id),
|
|
1785
|
-
syncVersion: analysesSync,
|
|
1786
|
-
}),
|
|
1787
|
-
staleTime: cached?.syncVersion === analysesSync ? 30_000 : 0,
|
|
1788
|
-
});
|
|
1789
|
-
},
|
|
1790
|
-
[analysesSync, queryClient],
|
|
1791
|
-
);
|
|
1792
|
-
|
|
1793
1649
|
const visibleDashboards = useMemo<SidebarDashboard[]>(() => {
|
|
1794
1650
|
const staticItems: SidebarDashboard[] = dashboards
|
|
1795
1651
|
.filter((d) => !hiddenIds.has(d.id))
|
|
@@ -1806,7 +1662,14 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1806
1662
|
visibility: d.visibility,
|
|
1807
1663
|
parentId: d.parentId,
|
|
1808
1664
|
}));
|
|
1809
|
-
const
|
|
1665
|
+
const analysisItems: SidebarDashboard[] = analysesList.map((a) => ({
|
|
1666
|
+
id: `analysis:${a.id}`,
|
|
1667
|
+
resourceId: a.id,
|
|
1668
|
+
name: a.name,
|
|
1669
|
+
source: "analysis",
|
|
1670
|
+
visibility: a.visibility,
|
|
1671
|
+
}));
|
|
1672
|
+
const all = [...staticItems, ...sqlItems, ...analysisItems];
|
|
1810
1673
|
if (dashboardSortMode === "alphabetical") {
|
|
1811
1674
|
return sortByName(all);
|
|
1812
1675
|
}
|
|
@@ -1814,11 +1677,25 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1814
1677
|
return applyOrder(all, dashboardOrderState);
|
|
1815
1678
|
}
|
|
1816
1679
|
return [...all].sort((a, b) => {
|
|
1817
|
-
const
|
|
1818
|
-
const
|
|
1680
|
+
const aResourceId = a.resourceId ?? a.id;
|
|
1681
|
+
const bResourceId = b.resourceId ?? b.id;
|
|
1682
|
+
const aFavoriteKey =
|
|
1683
|
+
a.source === "analysis" ? `analysis:${aResourceId}` : a.id;
|
|
1684
|
+
const bFavoriteKey =
|
|
1685
|
+
b.source === "analysis" ? `analysis:${bResourceId}` : b.id;
|
|
1686
|
+
const aFav = favoriteIds.has(aFavoriteKey) ? 0 : 1;
|
|
1687
|
+
const bFav = favoriteIds.has(bFavoriteKey) ? 0 : 1;
|
|
1819
1688
|
if (aFav !== bFav) return aFav - bFav;
|
|
1820
|
-
const aPop = popularityOf(
|
|
1821
|
-
|
|
1689
|
+
const aPop = popularityOf(
|
|
1690
|
+
popularity,
|
|
1691
|
+
a.source === "analysis" ? "analysis" : "dashboard",
|
|
1692
|
+
aResourceId,
|
|
1693
|
+
);
|
|
1694
|
+
const bPop = popularityOf(
|
|
1695
|
+
popularity,
|
|
1696
|
+
b.source === "analysis" ? "analysis" : "dashboard",
|
|
1697
|
+
bResourceId,
|
|
1698
|
+
);
|
|
1822
1699
|
if (aPop !== bPop) return bPop - aPop;
|
|
1823
1700
|
return a.name.localeCompare(b.name) || a.id.localeCompare(b.id);
|
|
1824
1701
|
});
|
|
@@ -1829,6 +1706,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1829
1706
|
dashboardSortMode,
|
|
1830
1707
|
dashboardOrderState,
|
|
1831
1708
|
sqlDashboards,
|
|
1709
|
+
analysesList,
|
|
1832
1710
|
popularity,
|
|
1833
1711
|
]);
|
|
1834
1712
|
|
|
@@ -1905,6 +1783,12 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1905
1783
|
|
|
1906
1784
|
const handleDashboardDelete = useCallback(
|
|
1907
1785
|
async (d: SidebarDashboard) => {
|
|
1786
|
+
if (d.source === "analysis") {
|
|
1787
|
+
await deleteAnalysisMut({ id: d.resourceId ?? d.id });
|
|
1788
|
+
queryClient.invalidateQueries({ queryKey: ["analyses-sidebar"] });
|
|
1789
|
+
queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1908
1792
|
if (d.source === "static") {
|
|
1909
1793
|
hideDashboard(d.id);
|
|
1910
1794
|
setHiddenIds(getHiddenDashboards());
|
|
@@ -1931,11 +1815,12 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1931
1815
|
throw err;
|
|
1932
1816
|
}
|
|
1933
1817
|
},
|
|
1934
|
-
[
|
|
1818
|
+
[deleteAnalysisMut, deleteSqlDashboard, queryClient],
|
|
1935
1819
|
);
|
|
1936
1820
|
|
|
1937
1821
|
const handleDashboardArchive = useCallback(
|
|
1938
1822
|
async (d: SidebarDashboard) => {
|
|
1823
|
+
if (d.source === "analysis") return;
|
|
1939
1824
|
if (d.source === "static") {
|
|
1940
1825
|
// Static dashboards can only be hidden, not archived; route to delete
|
|
1941
1826
|
// (which calls hideDashboard for static items).
|
|
@@ -1970,6 +1855,16 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
1970
1855
|
const trimmed = name.trim();
|
|
1971
1856
|
if (!trimmed || trimmed === d.name) return;
|
|
1972
1857
|
|
|
1858
|
+
if (d.source === "analysis") {
|
|
1859
|
+
await renameAnalysis({ id: d.resourceId ?? d.id, name: trimmed });
|
|
1860
|
+
queryClient.invalidateQueries({ queryKey: ["analyses-sidebar"] });
|
|
1861
|
+
queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
|
|
1862
|
+
queryClient.invalidateQueries({
|
|
1863
|
+
queryKey: ["analysis-detail", d.resourceId ?? d.id],
|
|
1864
|
+
});
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1973
1868
|
if (d.source === "static") {
|
|
1974
1869
|
setStaticDashboardRenamesState((prev) => {
|
|
1975
1870
|
const next = { ...prev, [d.id]: trimmed };
|
|
@@ -2001,38 +1896,27 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2001
1896
|
throw err;
|
|
2002
1897
|
}
|
|
2003
1898
|
},
|
|
2004
|
-
[queryClient, renameDashboard],
|
|
2005
|
-
);
|
|
2006
|
-
|
|
2007
|
-
const handleAnalysisDelete = useCallback(
|
|
2008
|
-
async (a: { id: string; name: string }) => {
|
|
2009
|
-
const queryKey = ["analyses-sidebar"] as const;
|
|
2010
|
-
const prev = getQuerySnapshots<{ id: string; name: string }[]>(
|
|
2011
|
-
queryClient,
|
|
2012
|
-
queryKey,
|
|
2013
|
-
);
|
|
2014
|
-
queryClient.setQueriesData<{ id: string; name: string }[]>(
|
|
2015
|
-
{ queryKey },
|
|
2016
|
-
(old) => (old ?? []).filter((item) => item.id !== a.id),
|
|
2017
|
-
);
|
|
2018
|
-
try {
|
|
2019
|
-
await deleteAnalysisMut({ id: a.id });
|
|
2020
|
-
queryClient.removeQueries({
|
|
2021
|
-
queryKey: analysisDetailPrefetchKey(a.id),
|
|
2022
|
-
});
|
|
2023
|
-
queryClient.invalidateQueries({ queryKey });
|
|
2024
|
-
queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
|
|
2025
|
-
} catch (err) {
|
|
2026
|
-
restoreQuerySnapshots(queryClient, prev);
|
|
2027
|
-
throw err;
|
|
2028
|
-
}
|
|
2029
|
-
},
|
|
2030
|
-
[queryClient, deleteAnalysisMut],
|
|
1899
|
+
[queryClient, renameAnalysis, renameDashboard],
|
|
2031
1900
|
);
|
|
2032
1901
|
|
|
2033
1902
|
const handleDashboardSetVisibility = useCallback(
|
|
2034
1903
|
async (d: SidebarDashboard, visibility: Visibility) => {
|
|
2035
1904
|
if (d.source === "static") return;
|
|
1905
|
+
if (d.source === "analysis") {
|
|
1906
|
+
await setResourceVisibility({
|
|
1907
|
+
resourceType: "analysis",
|
|
1908
|
+
resourceId: d.resourceId ?? d.id,
|
|
1909
|
+
visibility,
|
|
1910
|
+
} as any);
|
|
1911
|
+
queryClient.invalidateQueries({ queryKey: ["analyses-sidebar"] });
|
|
1912
|
+
queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
|
|
1913
|
+
toast.success(
|
|
1914
|
+
visibility === "org"
|
|
1915
|
+
? t("sidebar.nameSharedWithOrg", { name: d.name })
|
|
1916
|
+
: t("sidebar.nameMadePrivate", { name: d.name }),
|
|
1917
|
+
);
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
2036
1920
|
const queryKey = [
|
|
2037
1921
|
"sql-dashboards-sidebar",
|
|
2038
1922
|
dashboardsSyncRef.current,
|
|
@@ -2066,143 +1950,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2066
1950
|
[queryClient, setResourceVisibility, t],
|
|
2067
1951
|
);
|
|
2068
1952
|
|
|
2069
|
-
const handleAnalysisSetVisibility = useCallback(
|
|
2070
|
-
async (a: { id: string; name: string }, visibility: Visibility) => {
|
|
2071
|
-
const sidebarKey = ["analyses-sidebar", analysesSyncRef.current] as const;
|
|
2072
|
-
const listKey = ["analyses-list"] as const;
|
|
2073
|
-
const prevSidebar = getQuerySnapshots<
|
|
2074
|
-
{ id: string; name: string; visibility?: Visibility }[]
|
|
2075
|
-
>(queryClient, sidebarKey);
|
|
2076
|
-
const prevList = getQuerySnapshots<any[]>(queryClient, listKey);
|
|
2077
|
-
queryClient.setQueriesData<
|
|
2078
|
-
{ id: string; name: string; visibility?: Visibility }[]
|
|
2079
|
-
>({ queryKey: sidebarKey }, (old) =>
|
|
2080
|
-
(old ?? []).map((item) =>
|
|
2081
|
-
item.id === a.id ? { ...item, visibility } : item,
|
|
2082
|
-
),
|
|
2083
|
-
);
|
|
2084
|
-
queryClient.setQueriesData<any[]>({ queryKey: listKey }, (old) =>
|
|
2085
|
-
(old ?? []).map((item) =>
|
|
2086
|
-
item.id === a.id ? { ...item, visibility } : item,
|
|
2087
|
-
),
|
|
2088
|
-
);
|
|
2089
|
-
try {
|
|
2090
|
-
await setResourceVisibility({
|
|
2091
|
-
resourceType: "analysis",
|
|
2092
|
-
resourceId: a.id,
|
|
2093
|
-
visibility,
|
|
2094
|
-
} as any);
|
|
2095
|
-
queryClient.invalidateQueries({ queryKey: sidebarKey });
|
|
2096
|
-
queryClient.invalidateQueries({ queryKey: listKey });
|
|
2097
|
-
toast.success(
|
|
2098
|
-
visibility === "org"
|
|
2099
|
-
? t("sidebar.nameSharedWithOrg", { name: a.name })
|
|
2100
|
-
: t("sidebar.nameMadePrivate", { name: a.name }),
|
|
2101
|
-
);
|
|
2102
|
-
} catch (err) {
|
|
2103
|
-
restoreQuerySnapshots(queryClient, prevSidebar);
|
|
2104
|
-
restoreQuerySnapshots(queryClient, prevList);
|
|
2105
|
-
throw err;
|
|
2106
|
-
}
|
|
2107
|
-
},
|
|
2108
|
-
[queryClient, setResourceVisibility, t],
|
|
2109
|
-
);
|
|
2110
|
-
|
|
2111
|
-
const handleAnalysisRename = useCallback(
|
|
2112
|
-
async (a: { id: string; name: string }, name: string) => {
|
|
2113
|
-
const trimmed = name.trim();
|
|
2114
|
-
if (!trimmed || trimmed === a.name) return;
|
|
2115
|
-
|
|
2116
|
-
const sidebarKey = ["analyses-sidebar"] as const;
|
|
2117
|
-
const listKey = ["analyses-list"] as const;
|
|
2118
|
-
const detailKey = ["analysis-detail", a.id] as const;
|
|
2119
|
-
const prevSidebar = getQuerySnapshots<{ id: string; name: string }[]>(
|
|
2120
|
-
queryClient,
|
|
2121
|
-
sidebarKey,
|
|
2122
|
-
);
|
|
2123
|
-
const prevList = getQuerySnapshots<any[]>(queryClient, listKey);
|
|
2124
|
-
const prevDetail = getQuerySnapshots<any>(queryClient, detailKey);
|
|
2125
|
-
|
|
2126
|
-
queryClient.setQueriesData<{ id: string; name: string }[]>(
|
|
2127
|
-
{ queryKey: sidebarKey },
|
|
2128
|
-
(old) =>
|
|
2129
|
-
(old ?? []).map((item) =>
|
|
2130
|
-
item.id === a.id ? { ...item, name: trimmed } : item,
|
|
2131
|
-
),
|
|
2132
|
-
);
|
|
2133
|
-
queryClient.setQueriesData<any[]>({ queryKey: listKey }, (old) =>
|
|
2134
|
-
(old ?? []).map((item) =>
|
|
2135
|
-
item.id === a.id ? { ...item, name: trimmed } : item,
|
|
2136
|
-
),
|
|
2137
|
-
);
|
|
2138
|
-
queryClient.setQueriesData<any>({ queryKey: detailKey }, (old: any) =>
|
|
2139
|
-
old ? { ...old, name: trimmed } : old,
|
|
2140
|
-
);
|
|
2141
|
-
|
|
2142
|
-
try {
|
|
2143
|
-
await renameAnalysis({ id: a.id, name: trimmed });
|
|
2144
|
-
queryClient.removeQueries({
|
|
2145
|
-
queryKey: analysisDetailPrefetchKey(a.id),
|
|
2146
|
-
});
|
|
2147
|
-
queryClient.invalidateQueries({ queryKey: sidebarKey });
|
|
2148
|
-
queryClient.invalidateQueries({ queryKey: listKey });
|
|
2149
|
-
queryClient.invalidateQueries({ queryKey: detailKey });
|
|
2150
|
-
} catch (err) {
|
|
2151
|
-
restoreQuerySnapshots(queryClient, prevSidebar);
|
|
2152
|
-
restoreQuerySnapshots(queryClient, prevList);
|
|
2153
|
-
restoreQuerySnapshots(queryClient, prevDetail);
|
|
2154
|
-
throw err;
|
|
2155
|
-
}
|
|
2156
|
-
},
|
|
2157
|
-
[queryClient, renameAnalysis],
|
|
2158
|
-
);
|
|
2159
|
-
|
|
2160
|
-
const handleAnalysisHide = useCallback(
|
|
2161
|
-
async (a: { id: string; name: string }) => {
|
|
2162
|
-
const sidebarKey = ["analyses-sidebar"] as const;
|
|
2163
|
-
const prev = getQuerySnapshots<{ id: string }[]>(queryClient, sidebarKey);
|
|
2164
|
-
// Optimistically drop it from the visible list (the hidden-filter
|
|
2165
|
-
// variant is invalidated below to pick it up).
|
|
2166
|
-
queryClient.setQueriesData<{ id: string }[]>(
|
|
2167
|
-
{ queryKey: sidebarKey },
|
|
2168
|
-
(old) => (old ?? []).filter((item) => item.id !== a.id),
|
|
2169
|
-
);
|
|
2170
|
-
try {
|
|
2171
|
-
await hideAnalysisMut({ id: a.id, hidden: true });
|
|
2172
|
-
queryClient.invalidateQueries({ queryKey: sidebarKey });
|
|
2173
|
-
queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
|
|
2174
|
-
toast.success(t("sidebar.nameHidden", { name: a.name }));
|
|
2175
|
-
} catch (err) {
|
|
2176
|
-
restoreQuerySnapshots(queryClient, prev);
|
|
2177
|
-
throw err;
|
|
2178
|
-
}
|
|
2179
|
-
},
|
|
2180
|
-
[queryClient, hideAnalysisMut, t],
|
|
2181
|
-
);
|
|
2182
|
-
|
|
2183
|
-
const handleAnalysisUnhide = useCallback(
|
|
2184
|
-
async (a: { id: string; name: string }) => {
|
|
2185
|
-
const sidebarKey = ["analyses-sidebar"] as const;
|
|
2186
|
-
const prev = getQuerySnapshots<{ id: string }[]>(queryClient, sidebarKey);
|
|
2187
|
-
// Optimistically drop it from the hidden list (the visible variant is
|
|
2188
|
-
// invalidated below to pick it up).
|
|
2189
|
-
queryClient.setQueriesData<{ id: string }[]>(
|
|
2190
|
-
{ queryKey: sidebarKey },
|
|
2191
|
-
(old) => (old ?? []).filter((item) => item.id !== a.id),
|
|
2192
|
-
);
|
|
2193
|
-
try {
|
|
2194
|
-
await hideAnalysisMut({ id: a.id, hidden: false });
|
|
2195
|
-
queryClient.invalidateQueries({ queryKey: sidebarKey });
|
|
2196
|
-
queryClient.invalidateQueries({ queryKey: ["analyses-list"] });
|
|
2197
|
-
toast.success(t("sidebar.nameUnhidden", { name: a.name }));
|
|
2198
|
-
} catch (err) {
|
|
2199
|
-
restoreQuerySnapshots(queryClient, prev);
|
|
2200
|
-
throw err;
|
|
2201
|
-
}
|
|
2202
|
-
},
|
|
2203
|
-
[queryClient, hideAnalysisMut, t],
|
|
2204
|
-
);
|
|
2205
|
-
|
|
2206
1953
|
const sensors = useSensors(
|
|
2207
1954
|
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
|
2208
1955
|
useSensor(KeyboardSensor, {
|
|
@@ -2226,24 +1973,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2226
1973
|
[setDashboardSortMode, dashboardRenderOrderIds],
|
|
2227
1974
|
);
|
|
2228
1975
|
|
|
2229
|
-
const handleAnalysisDragEnd = useCallback(
|
|
2230
|
-
(event: DragEndEvent) => {
|
|
2231
|
-
const { active, over } = event;
|
|
2232
|
-
if (!over || active.id === over.id) return;
|
|
2233
|
-
setAnalysisSortMode("manual");
|
|
2234
|
-
setAnalysisOrderState((prev) => {
|
|
2235
|
-
const ids = prev.length > 0 ? prev : sortedAnalyses.map((a) => a.id);
|
|
2236
|
-
const oldIndex = ids.indexOf(active.id as string);
|
|
2237
|
-
const newIndex = ids.indexOf(over.id as string);
|
|
2238
|
-
if (oldIndex === -1 || newIndex === -1) return prev;
|
|
2239
|
-
const newOrder = arrayMove(ids, oldIndex, newIndex);
|
|
2240
|
-
setAnalysisOrder(newOrder);
|
|
2241
|
-
return newOrder;
|
|
2242
|
-
});
|
|
2243
|
-
},
|
|
2244
|
-
[setAnalysisSortMode, sortedAnalyses],
|
|
2245
|
-
);
|
|
2246
|
-
|
|
2247
1976
|
const handleResizeStart = useCallback(
|
|
2248
1977
|
(e: React.MouseEvent) => {
|
|
2249
1978
|
if (effectiveCollapsed) return;
|
|
@@ -2283,7 +2012,8 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2283
2012
|
|
|
2284
2013
|
const isAdhocActive =
|
|
2285
2014
|
location.pathname.startsWith("/adhoc") ||
|
|
2286
|
-
location.pathname.startsWith("/dashboards")
|
|
2015
|
+
location.pathname.startsWith("/dashboards") ||
|
|
2016
|
+
location.pathname.startsWith("/analyses");
|
|
2287
2017
|
|
|
2288
2018
|
useEffect(() => {
|
|
2289
2019
|
if (typeof window === "undefined") return;
|
|
@@ -2322,12 +2052,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2322
2052
|
href: "/dashboards",
|
|
2323
2053
|
active: isAdhocActive,
|
|
2324
2054
|
},
|
|
2325
|
-
{
|
|
2326
|
-
icon: IconReportAnalytics,
|
|
2327
|
-
label: t("navigation.analyses"),
|
|
2328
|
-
href: "/analyses",
|
|
2329
|
-
active: location.pathname.startsWith("/analyses"),
|
|
2330
|
-
},
|
|
2331
2055
|
{
|
|
2332
2056
|
icon: IconPlayerPlay,
|
|
2333
2057
|
label: t("navigation.sessions"),
|
|
@@ -2665,7 +2389,11 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2665
2389
|
<Fragment key={d.id}>
|
|
2666
2390
|
<SortableDashboardItem
|
|
2667
2391
|
d={d}
|
|
2668
|
-
isActive={
|
|
2392
|
+
isActive={
|
|
2393
|
+
d.source === "analysis"
|
|
2394
|
+
? activeAnalysisId === d.resourceId
|
|
2395
|
+
: activeDashboardId === d.id
|
|
2396
|
+
}
|
|
2669
2397
|
location={location}
|
|
2670
2398
|
favoriteIds={favoriteIds}
|
|
2671
2399
|
onToggleFavorite={toggleFavorite}
|
|
@@ -2683,7 +2411,10 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2683
2411
|
key={child.id}
|
|
2684
2412
|
d={child}
|
|
2685
2413
|
isActive={
|
|
2686
|
-
|
|
2414
|
+
child.source === "analysis"
|
|
2415
|
+
? activeAnalysisId ===
|
|
2416
|
+
child.resourceId
|
|
2417
|
+
: activeDashboardId === child.id
|
|
2687
2418
|
}
|
|
2688
2419
|
location={location}
|
|
2689
2420
|
favoriteIds={favoriteIds}
|
|
@@ -2746,156 +2477,30 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2746
2477
|
onRetry={() => void refetchSqlDashboards()}
|
|
2747
2478
|
/>
|
|
2748
2479
|
)}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
)}
|
|
2765
|
-
>
|
|
2766
|
-
<button
|
|
2767
|
-
type="button"
|
|
2768
|
-
onClick={toggleAnalysesOpen}
|
|
2769
|
-
className="flex min-w-0 flex-1 items-center gap-3 px-3 py-2 text-start"
|
|
2770
|
-
aria-expanded={analysesOpen}
|
|
2771
|
-
>
|
|
2772
|
-
<IconReportAnalytics className="h-4 w-4 shrink-0" />
|
|
2773
|
-
<span className="min-w-0 flex-1 truncate">
|
|
2774
|
-
{t("navigation.analyses")}
|
|
2775
|
-
</span>
|
|
2776
|
-
</button>
|
|
2777
|
-
<SidebarSectionSettingsPopover
|
|
2778
|
-
label={t("navigation.analyses")}
|
|
2779
|
-
sortMode={analysisSortMode}
|
|
2780
|
-
onSortModeChange={setAnalysisSortMode}
|
|
2781
|
-
visibilityFilter={analysisFilter}
|
|
2782
|
-
onVisibilityFilterChange={setAnalysisFilter}
|
|
2783
|
-
showHidden={analysisHiddenFilter === "hidden"}
|
|
2784
|
-
onShowHiddenChange={(checked) =>
|
|
2785
|
-
setAnalysisHiddenFilter(checked ? "hidden" : "visible")
|
|
2786
|
-
}
|
|
2787
|
-
/>
|
|
2788
|
-
<button
|
|
2789
|
-
type="button"
|
|
2790
|
-
onClick={toggleAnalysesOpen}
|
|
2791
|
-
className="me-1 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground/70 hover:bg-sidebar-accent hover:text-foreground"
|
|
2792
|
-
aria-label={
|
|
2793
|
-
analysesOpen
|
|
2794
|
-
? t("sidebar.collapseAnalyses")
|
|
2795
|
-
: t("sidebar.expandAnalyses")
|
|
2796
|
-
}
|
|
2797
|
-
>
|
|
2798
|
-
<IconChevronDown
|
|
2799
|
-
className={cn(
|
|
2800
|
-
"h-3.5 w-3.5 shrink-0 transition-transform",
|
|
2801
|
-
!analysesOpen && "-rotate-90",
|
|
2802
|
-
)}
|
|
2803
|
-
/>
|
|
2804
|
-
</button>
|
|
2805
|
-
</div>
|
|
2806
|
-
|
|
2807
|
-
{analysesOpen && (
|
|
2808
|
-
<DndContext
|
|
2809
|
-
sensors={sensors}
|
|
2810
|
-
collisionDetection={closestCenter}
|
|
2811
|
-
onDragEnd={handleAnalysisDragEnd}
|
|
2812
|
-
>
|
|
2813
|
-
<SortableContext
|
|
2814
|
-
items={displayedAnalyses.map((a) => a.id)}
|
|
2815
|
-
strategy={verticalListSortingStrategy}
|
|
2816
|
-
>
|
|
2817
|
-
<div className="ms-4 min-w-0 space-y-0.5">
|
|
2818
|
-
{displayedAnalyses.map((a) => (
|
|
2819
|
-
<SortableRow
|
|
2820
|
-
key={a.id}
|
|
2821
|
-
id={a.id}
|
|
2822
|
-
favoriteKey={`analysis:${a.id}`}
|
|
2823
|
-
name={a.name}
|
|
2824
|
-
href={`/analyses/${a.id}`}
|
|
2825
|
-
isActive={location.pathname === `/analyses/${a.id}`}
|
|
2826
|
-
favoriteIds={favoriteIds}
|
|
2827
|
-
onToggleFavorite={toggleFavorite}
|
|
2828
|
-
onDelete={() => handleAnalysisDelete(a)}
|
|
2829
|
-
onRename={(name) => handleAnalysisRename(a, name)}
|
|
2830
|
-
hidden={analysisHiddenFilter === "hidden"}
|
|
2831
|
-
onHide={
|
|
2832
|
-
analysisHiddenFilter === "hidden"
|
|
2833
|
-
? undefined
|
|
2834
|
-
: () => handleAnalysisHide(a)
|
|
2835
|
-
}
|
|
2836
|
-
onUnhide={
|
|
2837
|
-
analysisHiddenFilter === "hidden"
|
|
2838
|
-
? () => handleAnalysisUnhide(a)
|
|
2839
|
-
: undefined
|
|
2840
|
-
}
|
|
2841
|
-
visibility={a.visibility}
|
|
2842
|
-
onSetVisibility={(v) =>
|
|
2843
|
-
handleAnalysisSetVisibility(a, v)
|
|
2844
|
-
}
|
|
2845
|
-
onPrefetch={() => prefetchAnalysis(a.id)}
|
|
2846
|
-
/>
|
|
2847
|
-
))}
|
|
2848
|
-
{filteredAnalyses.length > SIDEBAR_PREVIEW_COUNT && (
|
|
2849
|
-
<button
|
|
2850
|
-
onClick={() => setAnalysesShowAll(!analysesShowAll)}
|
|
2851
|
-
className="flex items-center gap-1 px-3 py-1 text-[11px] text-muted-foreground/70 hover:text-primary"
|
|
2852
|
-
>
|
|
2853
|
-
{analysesShowAll
|
|
2854
|
-
? t("sidebar.showLess")
|
|
2855
|
-
: t("sidebar.showMore", {
|
|
2856
|
-
count:
|
|
2857
|
-
filteredAnalyses.length -
|
|
2858
|
-
SIDEBAR_PREVIEW_COUNT,
|
|
2859
|
-
})}
|
|
2860
|
-
</button>
|
|
2480
|
+
{analysesLoading && analysesList.length === 0 && (
|
|
2481
|
+
<div className="flex items-center gap-2 px-3 py-1">
|
|
2482
|
+
<Skeleton
|
|
2483
|
+
className={cn(
|
|
2484
|
+
"h-3.5 w-3.5 shrink-0 rounded-sm",
|
|
2485
|
+
SIDEBAR_SKELETON_CLASS,
|
|
2486
|
+
)}
|
|
2487
|
+
/>
|
|
2488
|
+
<Skeleton
|
|
2489
|
+
className={cn(
|
|
2490
|
+
"h-3 w-3/4 rounded",
|
|
2491
|
+
SIDEBAR_SKELETON_CLASS,
|
|
2492
|
+
)}
|
|
2493
|
+
/>
|
|
2494
|
+
</div>
|
|
2861
2495
|
)}
|
|
2862
|
-
{
|
|
2863
|
-
sortedAnalyses.length === 0 &&
|
|
2864
|
-
Array.from({ length: 3 }).map((_, i) => (
|
|
2865
|
-
<div
|
|
2866
|
-
key={`analysis-skeleton-${i}`}
|
|
2867
|
-
className="flex items-center gap-2 px-3 py-1"
|
|
2868
|
-
>
|
|
2869
|
-
<Skeleton
|
|
2870
|
-
className={cn(
|
|
2871
|
-
"h-3.5 w-3.5 shrink-0 rounded-sm",
|
|
2872
|
-
SIDEBAR_SKELETON_CLASS,
|
|
2873
|
-
)}
|
|
2874
|
-
/>
|
|
2875
|
-
<Skeleton
|
|
2876
|
-
className={cn(
|
|
2877
|
-
"h-3 rounded",
|
|
2878
|
-
SIDEBAR_SKELETON_CLASS,
|
|
2879
|
-
)}
|
|
2880
|
-
style={{ width: `${60 + ((i * 17) % 30)}%` }}
|
|
2881
|
-
/>
|
|
2882
|
-
</div>
|
|
2883
|
-
))}
|
|
2884
|
-
{analysesError && sortedAnalyses.length === 0 && (
|
|
2496
|
+
{analysesError && analysesList.length === 0 && (
|
|
2885
2497
|
<SidebarLoadError
|
|
2886
2498
|
message={t("sidebar.analysesLoadFailed")}
|
|
2887
2499
|
retryLabel={t("sidebar.retry")}
|
|
2888
2500
|
onRetry={() => void refetchAnalyses()}
|
|
2889
2501
|
/>
|
|
2890
2502
|
)}
|
|
2891
|
-
|
|
2892
|
-
!analysesError &&
|
|
2893
|
-
sortedAnalyses.length === 0 && (
|
|
2894
|
-
<p className="px-3 py-1 text-[11px] text-muted-foreground/60">
|
|
2895
|
-
{t("sidebar.noAnalysesYet")}
|
|
2896
|
-
</p>
|
|
2897
|
-
)}
|
|
2898
|
-
<NewAnalysisDialog />
|
|
2503
|
+
<NewDashboardDialog />
|
|
2899
2504
|
</div>
|
|
2900
2505
|
</SortableContext>
|
|
2901
2506
|
</DndContext>
|
|
@@ -2926,10 +2531,6 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2926
2531
|
})}
|
|
2927
2532
|
</nav>
|
|
2928
2533
|
|
|
2929
|
-
<div className="min-w-0">
|
|
2930
|
-
<ExtensionsSidebarSection />
|
|
2931
|
-
</div>
|
|
2932
|
-
|
|
2933
2534
|
<div className="space-y-2 pt-2">
|
|
2934
2535
|
<OrgSwitcher />
|
|
2935
2536
|
<TooltipProvider delayDuration={200}>
|