@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.
Files changed (170) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +9 -0
  3. package/corpus/core/docs/content/extensions.mdx +23 -4
  4. package/corpus/core/package.json +1 -1
  5. package/corpus/core/src/agent/production-agent.ts +4 -0
  6. package/corpus/core/src/agent/run-manager.ts +36 -10
  7. package/corpus/core/src/agent/types.ts +2 -0
  8. package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +16 -3
  9. package/corpus/core/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
  10. package/corpus/core/src/client/settings/SettingsPanel.tsx +10 -0
  11. package/corpus/core/src/deploy/build.ts +3 -0
  12. package/corpus/core/src/server/analytics.ts +154 -25
  13. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
  14. package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +2 -2
  15. package/corpus/core/src/templates/chat/netlify.toml +1 -0
  16. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  17. package/corpus/core/src/vite/client.ts +6 -0
  18. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +27 -43
  19. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -8
  20. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +2 -2
  21. package/corpus/templates/analytics/AGENTS.md +20 -8
  22. package/corpus/templates/analytics/actions/get-explorer-dashboard.ts +3 -0
  23. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +140 -46
  24. package/corpus/templates/analytics/actions/migrate-analytics-artifacts.ts +48 -0
  25. package/corpus/templates/analytics/actions/save-analysis.ts +2 -3
  26. package/corpus/templates/analytics/actions/view-screen.ts +1 -1
  27. package/corpus/templates/analytics/app/components/dashboard/DashboardMetadata.tsx +77 -0
  28. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
  29. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  30. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +103 -502
  31. package/corpus/templates/analytics/app/global.css +30 -0
  32. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -2
  33. package/corpus/templates/analytics/app/i18n-data.ts +80 -0
  34. package/corpus/templates/analytics/app/pages/Agents.tsx +49 -2
  35. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +54 -11
  36. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -53
  37. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +23 -25
  38. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
  39. package/corpus/templates/analytics/app/routes/analyses._index.tsx +3 -2
  40. package/corpus/templates/analytics/app/routes/extensions._index.tsx +2 -2
  41. package/corpus/templates/analytics/changelog/2026-07-23-analytics-now-groups-saved-analyses-and-embedded-extensions-.md +6 -0
  42. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-menus-show-who-created-and-last-updated-each-dashb.md +6 -0
  43. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-usage-now-loads-reliably-for-organizations-with-ma.md +6 -0
  44. package/corpus/templates/analytics/changelog/2026-07-23-long-dashboard-building-requests-now-continue-without-stoppi.md +6 -0
  45. package/corpus/templates/analytics/netlify.toml +4 -0
  46. package/corpus/templates/analytics/server/db/schema.ts +4 -0
  47. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -1
  48. package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +1 -0
  49. package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +1062 -0
  50. package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -52
  51. package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -1
  52. package/corpus/templates/analytics/server/plugins/db.ts +10 -0
  53. package/corpus/templates/assets/actions/generate-image-batch.ts +36 -1
  54. package/corpus/templates/assets/actions/generate-image.ts +28 -1
  55. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +46 -8
  56. package/corpus/templates/assets/app/routes/extensions._index.tsx +2 -2
  57. package/corpus/templates/assets/changelog/2026-07-23-assets-image-generation-now-recovers-after-a-completed-tool-.md +6 -0
  58. package/corpus/templates/assets/netlify.toml +1 -0
  59. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -8
  60. package/corpus/templates/brain/app/routes/extensions._index.tsx +2 -2
  61. package/corpus/templates/brain/netlify.toml +1 -0
  62. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +43 -5
  63. package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +2 -16
  64. package/corpus/templates/calendar/netlify.toml +1 -0
  65. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +35 -6
  66. package/corpus/templates/chat/app/routes/extensions._index.tsx +2 -2
  67. package/corpus/templates/chat/netlify.toml +1 -0
  68. package/corpus/templates/clips/app/components/library/library-layout.tsx +52 -6
  69. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +2 -16
  70. package/corpus/templates/clips/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  71. package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +6 -0
  72. package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +1 -1
  73. package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +2 -0
  74. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +1 -3
  75. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +86 -0
  76. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +24 -0
  77. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +5 -69
  78. package/corpus/templates/clips/desktop/src/styles.css +35 -3
  79. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +13 -44
  80. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +119 -17
  81. package/corpus/templates/clips/netlify.toml +1 -0
  82. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +3 -7
  83. package/corpus/templates/content/app/routes/_app.extensions._index.tsx +2 -2
  84. package/corpus/templates/content/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  85. package/corpus/templates/content/netlify.toml +1 -0
  86. package/corpus/templates/design/app/components/layout/Sidebar.tsx +55 -17
  87. package/corpus/templates/design/app/routes/extensions._index.tsx +2 -2
  88. package/corpus/templates/design/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  89. package/corpus/templates/design/netlify.toml +1 -0
  90. package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -1
  91. package/corpus/templates/dispatch/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  92. package/corpus/templates/dispatch/netlify.toml +1 -0
  93. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +41 -45
  94. package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +2 -2
  95. package/corpus/templates/forms/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  96. package/corpus/templates/forms/netlify.toml +1 -0
  97. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +36 -5
  98. package/corpus/templates/macros/app/routes/extensions._index.tsx +2 -2
  99. package/corpus/templates/macros/netlify.toml +1 -0
  100. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +0 -9
  101. package/corpus/templates/mail/app/routes/extensions._index.tsx +2 -2
  102. package/corpus/templates/mail/netlify.toml +1 -0
  103. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +38 -5
  104. package/corpus/templates/plan/app/routes/extensions._index.tsx +2 -2
  105. package/corpus/templates/plan/netlify.toml +1 -0
  106. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +47 -4
  107. package/corpus/templates/slides/app/routes/extensions._index.tsx +2 -2
  108. package/corpus/templates/slides/netlify.toml +1 -0
  109. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +37 -6
  110. package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -2
  111. package/corpus/templates/tasks/app/routes/settings.tsx +31 -0
  112. package/corpus/templates/tasks/netlify.toml +1 -0
  113. package/dist/agent/production-agent.d.ts.map +1 -1
  114. package/dist/agent/production-agent.js +4 -0
  115. package/dist/agent/production-agent.js.map +1 -1
  116. package/dist/agent/run-manager.d.ts +9 -0
  117. package/dist/agent/run-manager.d.ts.map +1 -1
  118. package/dist/agent/run-manager.js +25 -10
  119. package/dist/agent/run-manager.js.map +1 -1
  120. package/dist/agent/types.d.ts +2 -2
  121. package/dist/agent/types.d.ts.map +1 -1
  122. package/dist/agent/types.js.map +1 -1
  123. package/dist/client/extensions/ExtensionsListPage.d.ts +5 -1
  124. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
  125. package/dist/client/extensions/ExtensionsListPage.js +5 -3
  126. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  127. package/dist/client/settings/ExtensionsSettingsContent.d.ts +2 -0
  128. package/dist/client/settings/ExtensionsSettingsContent.d.ts.map +1 -0
  129. package/dist/client/settings/ExtensionsSettingsContent.js +10 -0
  130. package/dist/client/settings/ExtensionsSettingsContent.js.map +1 -0
  131. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  132. package/dist/client/settings/SettingsPanel.js +10 -1
  133. package/dist/client/settings/SettingsPanel.js.map +1 -1
  134. package/dist/collab/awareness.d.ts +2 -2
  135. package/dist/collab/awareness.d.ts.map +1 -1
  136. package/dist/deploy/build.js +1 -0
  137. package/dist/deploy/build.js.map +1 -1
  138. package/dist/observability/routes.d.ts +3 -3
  139. package/dist/progress/routes.d.ts +1 -1
  140. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  141. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  142. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  143. package/dist/server/analytics.d.ts +9 -4
  144. package/dist/server/analytics.d.ts.map +1 -1
  145. package/dist/server/analytics.js +111 -22
  146. package/dist/server/analytics.js.map +1 -1
  147. package/dist/templates/chat/app/components/layout/Sidebar.tsx +35 -6
  148. package/dist/templates/chat/app/routes/extensions._index.tsx +2 -2
  149. package/dist/templates/chat/netlify.toml +1 -0
  150. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  151. package/dist/vite/client.d.ts.map +1 -1
  152. package/dist/vite/client.js +2 -0
  153. package/dist/vite/client.js.map +1 -1
  154. package/docs/content/extensions.mdx +23 -4
  155. package/package.json +1 -1
  156. package/src/agent/production-agent.ts +4 -0
  157. package/src/agent/run-manager.ts +36 -10
  158. package/src/agent/types.ts +2 -0
  159. package/src/client/extensions/ExtensionsListPage.tsx +16 -3
  160. package/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
  161. package/src/client/settings/SettingsPanel.tsx +10 -0
  162. package/src/deploy/build.ts +3 -0
  163. package/src/server/analytics.ts +154 -25
  164. package/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
  165. package/src/templates/chat/app/routes/extensions._index.tsx +2 -2
  166. package/src/templates/chat/netlify.toml +1 -0
  167. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  168. package/src/vite/client.ts +6 -0
  169. package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +0 -165
  170. 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 href = `/dashboards/${d.id}`;
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={d.id}
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={onArchive ? () => onArchive(d) : undefined}
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, analysisHiddenFilter],
1698
- queryFn: () => fetchSidebarAnalyses(t, analysisHiddenFilter),
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 all = [...staticItems, ...sqlItems];
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 aFav = favoriteIds.has(a.id) ? 0 : 1;
1818
- const bFav = favoriteIds.has(b.id) ? 0 : 1;
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(popularity, "dashboard", a.id);
1821
- const bPop = popularityOf(popularity, "dashboard", b.id);
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
- [queryClient, deleteSqlDashboard],
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={activeDashboardId === d.id}
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
- activeDashboardId === child.id
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
- <NewDashboardDialog />
2750
- </div>
2751
- </SortableContext>
2752
- </DndContext>
2753
- )}
2754
- </div>
2755
-
2756
- {/* Analyses section */}
2757
- <div className="order-3 group/section min-w-0 space-y-1">
2758
- <div
2759
- className={cn(
2760
- "flex w-full min-w-0 items-center rounded-lg transition-colors hover:text-primary",
2761
- location.pathname.startsWith("/analyses")
2762
- ? "text-sidebar-accent-foreground"
2763
- : "text-muted-foreground hover:bg-sidebar-accent/50",
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
- {analysesLoading &&
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
- {!analysesLoading &&
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}>