@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.
- package/LICENSE +21 -0
- package/README.md +252 -0
- package/actions/entity-actions.d.ts +8 -0
- package/actions/entity-actions.js +16 -0
- package/actions/graph-actions.d.ts +3 -0
- package/actions/graph-actions.js +13 -0
- package/auth-components/AppSwitcher.d.ts +6 -0
- package/auth-components/AppSwitcher.js +28 -0
- package/auth-components/AuthGuard.d.ts +7 -0
- package/auth-components/AuthGuard.js +31 -0
- package/auth-components/AuthProvider.d.ts +9 -0
- package/auth-components/AuthProvider.js +565 -0
- package/auth-components/SessionWarningDialog.d.ts +1 -0
- package/auth-components/SessionWarningDialog.js +51 -0
- package/auth-components/SignInForm.d.ts +20 -0
- package/auth-components/SignInForm.js +139 -0
- package/auth-components/SignUpForm.d.ts +15 -0
- package/auth-components/SignUpForm.js +167 -0
- package/auth-components/TurnstileWidget.d.ts +99 -0
- package/auth-components/TurnstileWidget.js +224 -0
- package/auth-components/index.d.ts +6 -0
- package/auth-components/index.js +6 -0
- package/auth-core/cleanup.d.ts +44 -0
- package/auth-core/cleanup.js +157 -0
- package/auth-core/client.d.ts +121 -0
- package/auth-core/client.js +767 -0
- package/auth-core/config.d.ts +19 -0
- package/auth-core/config.js +56 -0
- package/auth-core/hooks.d.ts +19 -0
- package/auth-core/hooks.js +63 -0
- package/auth-core/index.d.ts +6 -0
- package/auth-core/index.js +6 -0
- package/auth-core/sso.d.ts +42 -0
- package/auth-core/sso.js +281 -0
- package/auth-core/token-storage.d.ts +58 -0
- package/auth-core/token-storage.js +177 -0
- package/auth-core/types.d.ts +145 -0
- package/auth-core/types.js +1 -0
- package/components/EntitySelector.d.ts +44 -0
- package/components/EntitySelector.js +64 -0
- package/components/EntitySelectorCore.d.ts +45 -0
- package/components/EntitySelectorCore.js +67 -0
- package/components/GraphSelectorCore.d.ts +21 -0
- package/components/GraphSelectorCore.js +39 -0
- package/components/PageLayout.d.ts +8 -0
- package/components/PageLayout.js +5 -0
- package/components/RepositoryGuard.d.ts +68 -0
- package/components/RepositoryGuard.js +91 -0
- package/components/console/ConsoleContent.d.ts +4 -0
- package/components/console/ConsoleContent.js +612 -0
- package/components/console/ConsoleMarkdown.d.ts +11 -0
- package/components/console/ConsoleMarkdown.js +49 -0
- package/components/console/ProgressiveText.d.ts +6 -0
- package/components/console/ProgressiveText.js +21 -0
- package/components/console/graphAwareConfig.d.ts +70 -0
- package/components/console/graphAwareConfig.js +411 -0
- package/components/console/index.d.ts +6 -0
- package/components/console/index.js +4 -0
- package/components/console/types.d.ts +84 -0
- package/components/console/types.js +1 -0
- package/components/graph-filters.d.ts +75 -0
- package/components/graph-filters.js +111 -0
- package/components/index.d.ts +10 -0
- package/components/index.js +9 -0
- package/components/repositories/ActiveSubscriptions.d.ts +26 -0
- package/components/repositories/ActiveSubscriptions.js +103 -0
- package/components/repositories/BrowseRepositories.d.ts +11 -0
- package/components/repositories/BrowseRepositories.js +184 -0
- package/components/repositories/index.d.ts +2 -0
- package/components/repositories/index.js +2 -0
- package/components/search/SearchContent.d.ts +4 -0
- package/components/search/SearchContent.js +168 -0
- package/components/search/index.d.ts +2 -0
- package/components/search/index.js +1 -0
- package/components/search/types.d.ts +15 -0
- package/components/search/types.js +1 -0
- package/contexts/entity-context.d.ts +28 -0
- package/contexts/entity-context.js +89 -0
- package/contexts/graph-context.d.ts +27 -0
- package/contexts/graph-context.js +128 -0
- package/contexts/index.d.ts +5 -0
- package/contexts/index.js +5 -0
- package/contexts/org-context.d.ts +25 -0
- package/contexts/org-context.js +64 -0
- package/contexts/service-offerings-context.d.ts +45 -0
- package/contexts/service-offerings-context.js +98 -0
- package/contexts/sidebar-context.d.ts +18 -0
- package/contexts/sidebar-context.js +44 -0
- package/hooks/index.d.ts +6 -0
- package/hooks/index.js +6 -0
- package/hooks/use-api-error.d.ts +3 -0
- package/hooks/use-api-error.js +24 -0
- package/hooks/use-media-query.d.ts +1 -0
- package/hooks/use-media-query.js +21 -0
- package/hooks/use-streaming-query.d.ts +22 -0
- package/hooks/use-streaming-query.js +142 -0
- package/hooks/use-toast.d.ts +37 -0
- package/hooks/use-toast.js +117 -0
- package/hooks/use-user-limits.d.ts +12 -0
- package/hooks/use-user-limits.js +73 -0
- package/hooks/use-user.d.ts +11 -0
- package/hooks/use-user.js +39 -0
- package/index.d.ts +29 -0
- package/index.js +77 -0
- package/lib/entity-cookie.d.ts +10 -0
- package/lib/entity-cookie.js +38 -0
- package/lib/graph-cookie.d.ts +11 -0
- package/lib/graph-cookie.js +62 -0
- package/lib/graph-tiers.d.ts +41 -0
- package/lib/graph-tiers.js +107 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/sidebar-cookie.d.ts +11 -0
- package/lib/sidebar-cookie.js +42 -0
- package/library/colors.d.ts +4 -0
- package/library/colors.js +41 -0
- package/library/components/ClassificationPicker.d.ts +8 -0
- package/library/components/ClassificationPicker.js +55 -0
- package/library/components/ElementBrowser.d.ts +11 -0
- package/library/components/ElementBrowser.js +106 -0
- package/library/components/ElementDetail.d.ts +7 -0
- package/library/components/ElementDetail.js +97 -0
- package/library/components/LibraryHierarchy.d.ts +13 -0
- package/library/components/LibraryHierarchy.js +380 -0
- package/library/components/TaxonomySidebar.d.ts +6 -0
- package/library/components/TaxonomySidebar.js +45 -0
- package/library/index.d.ts +8 -0
- package/library/index.js +7 -0
- package/package.json +37 -0
- package/research/CoverageCard.d.ts +12 -0
- package/research/CoverageCard.js +15 -0
- package/research/CoverageGrid.d.ts +6 -0
- package/research/CoverageGrid.js +9 -0
- package/research/CoverageHistory.d.ts +5 -0
- package/research/CoverageHistory.js +7 -0
- package/research/ResearchArticle.d.ts +15 -0
- package/research/ResearchArticle.js +26 -0
- package/research/catalog.d.ts +14 -0
- package/research/catalog.js +42 -0
- package/research/index.d.ts +6 -0
- package/research/index.js +9 -0
- package/research/types.d.ts +38 -0
- package/research/types.js +4 -0
- package/task-monitoring/hooks.d.ts +26 -0
- package/task-monitoring/hooks.js +126 -0
- package/task-monitoring/index.d.ts +5 -0
- package/task-monitoring/index.js +7 -0
- package/task-monitoring/operationErrors.d.ts +82 -0
- package/task-monitoring/operationErrors.js +251 -0
- package/task-monitoring/operationHooks.d.ts +117 -0
- package/task-monitoring/operationHooks.js +536 -0
- package/task-monitoring/operationMonitor.d.ts +78 -0
- package/task-monitoring/operationMonitor.js +200 -0
- package/task-monitoring/operationTypes.d.ts +236 -0
- package/task-monitoring/operationTypes.js +60 -0
- package/task-monitoring/taskMonitor.d.ts +14 -0
- package/task-monitoring/taskMonitor.js +132 -0
- package/task-monitoring/types.d.ts +31 -0
- package/task-monitoring/types.js +1 -0
- package/theme/flowbite-theme.d.ts +758 -0
- package/theme/flowbite-theme.js +765 -0
- package/theme/index.d.ts +1 -0
- package/theme/index.js +1 -0
- package/types/entity.d.ts +19 -0
- package/types/index.d.ts +2 -0
- package/types/index.js +1 -0
- package/types/user.d.ts +6 -0
- package/ui-components/ConfirmModal.d.ts +41 -0
- package/ui-components/ConfirmModal.js +19 -0
- package/ui-components/EmptyState.d.ts +22 -0
- package/ui-components/EmptyState.js +10 -0
- package/ui-components/LoadingState.d.ts +27 -0
- package/ui-components/LoadingState.js +12 -0
- package/ui-components/Logo.d.ts +28 -0
- package/ui-components/Logo.js +95 -0
- package/ui-components/Spinner.d.ts +37 -0
- package/ui-components/Spinner.js +48 -0
- package/ui-components/StatCard.d.ts +21 -0
- package/ui-components/StatCard.js +10 -0
- package/ui-components/api-keys/ApiKeyDisplay.d.ts +13 -0
- package/ui-components/api-keys/ApiKeyDisplay.js +7 -0
- package/ui-components/api-keys/ApiKeyTable.d.ts +9 -0
- package/ui-components/api-keys/ApiKeyTable.js +17 -0
- package/ui-components/api-keys/ApiKeysCard.d.ts +6 -0
- package/ui-components/api-keys/ApiKeysCard.js +97 -0
- package/ui-components/api-keys/CreateApiKeyModal.d.ts +11 -0
- package/ui-components/api-keys/CreateApiKeyModal.js +47 -0
- package/ui-components/api-keys/SecureApiKeyField.d.ts +13 -0
- package/ui-components/api-keys/SecureApiKeyField.js +64 -0
- package/ui-components/api-keys/index.d.ts +5 -0
- package/ui-components/api-keys/index.js +5 -0
- package/ui-components/chat/ChatHeader.d.ts +12 -0
- package/ui-components/chat/ChatHeader.js +5 -0
- package/ui-components/chat/ChatInputArea.d.ts +16 -0
- package/ui-components/chat/ChatInputArea.js +9 -0
- package/ui-components/chat/ChatMessage.d.ts +7 -0
- package/ui-components/chat/ChatMessage.js +24 -0
- package/ui-components/chat/DeepResearchToggle.d.ts +8 -0
- package/ui-components/chat/DeepResearchToggle.js +13 -0
- package/ui-components/chat/index.d.ts +9 -0
- package/ui-components/chat/index.js +8 -0
- package/ui-components/chat/types.d.ts +23 -0
- package/ui-components/chat/types.js +51 -0
- package/ui-components/forms/PasswordRequirements.d.ts +10 -0
- package/ui-components/forms/PasswordRequirements.js +15 -0
- package/ui-components/forms/SettingsCard.d.ts +11 -0
- package/ui-components/forms/SettingsCard.js +5 -0
- package/ui-components/forms/SettingsFormField.d.ts +18 -0
- package/ui-components/forms/SettingsFormField.js +17 -0
- package/ui-components/forms/StatusAlert.d.ts +8 -0
- package/ui-components/forms/StatusAlert.js +7 -0
- package/ui-components/forms/index.d.ts +4 -0
- package/ui-components/forms/index.js +4 -0
- package/ui-components/index.d.ts +13 -0
- package/ui-components/index.js +18 -0
- package/ui-components/layout/CoreNavbar.d.ts +14 -0
- package/ui-components/layout/CoreNavbar.js +53 -0
- package/ui-components/layout/CoreSidebar.d.ts +28 -0
- package/ui-components/layout/CoreSidebar.js +62 -0
- package/ui-components/layout/LandingFooter.d.ts +28 -0
- package/ui-components/layout/LandingFooter.js +34 -0
- package/ui-components/layout/PageContainer.d.ts +8 -0
- package/ui-components/layout/PageContainer.js +17 -0
- package/ui-components/layout/PageHeader.d.ts +23 -0
- package/ui-components/layout/PageHeader.js +12 -0
- package/ui-components/layout/SettingsContainer.d.ts +10 -0
- package/ui-components/layout/SettingsContainer.js +9 -0
- package/ui-components/layout/ThemeToggle.d.ts +4 -0
- package/ui-components/layout/ThemeToggle.js +6 -0
- package/ui-components/layout/index.d.ts +7 -0
- package/ui-components/layout/index.js +7 -0
- package/ui-components/settings/GeneralInformationCard.d.ts +12 -0
- package/ui-components/settings/GeneralInformationCard.js +72 -0
- package/ui-components/settings/PasswordInformationCard.d.ts +10 -0
- package/ui-components/settings/PasswordInformationCard.js +167 -0
- package/ui-components/settings/SettingsPageHeader.d.ts +7 -0
- package/ui-components/settings/SettingsPageHeader.js +6 -0
- package/ui-components/settings/index.d.ts +3 -0
- package/ui-components/settings/index.js +3 -0
- package/ui-components/support/SupportModal.d.ts +15 -0
- package/ui-components/support/SupportModal.js +74 -0
- package/ui-components/support/index.d.ts +2 -0
- package/ui-components/support/index.js +1 -0
- package/ui-components/types/index.d.ts +39 -0
- package/ui-components/types/index.js +1 -0
- package/utils/index.d.ts +31 -0
- package/utils/index.js +38 -0
- package/utils/turnstile-config.d.ts +11 -0
- package/utils/turnstile-config.js +19 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Badge, Card } from 'flowbite-react';
|
|
4
|
+
const SECTION_ORDER = ['reporting', 'chart_of_accounts', 'schedule', 'mapping'];
|
|
5
|
+
const SECTION_LABELS = {
|
|
6
|
+
reporting: 'Reporting',
|
|
7
|
+
chart_of_accounts: 'Chart of Accounts',
|
|
8
|
+
schedule: 'Schedules',
|
|
9
|
+
mapping: 'Mappings',
|
|
10
|
+
};
|
|
11
|
+
export function TaxonomySidebar({ taxonomies, selectedId, onSelect, }) {
|
|
12
|
+
var _a;
|
|
13
|
+
// Group by taxonomyType, preserve SECTION_ORDER
|
|
14
|
+
const groups = [];
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
const grouped = new Map();
|
|
17
|
+
for (const t of taxonomies) {
|
|
18
|
+
const type = (_a = t.taxonomyType) !== null && _a !== void 0 ? _a : 'reporting';
|
|
19
|
+
if (!grouped.has(type))
|
|
20
|
+
grouped.set(type, []);
|
|
21
|
+
grouped.get(type).push(t);
|
|
22
|
+
}
|
|
23
|
+
for (const type of SECTION_ORDER) {
|
|
24
|
+
if (grouped.has(type)) {
|
|
25
|
+
groups.push({ type, items: grouped.get(type) });
|
|
26
|
+
seen.add(type);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Any remaining types not in SECTION_ORDER
|
|
30
|
+
for (const [type, items] of grouped) {
|
|
31
|
+
if (!seen.has(type))
|
|
32
|
+
groups.push({ type, items });
|
|
33
|
+
}
|
|
34
|
+
const showSections = groups.length > 1;
|
|
35
|
+
return (_jsx("aside", { className: "col-span-12 min-h-0 md:col-span-3", 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: "Taxonomies" }), _jsx("div", { className: "min-h-0 flex-1 space-y-3 overflow-y-auto", children: groups.map(({ type, items }, gi) => {
|
|
36
|
+
var _a;
|
|
37
|
+
return (_jsxs("div", { children: [showSections && (_jsx("div", { className: `px-1 pb-1 text-[10px] font-semibold tracking-wider text-gray-400 uppercase dark:text-gray-500 ${gi > 0 ? 'border-t border-gray-100 pt-3 dark:border-gray-700' : ''}`, children: (_a = SECTION_LABELS[type]) !== null && _a !== void 0 ? _a : type })), _jsx("ul", { className: "space-y-1", children: items.map((t) => {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const isSelected = t.id === selectedId;
|
|
40
|
+
return (_jsx("li", { children: _jsxs("button", { onClick: () => onSelect(t.id), className: `w-full rounded px-3 py-2 text-left text-sm transition-colors ${isSelected
|
|
41
|
+
? 'bg-primary-100 text-primary-900 dark:bg-primary-900/50 dark:text-primary-100'
|
|
42
|
+
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-800'}`, children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [t.standard ? (_jsxs("span", { className: "truncate font-mono text-xs", title: `${t.standard}/${(_a = t.version) !== null && _a !== void 0 ? _a : ''}`, children: [t.standard, _jsxs("span", { className: "text-gray-400 dark:text-gray-500", children: ["/", (_b = t.version) !== null && _b !== void 0 ? _b : ''] })] })) : (_jsx("span", { className: "truncate text-xs font-medium", title: t.name, children: t.name })), t.elementCount !== null && (_jsx(Badge, { color: "gray", size: "xs", className: "shrink-0", children: t.elementCount.toLocaleString() }))] }), t.standard && (_jsx("div", { className: "truncate text-xs text-gray-500 dark:text-gray-400", title: t.name, children: t.name }))] }) }, t.id));
|
|
43
|
+
}) })] }, type));
|
|
44
|
+
}) })] }) }));
|
|
45
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { LIBRARY_GRAPH_ID, LibraryClient } from '@robosystems/client/clients';
|
|
2
|
+
export type { GetLibraryElementIdentifier, LibraryArc, LibraryElement, LibraryElementArc, LibraryElementClassification, LibraryElementDetail, LibraryEquivalence, LibraryLabel, LibraryReference, LibrarySearchResult, LibraryStructure, LibraryTaxonomy, LibraryTaxonomyDetail, ListLibraryElementsOptions, ListLibraryStructuresOptions, ListLibraryTaxonomyArcsOptions, ListLibraryTaxonomyArcsResult, SearchLibraryElementsOptions, } from '@robosystems/client/clients';
|
|
3
|
+
export { arcTypeColor, classificationColor } from './colors';
|
|
4
|
+
export { ClassificationPicker } from './components/ClassificationPicker';
|
|
5
|
+
export { ElementBrowser } from './components/ElementBrowser';
|
|
6
|
+
export { ElementDetail } from './components/ElementDetail';
|
|
7
|
+
export { LibraryHierarchy } from './components/LibraryHierarchy';
|
|
8
|
+
export { TaxonomySidebar } from './components/TaxonomySidebar';
|
package/library/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { LIBRARY_GRAPH_ID, LibraryClient } from '@robosystems/client/clients';
|
|
2
|
+
export { arcTypeColor, classificationColor } from './colors';
|
|
3
|
+
export { ClassificationPicker } from './components/ClassificationPicker';
|
|
4
|
+
export { ElementBrowser } from './components/ElementBrowser';
|
|
5
|
+
export { ElementDetail } from './components/ElementDetail';
|
|
6
|
+
export { LibraryHierarchy } from './components/LibraryHierarchy';
|
|
7
|
+
export { TaxonomySidebar } from './components/TaxonomySidebar';
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@robosystems/core",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Shared RoboSystems frontend core library (auth, contexts, task monitoring, UI components) for the RoboSystems Next.js apps",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./index.js",
|
|
9
|
+
"types": "./index.d.ts",
|
|
10
|
+
"sideEffects": [
|
|
11
|
+
"./index.js"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/RoboFinSystems/robosystems-core.git"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"registry": "https://registry.npmjs.org/"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.0.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"react-markdown": "^10",
|
|
26
|
+
"remark-gfm": "^4.0.1",
|
|
27
|
+
"tailwind-merge": "^3"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@robosystems/client": ">=0.3.2 <1",
|
|
31
|
+
"flowbite-react": "^0.12.5",
|
|
32
|
+
"next": ">=15 <17",
|
|
33
|
+
"react": ">=18 <20",
|
|
34
|
+
"react-dom": ">=18 <20",
|
|
35
|
+
"react-icons": ">=4 <6"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CoverageItem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* A coverage tile: full-bleed thumbnail on top, then ticker/label, title, summary, and
|
|
4
|
+
* prior-report count. Self-contained (not the shared Flowbite Card) so the cover image
|
|
5
|
+
* sits flush at the top of every card regardless of how much text each one has — the
|
|
6
|
+
* shared card theme centers its content (`justify-center`), which left the covers at
|
|
7
|
+
* different heights across a row.
|
|
8
|
+
*/
|
|
9
|
+
export declare function CoverageCard({ item, hrefBase, }: {
|
|
10
|
+
item: CoverageItem;
|
|
11
|
+
hrefBase?: string;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Link from 'next/link';
|
|
3
|
+
/**
|
|
4
|
+
* A coverage tile: full-bleed thumbnail on top, then ticker/label, title, summary, and
|
|
5
|
+
* prior-report count. Self-contained (not the shared Flowbite Card) so the cover image
|
|
6
|
+
* sits flush at the top of every card regardless of how much text each one has — the
|
|
7
|
+
* shared card theme centers its content (`justify-center`), which left the covers at
|
|
8
|
+
* different heights across a row.
|
|
9
|
+
*/
|
|
10
|
+
export function CoverageCard({ item, hrefBase = '/research', }) {
|
|
11
|
+
var _a;
|
|
12
|
+
return (_jsx(Link, { href: `${hrefBase}/${item.ticker.toLowerCase()}`, className: "group block h-full", children: _jsxs("div", { className: "flex h-full flex-col overflow-hidden rounded-lg border border-gray-200 bg-white/80 shadow-lg backdrop-blur-sm transition-shadow duration-300 hover:shadow-xl dark:border-gray-700 dark:bg-zinc-950", children: [item.assets.thumbnail && (
|
|
13
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
14
|
+
_jsx("img", { src: item.assets.thumbnail, alt: item.title, className: "aspect-video w-full object-cover" })), _jsxs("div", { className: "flex flex-1 flex-col gap-2 p-5", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-500 dark:text-gray-400", children: [_jsx("span", { className: "rounded bg-cyan-500/10 px-2 py-0.5 font-semibold text-cyan-600 dark:text-cyan-400", children: item.ticker }), item.coverage_label && _jsx("span", { children: item.coverage_label }), _jsx("span", { className: "ml-auto", children: (_a = item.date) === null || _a === void 0 ? void 0 : _a.slice(0, 10) })] }), _jsx("h3", { className: "text-lg font-bold text-gray-900 group-hover:text-cyan-600 dark:text-white dark:group-hover:text-cyan-400", children: item.title }), _jsx("p", { className: "line-clamp-3 text-sm text-gray-600 dark:text-gray-300", children: item.summary }), item.history.length > 0 && (_jsxs("p", { className: "mt-auto pt-1 text-xs text-gray-400", children: ["+", item.history.length, " prior", ' ', item.history.length === 1 ? 'report' : 'reports'] }))] })] }) }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { CoverageCard } from './CoverageCard';
|
|
3
|
+
/** Responsive grid of coverage cards. */
|
|
4
|
+
export function CoverageGrid({ items, hrefBase, }) {
|
|
5
|
+
if (!items.length) {
|
|
6
|
+
return (_jsx("p", { className: "py-12 text-center text-gray-500 dark:text-gray-400", children: "No coverage yet \u2014 check back soon." }));
|
|
7
|
+
}
|
|
8
|
+
return (_jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: items.map((item) => (_jsx(CoverageCard, { item: item, hrefBase: hrefBase }, item.ticker))) }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/** The continuing-coverage timeline: prior dated reports with links to their archived assets. */
|
|
3
|
+
export function CoverageHistory({ history }) {
|
|
4
|
+
if (!history.length)
|
|
5
|
+
return null;
|
|
6
|
+
return (_jsxs("section", { className: "mt-12 border-t border-gray-200 pt-8 dark:border-gray-800", children: [_jsx("h2", { className: "mb-4 text-xl font-bold text-gray-900 dark:text-white", children: "Coverage history" }), _jsx("ol", { className: "space-y-3", children: history.map((h) => (_jsxs("li", { className: "flex flex-col gap-1 rounded-lg border border-gray-200 p-4 sm:flex-row sm:items-center sm:gap-4 dark:border-gray-800", children: [_jsx("span", { className: "shrink-0 rounded bg-gray-100 px-2 py-0.5 text-xs font-semibold text-gray-600 dark:bg-gray-800 dark:text-gray-300", children: h.version }), _jsxs("span", { className: "flex-1 text-sm text-gray-700 dark:text-gray-200", children: [h.title || 'Prior report', h.legacy_ticker && (_jsxs("em", { className: "text-gray-400", children: [" (as ", h.legacy_ticker, ")"] }))] }), _jsxs("span", { className: "flex gap-3 text-xs", children: [(h.youtube_url || h.assets.video) && (_jsx("a", { href: h.youtube_url || h.assets.video, className: "text-cyan-600 hover:underline dark:text-cyan-400", children: "Video" })), h.assets.podcast_mp3 && (_jsx("a", { href: h.assets.podcast_mp3, className: "text-cyan-600 hover:underline dark:text-cyan-400", children: "Podcast" })), h.assets.brief && (_jsx("a", { href: h.assets.brief, className: "text-cyan-600 hover:underline dark:text-cyan-400", children: "Brief" }))] })] }, h.version))) })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CoverageItem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The full coverage report: native video, the Q&A podcast (surfaced as a prominent
|
|
4
|
+
* "Listen" card right under the video), then the brief rendered from markdown (its own
|
|
5
|
+
* leading H1 is stripped — we render the title above it), and the continuing-coverage
|
|
6
|
+
* history. Works in a server component (SSR'd for SEO) or a client one.
|
|
7
|
+
* Styled for a dark background — render inside a dark (`bg-black`/`.dark`) container.
|
|
8
|
+
* The prose styling mirrors the blog article body so research reads consistently with it
|
|
9
|
+
* (explicit `prose-invert` + element overrides rather than `dark:prose-invert`, which the
|
|
10
|
+
* Tailwind v4 + typography-plugin setup in these apps does not resolve from a `.dark` class).
|
|
11
|
+
*/
|
|
12
|
+
export declare function ResearchArticle({ item, briefMarkdown, }: {
|
|
13
|
+
item: CoverageItem;
|
|
14
|
+
briefMarkdown?: string;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import ReactMarkdown from 'react-markdown';
|
|
3
|
+
import remarkGfm from 'remark-gfm';
|
|
4
|
+
import { youtubeId } from './catalog';
|
|
5
|
+
import { CoverageHistory } from './CoverageHistory';
|
|
6
|
+
/**
|
|
7
|
+
* The full coverage report: native video, the Q&A podcast (surfaced as a prominent
|
|
8
|
+
* "Listen" card right under the video), then the brief rendered from markdown (its own
|
|
9
|
+
* leading H1 is stripped — we render the title above it), and the continuing-coverage
|
|
10
|
+
* history. Works in a server component (SSR'd for SEO) or a client one.
|
|
11
|
+
* Styled for a dark background — render inside a dark (`bg-black`/`.dark`) container.
|
|
12
|
+
* The prose styling mirrors the blog article body so research reads consistently with it
|
|
13
|
+
* (explicit `prose-invert` + element overrides rather than `dark:prose-invert`, which the
|
|
14
|
+
* Tailwind v4 + typography-plugin setup in these apps does not resolve from a `.dark` class).
|
|
15
|
+
*/
|
|
16
|
+
export function ResearchArticle({ item, briefMarkdown, }) {
|
|
17
|
+
var _a;
|
|
18
|
+
const body = (briefMarkdown || '').replace(/^#\s.*(\r?\n)+/, '');
|
|
19
|
+
const ytId = youtubeId(item.youtube_url);
|
|
20
|
+
const podcastYtId = youtubeId(item.podcast_youtube_url);
|
|
21
|
+
return (_jsxs("article", { className: "mx-auto max-w-3xl", children: [_jsxs("div", { className: "mb-3 flex flex-wrap items-center gap-2 text-sm text-gray-500 dark:text-gray-400", children: [_jsxs("span", { className: "rounded bg-cyan-500/10 px-2 py-0.5 font-semibold text-cyan-600 dark:text-cyan-400", children: [item.company, " \u00B7 ", item.ticker] }), item.coverage_label && _jsx("span", { children: item.coverage_label }), _jsx("span", { children: (_a = item.date) === null || _a === void 0 ? void 0 : _a.slice(0, 10) })] }), _jsx("h1", { className: "mb-6 text-3xl font-bold text-gray-900 dark:text-white", children: item.title }), ytId ? (_jsx("div", { className: "mb-8 aspect-video w-full overflow-hidden rounded-xl bg-black", children: _jsx("iframe", { className: "h-full w-full", src: `https://www.youtube.com/embed/${ytId}`, title: item.title, loading: "lazy", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true }) })) : (item.assets.video && (
|
|
22
|
+
// eslint-disable-next-line jsx-a11y/media-has-caption
|
|
23
|
+
_jsx("video", { controls: true, poster: item.assets.thumbnail, src: item.assets.video, className: "mb-8 aspect-video w-full rounded-xl bg-black" }))), (item.assets.podcast_mp3 || podcastYtId) && (_jsx("section", { className: "mb-8", children: item.assets.podcast_mp3 ? (_jsxs("div", { className: "rounded-xl border border-cyan-500/30 bg-gray-900/50 p-4", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [_jsx("p", { className: "text-sm font-semibold text-cyan-600 dark:text-cyan-400", children: "\uD83C\uDF99 Listen \u2014 Q&A podcast" }), podcastYtId && (_jsx("a", { href: item.podcast_youtube_url, target: "_blank", rel: "noopener noreferrer", className: "shrink-0 text-xs text-cyan-600 hover:underline dark:text-cyan-400", children: "Watch on YouTube \u2197" }))] }), _jsx("audio", { controls: true, preload: "none", src: item.assets.podcast_mp3, className: "w-full" })] })) : (
|
|
24
|
+
// no MP3 yet — fall back to the YouTube video player
|
|
25
|
+
_jsxs(_Fragment, { children: [_jsx("h2", { className: "mb-3 text-xl font-bold text-gray-900 dark:text-white", children: "Listen \u2014 Q&A podcast" }), _jsx("div", { className: "aspect-video w-full overflow-hidden rounded-xl bg-black", children: _jsx("iframe", { className: "h-full w-full", src: `https://www.youtube.com/embed/${podcastYtId}`, title: `${item.title} — Q&A podcast`, loading: "lazy", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true }) })] })) })), body && (_jsx("div", { className: "prose prose-lg prose-invert prose-headings:font-heading prose-headings:font-bold prose-headings:text-white prose-p:text-gray-300 prose-p:leading-relaxed prose-a:text-cyan-400 prose-a:no-underline hover:prose-a:text-cyan-300 prose-strong:text-white prose-strong:font-semibold prose-code:text-cyan-400 prose-code:bg-gray-800 prose-code:px-2 prose-code:py-1 prose-code:rounded prose-pre:bg-gray-900 prose-pre:border prose-pre:border-gray-800 prose-blockquote:border-l-cyan-500 prose-blockquote:text-gray-400 prose-blockquote:italic prose-ul:text-gray-300 prose-ol:text-gray-300 prose-li:marker:text-cyan-500 prose-table:border-gray-700 prose-th:bg-gray-900 prose-th:text-white prose-td:text-gray-300 max-w-none", children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: body }) })), _jsx(CoverageHistory, { history: item.history }), _jsxs("footer", { className: "mt-12 border-t border-gray-200 pt-6 dark:border-gray-800", children: [_jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Audio & music produced with ElevenLabs." }), _jsxs("a", { href: "https://elevenlabs.io/startup-grants", target: "_blank", rel: "noopener noreferrer", "aria-label": "Backed by the ElevenLabs Grants program", className: "mt-3 inline-block", children: [_jsx("img", { src: "/images/logos/elevenlabs-grants.webp", alt: "Backed by the ElevenLabs Grants program", width: 200, className: "h-auto dark:hidden" }), _jsx("img", { src: "/images/logos/elevenlabs-grants-white.webp", alt: "Backed by the ElevenLabs Grants program", width: 200, className: "hidden h-auto dark:block" })] }), _jsxs("p", { className: "mt-2 text-xs text-gray-400 dark:text-gray-500", children: ["Using ElevenLabs yourself? Our", ' ', _jsx("a", { href: "https://try.elevenlabs.io/v9z3wzm97gk3", target: "_blank", rel: "noopener noreferrer", className: "hover:underline", children: "referral link" }), ' ', "costs you nothing extra and supports this research."] })] })] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Catalog, CoverageItem } from './types';
|
|
2
|
+
export declare const CATALOG_URL: string;
|
|
3
|
+
/** Fetch the full catalog. `revalidate` (seconds) drives Next ISR on the server. */
|
|
4
|
+
export declare function fetchCatalog(revalidate?: number): Promise<Catalog>;
|
|
5
|
+
/** All coverage items, newest first (the catalog is already sorted, but be defensive). */
|
|
6
|
+
export declare function getAllCoverage(revalidate?: number): Promise<CoverageItem[]>;
|
|
7
|
+
/** One company's coverage (latest + history), or null if not in the catalog. */
|
|
8
|
+
export declare function getCoverage(ticker: string, revalidate?: number): Promise<CoverageItem | null>;
|
|
9
|
+
/** Tickers for generateStaticParams(). */
|
|
10
|
+
export declare function getCoverageTickers(): Promise<string[]>;
|
|
11
|
+
/** Fetch a brief's raw markdown (rendered to HTML by ResearchArticle). */
|
|
12
|
+
export declare function fetchBrief(url: string, revalidate?: number): Promise<string>;
|
|
13
|
+
/** Extract a YouTube video id from a youtu.be / watch?v= / embed URL (or null). */
|
|
14
|
+
export declare function youtubeId(url?: string | null): string | null;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Data access for the research catalog. The catalog is a single public JSON file served
|
|
2
|
+
// via the CloudFront CDN, produced by `just reindex` in robosystems-content-machine.
|
|
3
|
+
// Fetched server-side (SSG/ISR) in the public site and client-side in the logged-in app.
|
|
4
|
+
export const CATALOG_URL = process.env.NEXT_PUBLIC_RESEARCH_CATALOG_URL ||
|
|
5
|
+
'https://assets.robosystems.ai/content/index.json';
|
|
6
|
+
/** Fetch the full catalog. `revalidate` (seconds) drives Next ISR on the server. */
|
|
7
|
+
export async function fetchCatalog(revalidate = 3600) {
|
|
8
|
+
const res = await fetch(CATALOG_URL, { next: { revalidate } });
|
|
9
|
+
if (!res.ok)
|
|
10
|
+
throw new Error(`Research catalog fetch failed: ${res.status}`);
|
|
11
|
+
return (await res.json());
|
|
12
|
+
}
|
|
13
|
+
/** All coverage items, newest first (the catalog is already sorted, but be defensive). */
|
|
14
|
+
export async function getAllCoverage(revalidate = 3600) {
|
|
15
|
+
const { items } = await fetchCatalog(revalidate);
|
|
16
|
+
return [...items].sort((a, b) => (b.date || '').localeCompare(a.date || ''));
|
|
17
|
+
}
|
|
18
|
+
/** One company's coverage (latest + history), or null if not in the catalog. */
|
|
19
|
+
export async function getCoverage(ticker, revalidate = 3600) {
|
|
20
|
+
const { items } = await fetchCatalog(revalidate);
|
|
21
|
+
const t = ticker.toUpperCase();
|
|
22
|
+
return items.find((i) => i.ticker.toUpperCase() === t) || null;
|
|
23
|
+
}
|
|
24
|
+
/** Tickers for generateStaticParams(). */
|
|
25
|
+
export async function getCoverageTickers() {
|
|
26
|
+
const { items } = await fetchCatalog();
|
|
27
|
+
return items.map((i) => i.ticker);
|
|
28
|
+
}
|
|
29
|
+
/** Fetch a brief's raw markdown (rendered to HTML by ResearchArticle). */
|
|
30
|
+
export async function fetchBrief(url, revalidate = 3600) {
|
|
31
|
+
const res = await fetch(url, { next: { revalidate } });
|
|
32
|
+
if (!res.ok)
|
|
33
|
+
throw new Error(`Brief fetch failed: ${res.status}`);
|
|
34
|
+
return await res.text();
|
|
35
|
+
}
|
|
36
|
+
/** Extract a YouTube video id from a youtu.be / watch?v= / embed URL (or null). */
|
|
37
|
+
export function youtubeId(url) {
|
|
38
|
+
if (!url)
|
|
39
|
+
return null;
|
|
40
|
+
const m = url.match(/(?:youtu\.be\/|[?&]v=|embed\/)([A-Za-z0-9_-]{6,})/);
|
|
41
|
+
return m ? m[1] : null;
|
|
42
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { CATALOG_URL, fetchBrief, fetchCatalog, getAllCoverage, getCoverage, getCoverageTickers, youtubeId, } from './catalog';
|
|
2
|
+
export { CoverageCard } from './CoverageCard';
|
|
3
|
+
export { CoverageGrid } from './CoverageGrid';
|
|
4
|
+
export { CoverageHistory } from './CoverageHistory';
|
|
5
|
+
export { ResearchArticle } from './ResearchArticle';
|
|
6
|
+
export * from './types';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Research portal module — shared across robosystems-app (public/SEO) and
|
|
2
|
+
// roboinvestor-app (logged-in). Data layer reads the S3 catalog produced by
|
|
3
|
+
// robosystems-content-machine; components render the coverage + continuing-coverage thread.
|
|
4
|
+
export { CATALOG_URL, fetchBrief, fetchCatalog, getAllCoverage, getCoverage, getCoverageTickers, youtubeId, } from './catalog';
|
|
5
|
+
export { CoverageCard } from './CoverageCard';
|
|
6
|
+
export { CoverageGrid } from './CoverageGrid';
|
|
7
|
+
export { CoverageHistory } from './CoverageHistory';
|
|
8
|
+
export { ResearchArticle } from './ResearchArticle';
|
|
9
|
+
export * from './types';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface CoverageAssets {
|
|
2
|
+
video?: string;
|
|
3
|
+
short?: string;
|
|
4
|
+
podcast_mp3?: string;
|
|
5
|
+
podcast_mp4?: string;
|
|
6
|
+
brief?: string;
|
|
7
|
+
thumbnail?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CoverageVersion {
|
|
10
|
+
version: string;
|
|
11
|
+
date?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
legacy_ticker?: string;
|
|
14
|
+
youtube_url?: string;
|
|
15
|
+
assets: CoverageAssets;
|
|
16
|
+
}
|
|
17
|
+
export interface CoverageItem {
|
|
18
|
+
ticker: string;
|
|
19
|
+
company: string;
|
|
20
|
+
title: string;
|
|
21
|
+
summary: string;
|
|
22
|
+
tags: string[];
|
|
23
|
+
campaign?: string | null;
|
|
24
|
+
campaign_slug?: string | null;
|
|
25
|
+
coverage_label?: string | null;
|
|
26
|
+
date: string;
|
|
27
|
+
version: string;
|
|
28
|
+
youtube_url?: string;
|
|
29
|
+
short_youtube_url?: string;
|
|
30
|
+
podcast_youtube_url?: string;
|
|
31
|
+
assets: CoverageAssets;
|
|
32
|
+
history: CoverageVersion[];
|
|
33
|
+
}
|
|
34
|
+
export interface Catalog {
|
|
35
|
+
generated: string;
|
|
36
|
+
count: number;
|
|
37
|
+
items: CoverageItem[];
|
|
38
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TaskMonitorState } from './types';
|
|
2
|
+
export interface UseTaskMonitoringResult extends TaskMonitorState {
|
|
3
|
+
startMonitoring: (taskId: string, options?: {
|
|
4
|
+
maxAttempts?: number;
|
|
5
|
+
pollInterval?: number;
|
|
6
|
+
}) => Promise<any>;
|
|
7
|
+
cancelTask: () => Promise<void>;
|
|
8
|
+
reset: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function useTaskMonitoring(): UseTaskMonitoringResult;
|
|
11
|
+
export declare function useEntityCreationTask(): {
|
|
12
|
+
createEntityWithGraph: (entityData: any) => Promise<any>;
|
|
13
|
+
startMonitoring: (taskId: string, options?: {
|
|
14
|
+
maxAttempts?: number;
|
|
15
|
+
pollInterval?: number;
|
|
16
|
+
}) => Promise<any>;
|
|
17
|
+
cancelTask: () => Promise<void>;
|
|
18
|
+
reset: () => void;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
progress: number | null;
|
|
21
|
+
currentStep: string | null;
|
|
22
|
+
error: string | null;
|
|
23
|
+
taskId: string | null;
|
|
24
|
+
result: any | null;
|
|
25
|
+
};
|
|
26
|
+
export declare function getProgressFromStep(step?: string | null): number;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import * as SDK from '@robosystems/client';
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { taskMonitor } from './taskMonitor';
|
|
5
|
+
export function useTaskMonitoring() {
|
|
6
|
+
const [state, setState] = useState({
|
|
7
|
+
isLoading: false,
|
|
8
|
+
progress: null,
|
|
9
|
+
currentStep: null,
|
|
10
|
+
error: null,
|
|
11
|
+
taskId: null,
|
|
12
|
+
result: null,
|
|
13
|
+
});
|
|
14
|
+
const currentTaskId = useRef(null);
|
|
15
|
+
const reset = useCallback(() => {
|
|
16
|
+
setState({
|
|
17
|
+
isLoading: false,
|
|
18
|
+
progress: null,
|
|
19
|
+
currentStep: null,
|
|
20
|
+
error: null,
|
|
21
|
+
taskId: null,
|
|
22
|
+
result: null,
|
|
23
|
+
});
|
|
24
|
+
currentTaskId.current = null;
|
|
25
|
+
}, []);
|
|
26
|
+
const cancelTask = useCallback(async () => {
|
|
27
|
+
if (currentTaskId.current) {
|
|
28
|
+
try {
|
|
29
|
+
await taskMonitor.cancelTask(currentTaskId.current);
|
|
30
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: false, error: 'Task was cancelled', result: null })));
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error('Failed to cancel task:', error);
|
|
34
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { error: 'Failed to cancel task', result: null })));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
const startMonitoring = useCallback(async (taskId, options) => {
|
|
39
|
+
currentTaskId.current = taskId;
|
|
40
|
+
setState({
|
|
41
|
+
isLoading: true,
|
|
42
|
+
progress: null,
|
|
43
|
+
currentStep: null,
|
|
44
|
+
error: null,
|
|
45
|
+
taskId,
|
|
46
|
+
result: null,
|
|
47
|
+
});
|
|
48
|
+
try {
|
|
49
|
+
const result = await taskMonitor.pollTask({
|
|
50
|
+
taskId,
|
|
51
|
+
maxAttempts: options === null || options === void 0 ? void 0 : options.maxAttempts,
|
|
52
|
+
pollInterval: options === null || options === void 0 ? void 0 : options.pollInterval,
|
|
53
|
+
onProgress: (status) => {
|
|
54
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { progress: status.progress || null, currentStep: status.step || status.message || 'Processing...' })));
|
|
55
|
+
},
|
|
56
|
+
onComplete: (result) => {
|
|
57
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: false, progress: 100, currentStep: 'Completed', result: result })));
|
|
58
|
+
},
|
|
59
|
+
onError: (errorMsg) => {
|
|
60
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: false, error: errorMsg, result: null })));
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return result.details || result;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
67
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: false, error: errorMessage, result: null })));
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
currentTaskId.current = null;
|
|
72
|
+
}
|
|
73
|
+
}, []);
|
|
74
|
+
// Cleanup on unmount
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
return () => {
|
|
77
|
+
if (currentTaskId.current) {
|
|
78
|
+
taskMonitor.cancelTask(currentTaskId.current);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}, []);
|
|
82
|
+
return Object.assign(Object.assign({}, state), { startMonitoring,
|
|
83
|
+
cancelTask,
|
|
84
|
+
reset });
|
|
85
|
+
}
|
|
86
|
+
// Convenience hook for entity creation specifically
|
|
87
|
+
export function useEntityCreationTask() {
|
|
88
|
+
const taskMonitoring = useTaskMonitoring();
|
|
89
|
+
const createEntityWithGraph = useCallback(async (entityData) => {
|
|
90
|
+
try {
|
|
91
|
+
// Call the SDK function to create company with new graph
|
|
92
|
+
// Note: createCompanyWithNewGraph function does not exist in SDK
|
|
93
|
+
// This is a placeholder implementation
|
|
94
|
+
const response = await SDK.createGraph({
|
|
95
|
+
body: entityData,
|
|
96
|
+
});
|
|
97
|
+
const responseData = response.data;
|
|
98
|
+
if (!(responseData === null || responseData === void 0 ? void 0 : responseData.operationId)) {
|
|
99
|
+
throw new Error('No operation ID returned from company creation');
|
|
100
|
+
}
|
|
101
|
+
// Start monitoring the task
|
|
102
|
+
const result = await taskMonitoring.startMonitoring(responseData.operationId);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error('Company creation failed:', error);
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}, [taskMonitoring]);
|
|
110
|
+
return Object.assign(Object.assign({}, taskMonitoring), { createEntityWithGraph });
|
|
111
|
+
}
|
|
112
|
+
// Helper function to get progress percentage from step name
|
|
113
|
+
export function getProgressFromStep(step) {
|
|
114
|
+
if (!step)
|
|
115
|
+
return 0;
|
|
116
|
+
const stepProgress = {
|
|
117
|
+
initializing: 10,
|
|
118
|
+
checking_mode: 20,
|
|
119
|
+
creating_database: 40,
|
|
120
|
+
installing_labels: 60,
|
|
121
|
+
creating_company: 80,
|
|
122
|
+
updating_user_graph: 90,
|
|
123
|
+
completed: 100,
|
|
124
|
+
};
|
|
125
|
+
return stepProgress[step.toLowerCase()] || 0;
|
|
126
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling for operation-based monitoring
|
|
3
|
+
*/
|
|
4
|
+
export declare enum OperationErrorCode {
|
|
5
|
+
CONNECTION_FAILED = "CONNECTION_FAILED",
|
|
6
|
+
CONNECTION_LOST = "CONNECTION_LOST",
|
|
7
|
+
TIMEOUT = "TIMEOUT",
|
|
8
|
+
OPERATION_NOT_FOUND = "OPERATION_NOT_FOUND",
|
|
9
|
+
OPERATION_CANCELLED = "OPERATION_CANCELLED",
|
|
10
|
+
OPERATION_FAILED = "OPERATION_FAILED",
|
|
11
|
+
UNAUTHORIZED = "UNAUTHORIZED",
|
|
12
|
+
FORBIDDEN = "FORBIDDEN",
|
|
13
|
+
INSUFFICIENT_CREDITS = "INSUFFICIENT_CREDITS",
|
|
14
|
+
RESOURCE_NOT_FOUND = "RESOURCE_NOT_FOUND",
|
|
15
|
+
RESOURCE_LOCKED = "RESOURCE_LOCKED",
|
|
16
|
+
RESOURCE_LIMIT_EXCEEDED = "RESOURCE_LIMIT_EXCEEDED",
|
|
17
|
+
INVALID_INPUT = "INVALID_INPUT",
|
|
18
|
+
INVALID_QUERY = "INVALID_QUERY",
|
|
19
|
+
SCHEMA_VIOLATION = "SCHEMA_VIOLATION",
|
|
20
|
+
INTERNAL_ERROR = "INTERNAL_ERROR",
|
|
21
|
+
SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
|
|
22
|
+
RATE_LIMITED = "RATE_LIMITED"
|
|
23
|
+
}
|
|
24
|
+
export declare class OperationError extends Error {
|
|
25
|
+
code: OperationErrorCode;
|
|
26
|
+
details?: any;
|
|
27
|
+
constructor(code: OperationErrorCode, message: string, details?: any);
|
|
28
|
+
/**
|
|
29
|
+
* Check if error is recoverable
|
|
30
|
+
*/
|
|
31
|
+
isRecoverable(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Check if error requires user action
|
|
34
|
+
*/
|
|
35
|
+
requiresUserAction(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Get user-friendly message
|
|
38
|
+
*/
|
|
39
|
+
getUserMessage(): string;
|
|
40
|
+
/**
|
|
41
|
+
* Get recommended action for the user
|
|
42
|
+
*/
|
|
43
|
+
getRecommendedAction(): string | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse SSE error events into OperationError
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseOperationError(event: any): OperationError;
|
|
49
|
+
/**
|
|
50
|
+
* Error recovery strategies
|
|
51
|
+
*/
|
|
52
|
+
export declare class ErrorRecovery {
|
|
53
|
+
private retryCount;
|
|
54
|
+
private readonly maxRetries;
|
|
55
|
+
private readonly baseDelay;
|
|
56
|
+
/**
|
|
57
|
+
* Determine if we should retry
|
|
58
|
+
*/
|
|
59
|
+
shouldRetry(operationId: string, error: OperationError): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Get retry delay with exponential backoff
|
|
62
|
+
*/
|
|
63
|
+
getRetryDelay(operationId: string): number;
|
|
64
|
+
/**
|
|
65
|
+
* Reset retry count for an operation
|
|
66
|
+
*/
|
|
67
|
+
reset(operationId: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* Clear all retry counts
|
|
70
|
+
*/
|
|
71
|
+
clear(): void;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Error notification helper
|
|
75
|
+
*/
|
|
76
|
+
export interface ErrorNotification {
|
|
77
|
+
title: string;
|
|
78
|
+
message: string;
|
|
79
|
+
action?: string;
|
|
80
|
+
severity: 'info' | 'warning' | 'error';
|
|
81
|
+
}
|
|
82
|
+
export declare function createErrorNotification(error: OperationError): ErrorNotification;
|