@robosystems/core 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/actions/entity-actions.d.ts +8 -0
  4. package/actions/entity-actions.js +16 -0
  5. package/actions/graph-actions.d.ts +3 -0
  6. package/actions/graph-actions.js +13 -0
  7. package/auth-components/AppSwitcher.d.ts +6 -0
  8. package/auth-components/AppSwitcher.js +28 -0
  9. package/auth-components/AuthGuard.d.ts +7 -0
  10. package/auth-components/AuthGuard.js +31 -0
  11. package/auth-components/AuthProvider.d.ts +9 -0
  12. package/auth-components/AuthProvider.js +565 -0
  13. package/auth-components/SessionWarningDialog.d.ts +1 -0
  14. package/auth-components/SessionWarningDialog.js +51 -0
  15. package/auth-components/SignInForm.d.ts +20 -0
  16. package/auth-components/SignInForm.js +139 -0
  17. package/auth-components/SignUpForm.d.ts +15 -0
  18. package/auth-components/SignUpForm.js +167 -0
  19. package/auth-components/TurnstileWidget.d.ts +99 -0
  20. package/auth-components/TurnstileWidget.js +224 -0
  21. package/auth-components/index.d.ts +6 -0
  22. package/auth-components/index.js +6 -0
  23. package/auth-core/cleanup.d.ts +44 -0
  24. package/auth-core/cleanup.js +157 -0
  25. package/auth-core/client.d.ts +121 -0
  26. package/auth-core/client.js +767 -0
  27. package/auth-core/config.d.ts +19 -0
  28. package/auth-core/config.js +56 -0
  29. package/auth-core/hooks.d.ts +19 -0
  30. package/auth-core/hooks.js +63 -0
  31. package/auth-core/index.d.ts +6 -0
  32. package/auth-core/index.js +6 -0
  33. package/auth-core/sso.d.ts +42 -0
  34. package/auth-core/sso.js +281 -0
  35. package/auth-core/token-storage.d.ts +58 -0
  36. package/auth-core/token-storage.js +177 -0
  37. package/auth-core/types.d.ts +145 -0
  38. package/auth-core/types.js +1 -0
  39. package/components/EntitySelector.d.ts +44 -0
  40. package/components/EntitySelector.js +64 -0
  41. package/components/EntitySelectorCore.d.ts +45 -0
  42. package/components/EntitySelectorCore.js +67 -0
  43. package/components/GraphSelectorCore.d.ts +21 -0
  44. package/components/GraphSelectorCore.js +39 -0
  45. package/components/PageLayout.d.ts +8 -0
  46. package/components/PageLayout.js +5 -0
  47. package/components/RepositoryGuard.d.ts +68 -0
  48. package/components/RepositoryGuard.js +91 -0
  49. package/components/console/ConsoleContent.d.ts +4 -0
  50. package/components/console/ConsoleContent.js +612 -0
  51. package/components/console/ConsoleMarkdown.d.ts +11 -0
  52. package/components/console/ConsoleMarkdown.js +49 -0
  53. package/components/console/ProgressiveText.d.ts +6 -0
  54. package/components/console/ProgressiveText.js +21 -0
  55. package/components/console/graphAwareConfig.d.ts +70 -0
  56. package/components/console/graphAwareConfig.js +411 -0
  57. package/components/console/index.d.ts +6 -0
  58. package/components/console/index.js +4 -0
  59. package/components/console/types.d.ts +84 -0
  60. package/components/console/types.js +1 -0
  61. package/components/graph-filters.d.ts +75 -0
  62. package/components/graph-filters.js +111 -0
  63. package/components/index.d.ts +10 -0
  64. package/components/index.js +9 -0
  65. package/components/repositories/ActiveSubscriptions.d.ts +26 -0
  66. package/components/repositories/ActiveSubscriptions.js +103 -0
  67. package/components/repositories/BrowseRepositories.d.ts +11 -0
  68. package/components/repositories/BrowseRepositories.js +184 -0
  69. package/components/repositories/index.d.ts +2 -0
  70. package/components/repositories/index.js +2 -0
  71. package/components/search/SearchContent.d.ts +4 -0
  72. package/components/search/SearchContent.js +168 -0
  73. package/components/search/index.d.ts +2 -0
  74. package/components/search/index.js +1 -0
  75. package/components/search/types.d.ts +15 -0
  76. package/components/search/types.js +1 -0
  77. package/contexts/entity-context.d.ts +28 -0
  78. package/contexts/entity-context.js +89 -0
  79. package/contexts/graph-context.d.ts +27 -0
  80. package/contexts/graph-context.js +128 -0
  81. package/contexts/index.d.ts +5 -0
  82. package/contexts/index.js +5 -0
  83. package/contexts/org-context.d.ts +25 -0
  84. package/contexts/org-context.js +64 -0
  85. package/contexts/service-offerings-context.d.ts +45 -0
  86. package/contexts/service-offerings-context.js +98 -0
  87. package/contexts/sidebar-context.d.ts +18 -0
  88. package/contexts/sidebar-context.js +44 -0
  89. package/hooks/index.d.ts +6 -0
  90. package/hooks/index.js +6 -0
  91. package/hooks/use-api-error.d.ts +3 -0
  92. package/hooks/use-api-error.js +24 -0
  93. package/hooks/use-media-query.d.ts +1 -0
  94. package/hooks/use-media-query.js +21 -0
  95. package/hooks/use-streaming-query.d.ts +22 -0
  96. package/hooks/use-streaming-query.js +142 -0
  97. package/hooks/use-toast.d.ts +37 -0
  98. package/hooks/use-toast.js +117 -0
  99. package/hooks/use-user-limits.d.ts +12 -0
  100. package/hooks/use-user-limits.js +73 -0
  101. package/hooks/use-user.d.ts +11 -0
  102. package/hooks/use-user.js +39 -0
  103. package/index.d.ts +29 -0
  104. package/index.js +77 -0
  105. package/lib/entity-cookie.d.ts +10 -0
  106. package/lib/entity-cookie.js +38 -0
  107. package/lib/graph-cookie.d.ts +11 -0
  108. package/lib/graph-cookie.js +62 -0
  109. package/lib/graph-tiers.d.ts +41 -0
  110. package/lib/graph-tiers.js +107 -0
  111. package/lib/index.d.ts +3 -0
  112. package/lib/index.js +3 -0
  113. package/lib/sidebar-cookie.d.ts +11 -0
  114. package/lib/sidebar-cookie.js +42 -0
  115. package/library/colors.d.ts +4 -0
  116. package/library/colors.js +41 -0
  117. package/library/components/ClassificationPicker.d.ts +8 -0
  118. package/library/components/ClassificationPicker.js +55 -0
  119. package/library/components/ElementBrowser.d.ts +11 -0
  120. package/library/components/ElementBrowser.js +106 -0
  121. package/library/components/ElementDetail.d.ts +7 -0
  122. package/library/components/ElementDetail.js +97 -0
  123. package/library/components/LibraryHierarchy.d.ts +13 -0
  124. package/library/components/LibraryHierarchy.js +380 -0
  125. package/library/components/TaxonomySidebar.d.ts +6 -0
  126. package/library/components/TaxonomySidebar.js +45 -0
  127. package/library/index.d.ts +8 -0
  128. package/library/index.js +7 -0
  129. package/package.json +37 -0
  130. package/research/CoverageCard.d.ts +12 -0
  131. package/research/CoverageCard.js +15 -0
  132. package/research/CoverageGrid.d.ts +6 -0
  133. package/research/CoverageGrid.js +9 -0
  134. package/research/CoverageHistory.d.ts +5 -0
  135. package/research/CoverageHistory.js +7 -0
  136. package/research/ResearchArticle.d.ts +15 -0
  137. package/research/ResearchArticle.js +26 -0
  138. package/research/catalog.d.ts +14 -0
  139. package/research/catalog.js +42 -0
  140. package/research/index.d.ts +6 -0
  141. package/research/index.js +9 -0
  142. package/research/types.d.ts +38 -0
  143. package/research/types.js +4 -0
  144. package/task-monitoring/hooks.d.ts +26 -0
  145. package/task-monitoring/hooks.js +126 -0
  146. package/task-monitoring/index.d.ts +5 -0
  147. package/task-monitoring/index.js +7 -0
  148. package/task-monitoring/operationErrors.d.ts +82 -0
  149. package/task-monitoring/operationErrors.js +251 -0
  150. package/task-monitoring/operationHooks.d.ts +117 -0
  151. package/task-monitoring/operationHooks.js +536 -0
  152. package/task-monitoring/operationMonitor.d.ts +78 -0
  153. package/task-monitoring/operationMonitor.js +200 -0
  154. package/task-monitoring/operationTypes.d.ts +236 -0
  155. package/task-monitoring/operationTypes.js +60 -0
  156. package/task-monitoring/taskMonitor.d.ts +14 -0
  157. package/task-monitoring/taskMonitor.js +132 -0
  158. package/task-monitoring/types.d.ts +31 -0
  159. package/task-monitoring/types.js +1 -0
  160. package/theme/flowbite-theme.d.ts +758 -0
  161. package/theme/flowbite-theme.js +765 -0
  162. package/theme/index.d.ts +1 -0
  163. package/theme/index.js +1 -0
  164. package/types/entity.d.ts +19 -0
  165. package/types/index.d.ts +2 -0
  166. package/types/index.js +1 -0
  167. package/types/user.d.ts +6 -0
  168. package/ui-components/ConfirmModal.d.ts +41 -0
  169. package/ui-components/ConfirmModal.js +19 -0
  170. package/ui-components/EmptyState.d.ts +22 -0
  171. package/ui-components/EmptyState.js +10 -0
  172. package/ui-components/LoadingState.d.ts +27 -0
  173. package/ui-components/LoadingState.js +12 -0
  174. package/ui-components/Logo.d.ts +28 -0
  175. package/ui-components/Logo.js +95 -0
  176. package/ui-components/Spinner.d.ts +37 -0
  177. package/ui-components/Spinner.js +48 -0
  178. package/ui-components/StatCard.d.ts +21 -0
  179. package/ui-components/StatCard.js +10 -0
  180. package/ui-components/api-keys/ApiKeyDisplay.d.ts +13 -0
  181. package/ui-components/api-keys/ApiKeyDisplay.js +7 -0
  182. package/ui-components/api-keys/ApiKeyTable.d.ts +9 -0
  183. package/ui-components/api-keys/ApiKeyTable.js +17 -0
  184. package/ui-components/api-keys/ApiKeysCard.d.ts +6 -0
  185. package/ui-components/api-keys/ApiKeysCard.js +97 -0
  186. package/ui-components/api-keys/CreateApiKeyModal.d.ts +11 -0
  187. package/ui-components/api-keys/CreateApiKeyModal.js +47 -0
  188. package/ui-components/api-keys/SecureApiKeyField.d.ts +13 -0
  189. package/ui-components/api-keys/SecureApiKeyField.js +64 -0
  190. package/ui-components/api-keys/index.d.ts +5 -0
  191. package/ui-components/api-keys/index.js +5 -0
  192. package/ui-components/chat/ChatHeader.d.ts +12 -0
  193. package/ui-components/chat/ChatHeader.js +5 -0
  194. package/ui-components/chat/ChatInputArea.d.ts +16 -0
  195. package/ui-components/chat/ChatInputArea.js +9 -0
  196. package/ui-components/chat/ChatMessage.d.ts +7 -0
  197. package/ui-components/chat/ChatMessage.js +24 -0
  198. package/ui-components/chat/DeepResearchToggle.d.ts +8 -0
  199. package/ui-components/chat/DeepResearchToggle.js +13 -0
  200. package/ui-components/chat/index.d.ts +9 -0
  201. package/ui-components/chat/index.js +8 -0
  202. package/ui-components/chat/types.d.ts +23 -0
  203. package/ui-components/chat/types.js +51 -0
  204. package/ui-components/forms/PasswordRequirements.d.ts +10 -0
  205. package/ui-components/forms/PasswordRequirements.js +15 -0
  206. package/ui-components/forms/SettingsCard.d.ts +11 -0
  207. package/ui-components/forms/SettingsCard.js +5 -0
  208. package/ui-components/forms/SettingsFormField.d.ts +18 -0
  209. package/ui-components/forms/SettingsFormField.js +17 -0
  210. package/ui-components/forms/StatusAlert.d.ts +8 -0
  211. package/ui-components/forms/StatusAlert.js +7 -0
  212. package/ui-components/forms/index.d.ts +4 -0
  213. package/ui-components/forms/index.js +4 -0
  214. package/ui-components/index.d.ts +13 -0
  215. package/ui-components/index.js +18 -0
  216. package/ui-components/layout/CoreNavbar.d.ts +14 -0
  217. package/ui-components/layout/CoreNavbar.js +53 -0
  218. package/ui-components/layout/CoreSidebar.d.ts +28 -0
  219. package/ui-components/layout/CoreSidebar.js +62 -0
  220. package/ui-components/layout/LandingFooter.d.ts +28 -0
  221. package/ui-components/layout/LandingFooter.js +34 -0
  222. package/ui-components/layout/PageContainer.d.ts +8 -0
  223. package/ui-components/layout/PageContainer.js +17 -0
  224. package/ui-components/layout/PageHeader.d.ts +23 -0
  225. package/ui-components/layout/PageHeader.js +12 -0
  226. package/ui-components/layout/SettingsContainer.d.ts +10 -0
  227. package/ui-components/layout/SettingsContainer.js +9 -0
  228. package/ui-components/layout/ThemeToggle.d.ts +4 -0
  229. package/ui-components/layout/ThemeToggle.js +6 -0
  230. package/ui-components/layout/index.d.ts +7 -0
  231. package/ui-components/layout/index.js +7 -0
  232. package/ui-components/settings/GeneralInformationCard.d.ts +12 -0
  233. package/ui-components/settings/GeneralInformationCard.js +72 -0
  234. package/ui-components/settings/PasswordInformationCard.d.ts +10 -0
  235. package/ui-components/settings/PasswordInformationCard.js +167 -0
  236. package/ui-components/settings/SettingsPageHeader.d.ts +7 -0
  237. package/ui-components/settings/SettingsPageHeader.js +6 -0
  238. package/ui-components/settings/index.d.ts +3 -0
  239. package/ui-components/settings/index.js +3 -0
  240. package/ui-components/support/SupportModal.d.ts +15 -0
  241. package/ui-components/support/SupportModal.js +74 -0
  242. package/ui-components/support/index.d.ts +2 -0
  243. package/ui-components/support/index.js +1 -0
  244. package/ui-components/types/index.d.ts +39 -0
  245. package/ui-components/types/index.js +1 -0
  246. package/utils/index.d.ts +31 -0
  247. package/utils/index.js +38 -0
  248. package/utils/turnstile-config.d.ts +11 -0
  249. package/utils/turnstile-config.js +19 -0
@@ -0,0 +1,106 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Alert, Badge, Button, Card, Select, Spinner, TextInput, } from 'flowbite-react';
4
+ import { useEffect, useState } from 'react';
5
+ import { HiInformationCircle, HiSearch } from 'react-icons/hi';
6
+ import { classificationColor } from '../colors';
7
+ import { ClassificationPicker } from './ClassificationPicker';
8
+ const PAGE_SIZE = 50;
9
+ export function ElementBrowser({ client, graphId, taxonomyId, taxonomies, onTaxonomyChange, selectedElementId, onSelectElement, }) {
10
+ const [elements, setElements] = useState([]);
11
+ const [state, setState] = useState('idle');
12
+ const [error, setError] = useState(null);
13
+ const [search, setSearch] = useState('');
14
+ const [offset, setOffset] = useState(0);
15
+ const [classification, setClassification] = useState(null);
16
+ const [activity, setActivity] = useState(null);
17
+ const activeQuery = search.trim();
18
+ const searchMode = activeQuery.length > 0;
19
+ useEffect(() => {
20
+ let cancelled = false;
21
+ setState('loading');
22
+ setError(null);
23
+ const run = async () => {
24
+ try {
25
+ const rows = searchMode
26
+ ? await client.searchLibraryElements(graphId, activeQuery, {
27
+ limit: PAGE_SIZE,
28
+ })
29
+ : await client.listLibraryElements(graphId, {
30
+ taxonomyId: taxonomyId !== null && taxonomyId !== void 0 ? taxonomyId : undefined,
31
+ classification: classification && classification !== 'abstract'
32
+ ? classification
33
+ : undefined,
34
+ activityType: activity !== null && activity !== void 0 ? activity : undefined,
35
+ isAbstract: classification === 'abstract'
36
+ ? true
37
+ : classification !== null
38
+ ? false
39
+ : null,
40
+ limit: PAGE_SIZE,
41
+ offset,
42
+ });
43
+ if (cancelled)
44
+ return;
45
+ setElements(rows);
46
+ setState('ready');
47
+ }
48
+ catch (err) {
49
+ if (cancelled)
50
+ return;
51
+ setError(err instanceof Error ? err.message : 'Unknown error');
52
+ setState('error');
53
+ }
54
+ };
55
+ run();
56
+ return () => {
57
+ cancelled = true;
58
+ };
59
+ }, [
60
+ client,
61
+ graphId,
62
+ taxonomyId,
63
+ offset,
64
+ classification,
65
+ activity,
66
+ searchMode,
67
+ activeQuery,
68
+ ]);
69
+ const handleSearchChange = (value) => {
70
+ setSearch(value);
71
+ setOffset(0);
72
+ };
73
+ const handleClassificationChange = (value) => {
74
+ setClassification(value);
75
+ setOffset(0);
76
+ };
77
+ const handleActivityChange = (value) => {
78
+ setActivity(value);
79
+ setOffset(0);
80
+ };
81
+ const handleTaxonomyChange = (id) => {
82
+ setOffset(0);
83
+ onTaxonomyChange === null || onTaxonomyChange === void 0 ? void 0 : onTaxonomyChange(id);
84
+ };
85
+ const showTaxonomyDropdown = taxonomies && onTaxonomyChange;
86
+ return (_jsx("section", { className: "col-span-12 min-h-0 md:col-span-5", children: _jsxs(Card, { className: "flex h-full flex-col overflow-hidden", children: [_jsx("h2", { className: "font-heading shrink-0 text-lg font-semibold text-gray-900 dark:text-white", children: "Elements" }), _jsxs("div", { className: "flex gap-2", children: [showTaxonomyDropdown && (_jsx(Select, { sizing: "sm", value: taxonomyId !== null && taxonomyId !== void 0 ? taxonomyId : '', onChange: (e) => handleTaxonomyChange(e.target.value), className: "shrink-0", "aria-label": "Taxonomy", children: taxonomies.map((t) => {
87
+ var _a;
88
+ return (_jsxs("option", { value: t.id, children: [(_a = t.standard) !== null && _a !== void 0 ? _a : t.name, t.version ? ` ${t.version}` : '', typeof t.elementCount === 'number'
89
+ ? ` (${t.elementCount.toLocaleString()})`
90
+ : ''] }, t.id));
91
+ }) })), _jsx(TextInput, { icon: HiSearch, sizing: "sm", placeholder: "Search qname, name, label\u2026", value: search, onChange: (e) => handleSearchChange(e.target.value), className: "flex-1" })] }), _jsx("div", { className: "space-y-1", children: _jsx(ClassificationPicker, { selected: classification, onSelect: handleClassificationChange, activity: activity, onActivityChange: handleActivityChange, disabled: searchMode }) }), state === 'loading' && (_jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400", children: [_jsx(Spinner, { size: "sm" }), " Loading\u2026"] })), state === 'error' && (_jsx(Alert, { color: "failure", icon: HiInformationCircle, children: error })), state === 'ready' && elements.length === 0 && (_jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "No elements match these filters." })), _jsx("ul", { className: "min-h-0 flex-1 space-y-1 overflow-y-auto", children: elements.map((el) => {
92
+ var _a, _b;
93
+ const isSelected = el.id === selectedElementId;
94
+ return (_jsx("li", { children: _jsxs("button", { onClick: () => onSelectElement(el.id), className: `w-full rounded px-3 py-2 text-left text-sm transition-colors ${isSelected
95
+ ? 'bg-primary-100 text-primary-900 dark:bg-primary-900/50 dark:text-primary-100'
96
+ : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-800'}`, children: [_jsx("div", { className: "truncate font-mono text-sm", title: el.qname, children: el.qname }), _jsxs("div", { className: "mt-0.5 flex items-center justify-between gap-2", children: [_jsx("span", { className: "truncate text-xs text-gray-500 dark:text-gray-400", title: el.name, children: el.name }), _jsx(Badge, { color: el.elementType === 'hypercube'
97
+ ? 'indigo'
98
+ : el.isAbstract
99
+ ? 'purple'
100
+ : classificationColor((_a = el.trait) !== null && _a !== void 0 ? _a : ''), size: "xs", className: "shrink-0", children: el.elementType === 'hypercube'
101
+ ? 'hypercube'
102
+ : el.isAbstract
103
+ ? 'abstract'
104
+ : ((_b = el.trait) !== null && _b !== void 0 ? _b : '—') })] })] }) }, el.id));
105
+ }) }), !searchMode && state === 'ready' && elements.length === PAGE_SIZE && (_jsxs("div", { className: "flex shrink-0 justify-between pt-2", children: [_jsx(Button, { size: "xs", color: "gray", disabled: offset === 0, onClick: () => setOffset(Math.max(0, offset - PAGE_SIZE)), children: "\u2190 Previous" }), _jsxs("span", { className: "self-center text-xs text-gray-500 dark:text-gray-400", children: [offset + 1, "\u2013", offset + elements.length] }), _jsx(Button, { size: "xs", color: "gray", onClick: () => setOffset(offset + PAGE_SIZE), children: "Next \u2192" })] }))] }) }));
106
+ }
@@ -0,0 +1,7 @@
1
+ import type { LibraryClient } from '@robosystems/client/clients';
2
+ export declare function ElementDetail({ client, graphId, elementId, onSelectElement, }: {
3
+ client: LibraryClient;
4
+ graphId: string;
5
+ elementId: string | null;
6
+ onSelectElement: (id: string) => void;
7
+ }): import("react").JSX.Element;
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Alert, Badge, Card, Spinner } from 'flowbite-react';
4
+ import { useEffect, useMemo, useState } from 'react';
5
+ import { HiExternalLink, HiInformationCircle } from 'react-icons/hi';
6
+ import { arcTypeColor, classificationColor } from '../colors';
7
+ const LABEL_ROLE_ORDER = [
8
+ 'standard',
9
+ 'verbose',
10
+ 'terse',
11
+ 'documentation',
12
+ 'periodStart',
13
+ 'periodEnd',
14
+ 'negated',
15
+ 'total',
16
+ 'commentaryGuidance',
17
+ 'other',
18
+ ];
19
+ export function ElementDetail({ client, graphId, elementId, onSelectElement, }) {
20
+ const [element, setElement] = useState(null);
21
+ const [arcs, setArcs] = useState([]);
22
+ const [classifications, setClassifications] = useState([]);
23
+ const [state, setState] = useState('idle');
24
+ const [error, setError] = useState(null);
25
+ useEffect(() => {
26
+ if (!elementId) {
27
+ setElement(null);
28
+ setArcs([]);
29
+ setClassifications([]);
30
+ setError(null);
31
+ setState('idle');
32
+ return;
33
+ }
34
+ let cancelled = false;
35
+ setState('loading');
36
+ setError(null);
37
+ Promise.all([
38
+ client.getLibraryElement(graphId, { id: elementId }),
39
+ client.getLibraryElementArcs(graphId, elementId),
40
+ client.getLibraryElementClassifications(graphId, elementId),
41
+ ])
42
+ .then(([el, arcRows, classRows]) => {
43
+ if (cancelled)
44
+ return;
45
+ setElement(el);
46
+ setArcs(arcRows);
47
+ setClassifications(classRows);
48
+ setState('ready');
49
+ })
50
+ .catch((err) => {
51
+ if (cancelled)
52
+ return;
53
+ setError(err instanceof Error ? err.message : 'Failed to load element');
54
+ setState('error');
55
+ });
56
+ return () => {
57
+ cancelled = true;
58
+ };
59
+ }, [client, graphId, elementId]);
60
+ const arcsByTaxonomy = useMemo(() => {
61
+ var _a, _b, _c, _d, _e, _f;
62
+ const groups = new Map();
63
+ for (const arc of arcs) {
64
+ const key = (_c = (_b = (_a = arc.taxonomyStandard) !== null && _a !== void 0 ? _a : arc.structureName) !== null && _b !== void 0 ? _b : arc.taxonomyName) !== null && _c !== void 0 ? _c : 'other';
65
+ const label = (_f = (_e = (_d = arc.taxonomyStandard) !== null && _d !== void 0 ? _d : arc.structureName) !== null && _e !== void 0 ? _e : arc.taxonomyName) !== null && _f !== void 0 ? _f : 'other';
66
+ if (!groups.has(key))
67
+ groups.set(key, { label, arcs: [] });
68
+ groups.get(key).arcs.push(arc);
69
+ }
70
+ return groups;
71
+ }, [arcs]);
72
+ const classificationsByCategory = useMemo(() => {
73
+ const groups = new Map();
74
+ for (const cls of classifications) {
75
+ if (!groups.has(cls.category))
76
+ groups.set(cls.category, []);
77
+ groups.get(cls.category).push(cls);
78
+ }
79
+ return groups;
80
+ }, [classifications]);
81
+ const sortedLabels = useMemo(() => {
82
+ if (!element)
83
+ return [];
84
+ return [...element.labels].sort((a, b) => {
85
+ const ia = LABEL_ROLE_ORDER.indexOf(a.role);
86
+ const ib = LABEL_ROLE_ORDER.indexOf(b.role);
87
+ return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
88
+ });
89
+ }, [element]);
90
+ return (_jsx("section", { className: "col-span-12 min-h-0 md:col-span-7", children: _jsxs(Card, { className: "flex h-full flex-col overflow-hidden", children: [state === 'idle' && (_jsx("div", { className: "flex h-full items-center justify-center text-sm text-gray-500 dark:text-gray-400", children: "Select an element to view details" })), state === 'loading' && (_jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [_jsx(Spinner, { size: "sm" }), " Loading element\u2026"] })), state === 'error' && (_jsx(Alert, { color: "failure", icon: HiInformationCircle, children: error !== null && error !== void 0 ? error : 'Failed to load element' })), state === 'ready' && element && (_jsxs("div", { className: "min-h-0 flex-1 space-y-4 overflow-y-auto pr-1", children: [_jsxs("div", { children: [_jsx("h2", { className: "font-mono text-lg font-semibold break-words text-gray-900 dark:text-white", title: element.qname, children: element.qname }), _jsx("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: element.name })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [element.trait && (_jsx(Badge, { color: classificationColor(element.trait), title: "FASB elementsOfFinancialStatements", children: element.trait })), _jsx(Badge, { color: "gray", children: element.balanceType }), _jsx(Badge, { color: "gray", children: element.periodType }), _jsx(Badge, { color: "gray", children: element.elementType }), element.isAbstract && _jsx(Badge, { color: "purple", children: "abstract" }), element.isMonetary && _jsx(Badge, { color: "indigo", children: "monetary" }), _jsx(Badge, { color: "info", children: element.source })] }), classificationsByCategory.size > 0 && (_jsxs("div", { children: [_jsx("h3", { className: "font-heading mb-2 text-sm font-semibold text-gray-900 dark:text-white", children: "Classifications" }), _jsx("div", { className: "space-y-2", children: Array.from(classificationsByCategory.entries()).map(([category, items]) => (_jsxs("div", { children: [_jsx("div", { className: "mb-1 px-1 font-mono text-[11px] font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400", children: category }), _jsx("div", { className: "flex flex-wrap gap-1", children: items.map((cls) => (_jsx(Badge, { color: category === 'elementsOfFinancialStatements'
91
+ ? classificationColor(cls.identifier)
92
+ : 'gray', size: "xs", children: cls.identifier }, cls.identifier))) })] }, category))) })] })), sortedLabels.length > 0 && (_jsxs("div", { children: [_jsx("h3", { className: "font-heading mb-2 text-sm font-semibold text-gray-900 dark:text-white", children: "Labels" }), _jsx("ul", { className: "space-y-1", children: sortedLabels.map((lab, i) => (_jsxs("li", { className: "rounded bg-gray-50 p-2 dark:bg-gray-800", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400", children: [_jsx("span", { className: "font-mono", children: lab.role }), _jsx("span", { children: "\u00B7" }), _jsx("span", { children: lab.language })] }), _jsx("p", { className: "text-sm text-gray-900 dark:text-gray-100", children: lab.text })] }, `${lab.role}-${lab.language}-${i}`))) })] })), element.references.length > 0 && (_jsxs("div", { children: [_jsx("h3", { className: "font-heading mb-2 text-sm font-semibold text-gray-900 dark:text-white", children: "References" }), _jsx("ul", { className: "space-y-1", children: element.references.map((ref, i) => (_jsxs("li", { className: "text-sm text-gray-700 dark:text-gray-300", children: [ref.refType && (_jsx(Badge, { color: "gray", size: "xs", className: "mr-2", children: ref.refType })), ref.citation, ref.uri && (_jsx("a", { href: ref.uri, target: "_blank", rel: "noopener noreferrer", className: "text-primary-600 dark:text-primary-400 ml-2 inline-flex items-center gap-1 hover:underline", children: _jsx(HiExternalLink, { className: "h-3 w-3" }) }))] }, i))) })] })), arcs.length > 0 && (_jsxs("div", { children: [_jsxs("h3", { className: "font-heading mb-1 text-sm font-semibold text-gray-900 dark:text-white", children: ["Mappings (", arcs.length, ")"] }), _jsx("p", { className: "mb-2 text-xs text-gray-500 dark:text-gray-400", children: "Cross-taxonomy arcs \u2014 equivalence, generalization, and type-subtype bridges. Click a peer to jump to it." }), _jsx("div", { className: "space-y-3", children: Array.from(arcsByTaxonomy.entries()).map(([key, { label, arcs: groupArcs }]) => (_jsxs("div", { children: [_jsxs("div", { className: "mb-1 flex items-center justify-between px-1", children: [_jsx("span", { className: "font-mono text-[11px] font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400", children: label }), _jsx(Badge, { color: "gray", size: "xs", children: groupArcs.length })] }), _jsx("ul", { className: "space-y-0.5", children: groupArcs.map((arc) => (_jsx("li", { children: _jsxs("button", { onClick: () => onSelectElement(arc.peer.id), className: "w-full rounded px-2 py-1 text-left transition-colors hover:bg-gray-100 dark:hover:bg-gray-800", title: `${arc.associationType} · ${arc.direction}`, children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "shrink-0 text-xs text-gray-400 dark:text-gray-500", title: arc.direction === 'outgoing'
93
+ ? 'this element → peer'
94
+ : 'peer → this element', children: arc.direction === 'outgoing' ? '→' : '←' }), _jsx("span", { className: "text-primary-700 dark:text-primary-300 truncate font-mono text-xs", children: arc.peer.qname }), _jsx(Badge, { color: arcTypeColor(arc.associationType), size: "xs", className: "ml-auto shrink-0", children: arc.associationType })] }), arc.peer.name &&
95
+ arc.peer.name !==
96
+ arc.peer.qname.split(':').pop() && (_jsx("div", { className: "truncate pl-5 text-xs text-gray-500 dark:text-gray-400", children: arc.peer.name }))] }) }, arc.id))) })] }, key))) })] }))] }))] }) }));
97
+ }
@@ -0,0 +1,13 @@
1
+ import type { LibraryClient, LibraryTaxonomy } from '@robosystems/client/clients';
2
+ export declare function LibraryHierarchy({ client, graphId, taxonomies, baseStandard, selectedTaxonomyId, selectedElementId, onSelectElement, }: {
3
+ client: LibraryClient;
4
+ graphId: string;
5
+ /** All taxonomies visible at this graph_id — used to resolve the arc-owning taxonomy. */
6
+ taxonomies: LibraryTaxonomy[];
7
+ /** Base reporting standard whose hierarchy to show (e.g. "rs-gaap", "fac"). */
8
+ baseStandard: string | null;
9
+ /** The selected taxonomy id — drives the Chart-of-Accounts parent_id tree. */
10
+ selectedTaxonomyId: string | null;
11
+ selectedElementId: string | null;
12
+ onSelectElement: (id: string) => void;
13
+ }): import("react").JSX.Element;
@@ -0,0 +1,380 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { clients } from '@robosystems/client/clients';
4
+ import { Alert, Badge, Card, Select, Spinner } from 'flowbite-react';
5
+ import { useEffect, useMemo, useState } from 'react';
6
+ import { HiChevronDown, HiChevronRight, HiInformationCircle, } from 'react-icons/hi';
7
+ import { classificationColor } from '../colors';
8
+ /**
9
+ * Arc types the hierarchy view can walk, with the taxonomy-standard suffix
10
+ * that owns each. The calc DAG lives in ``{base}-calculations`` and the
11
+ * presentation networks in ``{base}-presentation`` — the arcs are NOT on the
12
+ * base reporting taxonomy, so we resolve the owning taxonomy by name. The
13
+ * general-special / type-subtype lattice is intentionally not exposed here:
14
+ * it's substrate (render fallback + classification), not a curated browse.
15
+ */
16
+ const ARC_TYPES = [
17
+ { value: 'presentation', label: 'Presentation', suffix: 'presentation' },
18
+ { value: 'calculation', label: 'Calculation', suffix: 'calculations' },
19
+ ];
20
+ /**
21
+ * Keep only genuine reporting styles in the structure picker. The arc-owning
22
+ * taxonomy also carries two kinds of structure that aren't styles:
23
+ * • the empty seed-time catch-all (blockType 'custom', 0 arcs); and
24
+ * • the auto-derived base networks — the type-subtype lattice projected as
25
+ * presentation (role '…-pres-bs|is|cf'), an exhaustive, unordered substrate
26
+ * the curated styles are carved from, not a statement layout.
27
+ * Both are substrate, not something to scope a view to.
28
+ */
29
+ function isReportingStyle(s) {
30
+ var _a;
31
+ if (s.blockType === 'custom')
32
+ return false;
33
+ return !/-pres-(bs|is|cf)$/i.test((_a = s.roleUri) !== null && _a !== void 0 ? _a : '');
34
+ }
35
+ /**
36
+ * Order reporting styles by financial-statement sequence (BS → IS → SE → CF)
37
+ * instead of the backend's name order. ``block_type`` encodes the statement;
38
+ * the sort is stable, so styles within one statement keep their seed order and
39
+ * non-statement structures (calc rules, disclosure tables) keep theirs.
40
+ */
41
+ const STATEMENT_ORDER = {
42
+ balance_sheet: 0,
43
+ income_statement: 1,
44
+ equity_statement: 2,
45
+ cash_flow_statement: 3,
46
+ };
47
+ function byStatementOrder(a, b) {
48
+ var _a, _b;
49
+ return (((_a = STATEMENT_ORDER[a.blockType]) !== null && _a !== void 0 ? _a : 99) - ((_b = STATEMENT_ORDER[b.blockType]) !== null && _b !== void 0 ? _b : 99));
50
+ }
51
+ /**
52
+ * The structure to land on for a given arc type. Presentation opens on a
53
+ * coherent statement — the balance sheet — rather than "All structures",
54
+ * whose union blends every statement (and the base networks) into an
55
+ * incoherent multi-root tree. Calculation defaults to "All structures"
56
+ * (null): there the union IS the single coherent calc DAG.
57
+ */
58
+ function defaultStructureId(arcType, structures) {
59
+ var _a, _b;
60
+ if (arcType !== 'presentation')
61
+ return null;
62
+ return (_b = (_a = structures.find((s) => s.blockType === 'balance_sheet')) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null;
63
+ }
64
+ const INITIAL_EXPAND_DEPTH = 2;
65
+ const FETCH_PAGE = 1000;
66
+ /** Fetch every arc for a taxonomy+type(+structure), paging past the cap. */
67
+ async function fetchAllArcs(client, graphId, taxonomyId, associationType, structureId) {
68
+ const all = [];
69
+ let offset = 0;
70
+ // Guard against a runaway loop if count is ever inconsistent.
71
+ for (let page = 0; page < 50; page++) {
72
+ const { arcs, count } = await client.listLibraryTaxonomyArcs(graphId, taxonomyId, {
73
+ associationType,
74
+ structureId: structureId !== null && structureId !== void 0 ? structureId : undefined,
75
+ limit: FETCH_PAGE,
76
+ offset,
77
+ });
78
+ all.push(...arcs);
79
+ if (arcs.length === 0 || all.length >= count)
80
+ break;
81
+ offset += FETCH_PAGE;
82
+ }
83
+ return all;
84
+ }
85
+ /** Assemble a forest from a flat arc list (roots = a `from` that's never a `to`). */
86
+ function buildForest(arcs) {
87
+ var _a, _b, _c;
88
+ const meta = new Map();
89
+ const childEdges = new Map();
90
+ const froms = new Set();
91
+ const tos = new Set();
92
+ for (const a of arcs) {
93
+ if (!a.fromElementId || !a.toElementId)
94
+ continue;
95
+ meta.set(a.fromElementId, {
96
+ qname: a.fromElementQname,
97
+ name: a.fromElementName,
98
+ trait: a.fromElementTrait,
99
+ isAbstract: (_a = a.fromElementIsAbstract) !== null && _a !== void 0 ? _a : false,
100
+ });
101
+ meta.set(a.toElementId, {
102
+ qname: a.toElementQname,
103
+ name: a.toElementName,
104
+ trait: a.toElementTrait,
105
+ isAbstract: (_b = a.toElementIsAbstract) !== null && _b !== void 0 ? _b : false,
106
+ });
107
+ froms.add(a.fromElementId);
108
+ tos.add(a.toElementId);
109
+ const edges = (_c = childEdges.get(a.fromElementId)) !== null && _c !== void 0 ? _c : [];
110
+ // Dedupe by target — the same parent→child arc recurs across structures
111
+ // (the "All structures" blend), which would otherwise render duplicate
112
+ // sibling nodes (and collide on React keys).
113
+ if (!edges.some((e) => e.childId === a.toElementId)) {
114
+ edges.push({
115
+ childId: a.toElementId,
116
+ weight: a.weight,
117
+ order: a.orderValue,
118
+ });
119
+ childEdges.set(a.fromElementId, edges);
120
+ }
121
+ }
122
+ const node = (id, weight, order, seen) => {
123
+ var _a, _b, _c, _d, _e;
124
+ const m = meta.get(id);
125
+ const self = {
126
+ id,
127
+ qname: (_a = m === null || m === void 0 ? void 0 : m.qname) !== null && _a !== void 0 ? _a : null,
128
+ name: (_b = m === null || m === void 0 ? void 0 : m.name) !== null && _b !== void 0 ? _b : null,
129
+ trait: (_c = m === null || m === void 0 ? void 0 : m.trait) !== null && _c !== void 0 ? _c : null,
130
+ isAbstract: (_d = m === null || m === void 0 ? void 0 : m.isAbstract) !== null && _d !== void 0 ? _d : false,
131
+ weight,
132
+ order,
133
+ children: [],
134
+ };
135
+ if (seen.has(id))
136
+ return self; // cycle guard
137
+ const next = new Set(seen);
138
+ next.add(id);
139
+ const edges = [...((_e = childEdges.get(id)) !== null && _e !== void 0 ? _e : [])].sort((a, b) => { var _a, _b; return ((_a = a.order) !== null && _a !== void 0 ? _a : 0) - ((_b = b.order) !== null && _b !== void 0 ? _b : 0); });
140
+ self.children = edges.map((e) => node(e.childId, e.weight, e.order, next));
141
+ return self;
142
+ };
143
+ const roots = [...froms]
144
+ .filter((id) => !tos.has(id))
145
+ .sort((a, b) => { var _a, _b, _c, _d; return ((_b = (_a = meta.get(a)) === null || _a === void 0 ? void 0 : _a.qname) !== null && _b !== void 0 ? _b : '').localeCompare((_d = (_c = meta.get(b)) === null || _c === void 0 ? void 0 : _c.qname) !== null && _d !== void 0 ? _d : ''); });
146
+ return roots.map((id) => node(id, null, null, new Set()));
147
+ }
148
+ // QB's standard CoA ordering by AccountType — kept in lockstep with the Chart
149
+ // of Accounts page (ledger/chart-of-accounts). Type-first (not code-first)
150
+ // because QB returns code == account name when account-numbering is off (the
151
+ // sandbox default), which would otherwise degrade to alphabetic-by-name and
152
+ // lose the asset → liability → equity → income → expense grouping.
153
+ const ACCOUNT_TYPE_ORDER = {
154
+ Bank: 0,
155
+ 'Accounts Receivable': 1,
156
+ 'Other Current Asset': 2,
157
+ 'Fixed Asset': 3,
158
+ 'Other Asset': 4,
159
+ 'Accounts Payable': 5,
160
+ 'Credit Card': 6,
161
+ 'Other Current Liability': 7,
162
+ 'Long Term Liability': 8,
163
+ Equity: 9,
164
+ Income: 10,
165
+ 'Cost of Goods Sold': 11,
166
+ Expense: 12,
167
+ 'Other Income': 13,
168
+ 'Other Expense': 14,
169
+ };
170
+ function compareAccountNodes(a, b) {
171
+ var _a, _b, _c, _d, _e, _f;
172
+ const ta = (_a = ACCOUNT_TYPE_ORDER[a.accountType || '']) !== null && _a !== void 0 ? _a : 99;
173
+ const tb = (_b = ACCOUNT_TYPE_ORDER[b.accountType || '']) !== null && _b !== void 0 ? _b : 99;
174
+ if (ta !== tb)
175
+ return ta - tb;
176
+ const ca = (_c = a.code) !== null && _c !== void 0 ? _c : '';
177
+ const cb = (_d = b.code) !== null && _d !== void 0 ? _d : '';
178
+ if (ca !== cb)
179
+ return ca.localeCompare(cb, undefined, { numeric: true });
180
+ return ((_e = a.name) !== null && _e !== void 0 ? _e : '').localeCompare((_f = b.name) !== null && _f !== void 0 ? _f : '');
181
+ }
182
+ function mapAccountNode(n) {
183
+ var _a, _b, _c, _d;
184
+ return {
185
+ id: n.id,
186
+ qname: (_a = n.code) !== null && _a !== void 0 ? _a : null,
187
+ name: (_b = n.name) !== null && _b !== void 0 ? _b : null,
188
+ trait: (_c = n.trait) !== null && _c !== void 0 ? _c : null,
189
+ isAbstract: false,
190
+ weight: null,
191
+ order: null,
192
+ children: [...((_d = n.children) !== null && _d !== void 0 ? _d : [])]
193
+ .sort(compareAccountNodes)
194
+ .map(mapAccountNode),
195
+ };
196
+ }
197
+ /** Node ids deeper than INITIAL_EXPAND_DEPTH — collapsed on first render. */
198
+ function deepNodeIds(forest) {
199
+ const out = new Set();
200
+ const walk = (n, depth) => {
201
+ if (depth >= INITIAL_EXPAND_DEPTH && n.children.length > 0)
202
+ out.add(n.id);
203
+ n.children.forEach((c) => walk(c, depth + 1));
204
+ };
205
+ forest.forEach((r) => walk(r, 0));
206
+ return out;
207
+ }
208
+ export function LibraryHierarchy({ client, graphId, taxonomies, baseStandard, selectedTaxonomyId, selectedElementId, onSelectElement, }) {
209
+ var _a;
210
+ const [arcType, setArcType] = useState('presentation');
211
+ const [structures, setStructures] = useState([]);
212
+ const [structureId, setStructureId] = useState(null);
213
+ const [arcs, setArcs] = useState([]);
214
+ const [state, setState] = useState('idle');
215
+ const [error, setError] = useState(null);
216
+ const [collapsed, setCollapsed] = useState(new Set());
217
+ // The taxonomy id the current `structures` + `structureId` belong to. Gates
218
+ // arc-loading until the structure list (and its default selection) catches up
219
+ // with a new arc type, so we never fetch the "All structures" union as an
220
+ // intermediate state when entering Presentation.
221
+ const [structuresTaxonomyId, setStructuresTaxonomyId] = useState(null);
222
+ const [coaForest, setCoaForest] = useState([]);
223
+ // A Chart of Accounts has no presentation/calc arcs — its hierarchy is the
224
+ // account parent/sub-account tree carried on each element's parentId. Detect
225
+ // it from the selected taxonomy's type and render that tree instead.
226
+ const isCoa = useMemo(() => {
227
+ var _a;
228
+ return ((_a = taxonomies.find((t) => t.id === selectedTaxonomyId)) === null || _a === void 0 ? void 0 : _a.taxonomyType) ===
229
+ 'chart_of_accounts';
230
+ }, [taxonomies, selectedTaxonomyId]);
231
+ // Resolve the taxonomy that owns this arc type for the chosen base standard.
232
+ const arcTaxonomy = useMemo(() => {
233
+ var _a, _b;
234
+ if (!baseStandard)
235
+ return null;
236
+ const suffix = (_a = ARC_TYPES.find((t) => t.value === arcType)) === null || _a === void 0 ? void 0 : _a.suffix;
237
+ return ((_b = taxonomies.find((t) => t.standard === `${baseStandard}-${suffix}`)) !== null && _b !== void 0 ? _b : null);
238
+ }, [taxonomies, baseStandard, arcType]);
239
+ // Load the structure list for the picker whenever the owning taxonomy
240
+ // changes, then select the per-arc-type default structure.
241
+ useEffect(() => {
242
+ if (!arcTaxonomy) {
243
+ setStructures([]);
244
+ setStructureId(null);
245
+ setStructuresTaxonomyId(null);
246
+ return;
247
+ }
248
+ let cancelled = false;
249
+ client
250
+ .listLibraryStructures(graphId, { taxonomyId: arcTaxonomy.id })
251
+ .then((rows) => {
252
+ if (cancelled)
253
+ return;
254
+ const filtered = rows.filter(isReportingStyle).sort(byStatementOrder);
255
+ setStructures(filtered);
256
+ setStructureId(defaultStructureId(arcType, filtered));
257
+ setStructuresTaxonomyId(arcTaxonomy.id);
258
+ })
259
+ .catch((err) => {
260
+ // Non-fatal: the tree still loads with "All structures"; the picker
261
+ // just won't appear. Log so the absence is diagnosable.
262
+ if (!cancelled) {
263
+ console.error('[LibraryHierarchy] failed to load structures', err);
264
+ setStructures([]);
265
+ setStructureId(null);
266
+ setStructuresTaxonomyId(arcTaxonomy.id);
267
+ }
268
+ });
269
+ return () => {
270
+ cancelled = true;
271
+ };
272
+ }, [client, graphId, arcTaxonomy, arcType]);
273
+ // Load arcs for the selected taxonomy + arc type (+ optional structure scope).
274
+ useEffect(() => {
275
+ if (isCoa)
276
+ return; // CoA uses the parentId tree below, not arcs.
277
+ if (!arcTaxonomy) {
278
+ setArcs([]);
279
+ setState('ready');
280
+ return;
281
+ }
282
+ // Wait until the structure list (and its default selection) matches this
283
+ // taxonomy — otherwise switching arc types would briefly fetch with a
284
+ // stale / "All structures" scope before the default lands.
285
+ if (structuresTaxonomyId !== arcTaxonomy.id) {
286
+ setState('loading');
287
+ return;
288
+ }
289
+ let cancelled = false;
290
+ setState('loading');
291
+ setError(null);
292
+ fetchAllArcs(client, graphId, arcTaxonomy.id, arcType, structureId)
293
+ .then((rows) => {
294
+ if (cancelled)
295
+ return;
296
+ setArcs(rows);
297
+ setState('ready');
298
+ })
299
+ .catch((err) => {
300
+ if (cancelled)
301
+ return;
302
+ setError(err instanceof Error ? err.message : 'Failed to load hierarchy');
303
+ setState('error');
304
+ });
305
+ return () => {
306
+ cancelled = true;
307
+ };
308
+ }, [
309
+ client,
310
+ graphId,
311
+ arcTaxonomy,
312
+ arcType,
313
+ structureId,
314
+ structuresTaxonomyId,
315
+ isCoa,
316
+ ]);
317
+ // Load the Chart of Accounts tree — the backend returns it built and
318
+ // active-only (same source as the Chart of Accounts page); we re-order it by
319
+ // AccountType so the layout matches that page rather than the backend's code
320
+ // order (which degrades to alphabetic-by-name when account numbering is off).
321
+ useEffect(() => {
322
+ if (!isCoa) {
323
+ setCoaForest([]);
324
+ return;
325
+ }
326
+ let cancelled = false;
327
+ setState('loading');
328
+ setError(null);
329
+ clients.ledger
330
+ .getAccountTree(graphId)
331
+ .then((tree) => {
332
+ var _a;
333
+ if (cancelled)
334
+ return;
335
+ const roots = ((_a = tree === null || tree === void 0 ? void 0 : tree.roots) !== null && _a !== void 0 ? _a : []);
336
+ setCoaForest([...roots].sort(compareAccountNodes).map(mapAccountNode));
337
+ setState('ready');
338
+ })
339
+ .catch((err) => {
340
+ if (cancelled)
341
+ return;
342
+ setError(err instanceof Error ? err.message : 'Failed to load accounts');
343
+ setState('error');
344
+ });
345
+ return () => {
346
+ cancelled = true;
347
+ };
348
+ }, [isCoa, graphId]);
349
+ const forest = useMemo(() => (isCoa ? coaForest : buildForest(arcs)), [isCoa, coaForest, arcs]);
350
+ // Reset the collapse state to the default expand depth on each new forest.
351
+ useEffect(() => {
352
+ setCollapsed(deepNodeIds(forest));
353
+ }, [forest]);
354
+ const toggle = (id) => setCollapsed((prev) => {
355
+ const next = new Set(prev);
356
+ if (next.has(id))
357
+ next.delete(id);
358
+ else
359
+ next.add(id);
360
+ return next;
361
+ });
362
+ const showWeights = !isCoa && arcType === 'calculation';
363
+ return (_jsx("section", { className: "col-span-12 min-h-0 md:col-span-5", children: _jsxs(Card, { className: "flex h-full flex-col overflow-hidden", children: [_jsxs("div", { className: "flex shrink-0 flex-wrap items-center justify-between gap-2", children: [_jsx("h2", { className: "font-heading text-lg font-semibold text-gray-900 dark:text-white", children: "Hierarchy" }), !isCoa && (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "flex overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700", role: "group", "aria-label": "Arc type", children: ARC_TYPES.map((t) => (_jsx("button", { onClick: () => setArcType(t.value), "aria-pressed": arcType === t.value, className: `px-3 py-1 text-xs font-medium transition-colors ${arcType === t.value
364
+ ? 'bg-primary-600 text-white'
365
+ : 'bg-white text-gray-600 hover:bg-gray-100 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700'}`, children: t.label }, t.value))) }), structures.length > 0 && (_jsxs(Select, { sizing: "sm", value: structureId !== null && structureId !== void 0 ? structureId : '', onChange: (e) => setStructureId(e.target.value || null), "aria-label": "Structure", children: [_jsx("option", { value: "", children: "All structures" }), structures.map((s) => (_jsx("option", { value: s.id, children: s.name }, s.id)))] }))] }))] }), state === 'loading' && (_jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400", children: [_jsx(Spinner, { size: "sm" }), " Loading hierarchy\u2026"] })), state === 'error' && (_jsx(Alert, { color: "failure", icon: HiInformationCircle, children: error })), state === 'ready' && !isCoa && !arcTaxonomy && (_jsxs(Alert, { color: "info", icon: HiInformationCircle, children: ["No ", (_a = ARC_TYPES.find((t) => t.value === arcType)) === null || _a === void 0 ? void 0 : _a.label.toLowerCase(), ' ', "hierarchy is published for", ' ', _jsx("span", { className: "font-mono", children: baseStandard !== null && baseStandard !== void 0 ? baseStandard : 'this taxonomy' }), "."] })), state === 'ready' && !isCoa && arcTaxonomy && forest.length === 0 && (_jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "No arcs in this hierarchy." })), state === 'ready' && isCoa && forest.length === 0 && (_jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "No accounts in this chart of accounts." })), state === 'ready' && forest.length > 0 && (_jsx("div", { role: "tree", "aria-label": "Taxonomy hierarchy", className: "min-h-0 flex-1 overflow-auto pr-1 font-mono text-sm", children: forest.map((root) => (_jsx(HierarchyRow, { path: root.id, node: root, depth: 0, showWeights: showWeights, collapsed: collapsed, onToggle: toggle, selectedElementId: selectedElementId, onSelectElement: onSelectElement }, root.id))) }))] }) }));
366
+ }
367
+ function HierarchyRow({ node, path, depth, showWeights, collapsed, onToggle, selectedElementId, onSelectElement, }) {
368
+ var _a, _b, _c;
369
+ const hasChildren = node.children.length > 0;
370
+ const isCollapsed = collapsed.has(node.id);
371
+ const isSelected = node.id === selectedElementId;
372
+ const label = (_b = (_a = node.name) !== null && _a !== void 0 ? _a : node.qname) !== null && _b !== void 0 ? _b : node.id;
373
+ return (_jsxs("div", { role: "treeitem", "aria-level": depth + 1, "aria-selected": isSelected, "aria-expanded": hasChildren ? !isCollapsed : undefined, children: [_jsxs("div", { className: `flex items-center gap-1 rounded px-1 py-0.5 ${isSelected
374
+ ? 'bg-primary-100 dark:bg-primary-900/50'
375
+ : 'hover:bg-gray-100 dark:hover:bg-gray-800'}`, style: { paddingLeft: `${depth * 16}px` }, children: [hasChildren ? (_jsx("button", { onClick: () => onToggle(node.id), className: "shrink-0 text-gray-400 hover:text-gray-700 dark:hover:text-gray-200", "aria-label": isCollapsed ? `Expand ${label}` : `Collapse ${label}`, children: isCollapsed ? (_jsx(HiChevronRight, { className: "h-4 w-4" })) : (_jsx(HiChevronDown, { className: "h-4 w-4" })) })) : (_jsx("span", { className: "inline-block w-4 shrink-0" })), showWeights && node.weight != null && (_jsx("span", { className: `w-3 shrink-0 text-center font-bold ${node.weight >= 0
376
+ ? 'text-green-600 dark:text-green-400'
377
+ : 'text-red-600 dark:text-red-400'}`, title: `calc weight ${node.weight}`, children: node.weight >= 0 ? '+' : '−' })), _jsxs("button", { onClick: () => onSelectElement(node.id), className: "flex min-w-0 flex-1 items-center gap-2 text-left", title: (_c = node.qname) !== null && _c !== void 0 ? _c : undefined, children: [_jsx("span", { className: `truncate ${isSelected
378
+ ? 'text-primary-900 dark:text-primary-100'
379
+ : 'text-gray-800 dark:text-gray-100'}`, children: label }), node.isAbstract ? (_jsx(Badge, { color: "purple", size: "xs", className: "shrink-0", children: "abstract" })) : node.trait ? (_jsx(Badge, { color: classificationColor(node.trait), size: "xs", className: "shrink-0", children: node.trait })) : null] })] }), hasChildren && !isCollapsed && (_jsx("div", { role: "group", children: node.children.map((c) => (_jsx(HierarchyRow, { path: `${path}/${c.id}`, node: c, depth: depth + 1, showWeights: showWeights, collapsed: collapsed, onToggle: onToggle, selectedElementId: selectedElementId, onSelectElement: onSelectElement }, `${path}/${c.id}`))) }))] }));
380
+ }
@@ -0,0 +1,6 @@
1
+ import type { LibraryTaxonomy } from '@robosystems/client/clients';
2
+ export declare function TaxonomySidebar({ taxonomies, selectedId, onSelect, }: {
3
+ taxonomies: LibraryTaxonomy[];
4
+ selectedId: string | null;
5
+ onSelect: (id: string) => void;
6
+ }): import("react").JSX.Element;