@burdenoff/microfe-botmanor 2026.1109.2 → 2026.1110.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/botlit/components/Badge.js +2 -2
- package/dist/botlit/components/Badge.js.map +1 -1
- package/dist/botlit/components/ChannelSelectorSheet.js +4 -4
- package/dist/botlit/components/ChannelSelectorSheet.js.map +1 -1
- package/dist/botlit/components/LoadMore.d.ts +9 -0
- package/dist/botlit/components/LoadMore.js +22 -0
- package/dist/botlit/components/LoadMore.js.map +1 -0
- package/dist/botlit/components/index.d.ts +1 -0
- package/dist/botlit/hooks/internal/workspaceGateway.d.ts +47 -0
- package/dist/botlit/hooks/internal/workspaceGateway.js +59 -1
- package/dist/botlit/hooks/internal/workspaceGateway.js.map +1 -1
- package/dist/botlit/hooks/useAgents.d.ts +2 -6
- package/dist/botlit/hooks/useAgents.js +71 -95
- package/dist/botlit/hooks/useAgents.js.map +1 -1
- package/dist/botlit/hooks/useBots.d.ts +2 -6
- package/dist/botlit/hooks/useBots.js +51 -75
- package/dist/botlit/hooks/useBots.js.map +1 -1
- package/dist/botlit/hooks/useExecutions.d.ts +2 -2
- package/dist/botlit/hooks/useExecutions.js +30 -34
- package/dist/botlit/hooks/useExecutions.js.map +1 -1
- package/dist/botlit/hooks/useStore.d.ts +3 -3
- package/dist/botlit/hooks/useStore.js +41 -50
- package/dist/botlit/hooks/useStore.js.map +1 -1
- package/dist/botlit/pages/A2AAgentDetailPage.js +1 -1
- package/dist/botlit/pages/A2AAgentDetailPage.js.map +1 -1
- package/dist/botlit/pages/AgentDetailPage.js +1 -1
- package/dist/botlit/pages/AgentDetailPage.js.map +1 -1
- package/dist/botlit/pages/AgentsListPage.js +157 -156
- package/dist/botlit/pages/AgentsListPage.js.map +1 -1
- package/dist/botlit/pages/BotChannelsTab.js +24 -24
- package/dist/botlit/pages/BotChannelsTab.js.map +1 -1
- package/dist/botlit/pages/BotsListPage.js +147 -146
- package/dist/botlit/pages/BotsListPage.js.map +1 -1
- package/dist/botlit/pages/BucketsPage.js +3 -3
- package/dist/botlit/pages/BucketsPage.js.map +1 -1
- package/dist/botlit/pages/ChannelDetailPage.js +2 -2
- package/dist/botlit/pages/ChannelDetailPage.js.map +1 -1
- package/dist/botlit/pages/ExecutionsPage.js +141 -134
- package/dist/botlit/pages/ExecutionsPage.js.map +1 -1
- package/dist/botlit/pages/InstalledPackagesPage.js +43 -36
- package/dist/botlit/pages/InstalledPackagesPage.js.map +1 -1
- package/dist/botlit/pages/MCPServersPage.js +1 -1
- package/dist/botlit/pages/MCPServersPage.js.map +1 -1
- package/dist/botlit/pages/SettingsPage.js +2 -2
- package/dist/botlit/pages/SettingsPage.js.map +1 -1
- package/dist/botlit/pages/SkillDetailPage.js +1 -1
- package/dist/botlit/pages/SkillDetailPage.js.map +1 -1
- package/dist/botlit/pages/StorePackageDetailPage.js +1 -1
- package/dist/botlit/pages/StorePackageDetailPage.js.map +1 -1
- package/dist/botlit/pages/StorePage.js +150 -143
- package/dist/botlit/pages/StorePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,8 +4,8 @@ import { jsx as t } from "react/jsx-runtime";
|
|
|
4
4
|
var n = {
|
|
5
5
|
default: "bg-action-primary-bg text-action-primary-text",
|
|
6
6
|
secondary: "bg-secondary text-secondary-foreground",
|
|
7
|
-
success: "bg-
|
|
8
|
-
warning: "bg-
|
|
7
|
+
success: "bg-status-success-bg-subtle text-status-success-text",
|
|
8
|
+
warning: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
9
9
|
destructive: "bg-status-error-bg/20 text-status-error-text",
|
|
10
10
|
outline: "border border-border-subtle text-text-primary"
|
|
11
11
|
}, r = ({ className: r, variant: i = "default", ...a }) => /* @__PURE__ */ t("span", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.js","names":["variantStyles","Badge","props","cn","variant","className","StatusBadge","status"],"sources":["../../../src/botlit/components/Badge.tsx"],"sourcesContent":["/**\n * Badge Component\n * @module botlit/components\n */\n\nimport type { FC, HTMLAttributes } from 'react';\nimport { cn } from '../utils/cn';\n\ninterface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n variant?: 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline';\n}\n\nconst variantStyles = {\n default: 'bg-action-primary-bg text-action-primary-text',\n secondary: 'bg-secondary text-secondary-foreground',\n success: 'bg-
|
|
1
|
+
{"version":3,"file":"Badge.js","names":["variantStyles","Badge","props","cn","variant","className","StatusBadge","status"],"sources":["../../../src/botlit/components/Badge.tsx"],"sourcesContent":["/**\n * Badge Component\n * @module botlit/components\n */\n\nimport type { FC, HTMLAttributes } from 'react';\nimport { cn } from '../utils/cn';\n\ninterface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n variant?: 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline';\n}\n\nconst variantStyles = {\n default: 'bg-action-primary-bg text-action-primary-text',\n secondary: 'bg-secondary text-secondary-foreground',\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n destructive: 'bg-status-error-bg/20 text-status-error-text',\n outline: 'border border-border-subtle text-text-primary',\n};\n\nexport const Badge: FC<BadgeProps> = ({ className, variant = 'default', ...props }) => {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n variantStyles[variant],\n className\n )}\n {...props}\n />\n );\n};\n\n// Status-specific badges\nexport const StatusBadge: FC<{ status: string }> = ({ status }) => {\n const variants: Record<string, BadgeProps['variant']> = {\n active: 'success',\n ready: 'success',\n connected: 'success',\n running: 'success',\n completed: 'success',\n draft: 'secondary',\n pending: 'secondary',\n paused: 'warning',\n training: 'warning',\n indexing: 'warning',\n processing: 'warning',\n connecting: 'warning',\n error: 'destructive',\n failed: 'destructive',\n disconnected: 'destructive',\n archived: 'outline',\n cancelled: 'outline',\n };\n\n return <Badge variant={variants[status.toLowerCase()] || 'default'}>{status}</Badge>;\n};\n"],"mappings":";;;AAYA,IAAMA,IAAgB;CACpB,SAAS;CACT,WAAW;CACX,SAAS;CACT,SAAS;CACT,aAAa;CACb,SAAS;AACX,GAEaC,KAAyB,EAAE,cAAW,aAAU,WAAW,GAAGC,QAEvE,kBAAC,QAAD;CACE,WAAWC,EACT,2EACAH,EAAcI,IACdC,CACF;CACA,GAAIH;AACL,CAAA,GAKQI,KAAuC,EAAE,gBAqB7C,kBAACL,GAAD;CAAO,SAAS;EAnBrB,QAAQ;EACR,OAAO;EACP,WAAW;EACX,SAAS;EACT,WAAW;EACX,OAAO;EACP,SAAS;EACT,QAAQ;EACR,UAAU;EACV,UAAU;EACV,YAAY;EACZ,YAAY;EACZ,OAAO;EACP,QAAQ;EACR,cAAc;EACd,UAAU;EACV,WAAW;CAGU,EAASM,EAAO,YAAY,MAAM;CAAY,UAAAA;AAAc,CAAA"}
|
|
@@ -12,7 +12,7 @@ var s = ({ open: s, onClose: c, onSelectChannel: l, availableChannels: u, platfo
|
|
|
12
12
|
};
|
|
13
13
|
return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
|
|
14
14
|
}, [s, c]), s ? /* @__PURE__ */ a(r, { children: [/* @__PURE__ */ i("div", {
|
|
15
|
-
className: "fixed inset-0 bg-
|
|
15
|
+
className: "fixed inset-0 bg-bg-overlay z-40 transition-opacity duration-300",
|
|
16
16
|
onClick: c,
|
|
17
17
|
"aria-hidden": "true"
|
|
18
18
|
}), /* @__PURE__ */ a("div", {
|
|
@@ -55,9 +55,9 @@ var s = ({ open: s, onClose: c, onSelectChannel: l, availableChannels: u, platfo
|
|
|
55
55
|
children: u.map((e) => {
|
|
56
56
|
let n = e.platform, r = f[n] || {
|
|
57
57
|
label: n,
|
|
58
|
-
badgeClass: "bg-
|
|
59
|
-
accentBg: "bg-
|
|
60
|
-
accentText: "text-
|
|
58
|
+
badgeClass: "bg-bg-sunken text-text-secondary",
|
|
59
|
+
accentBg: "bg-accent",
|
|
60
|
+
accentText: "text-text-primary"
|
|
61
61
|
}, o = d[n];
|
|
62
62
|
return /* @__PURE__ */ i("button", {
|
|
63
63
|
onClick: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelSelectorSheet.js","names":["_Fragment","ChannelSelectorSheet","useEffect","open","handleEscape","onClose","Fragment","Button","X","availableChannels","platform","channel","meta","platformMeta","icon","platformIcons","onSelectChannel","Badge"],"sources":["../../../src/botlit/components/ChannelSelectorSheet.tsx"],"sourcesContent":["/**\n * ChannelSelectorSheet\n * @module botlit/components\n *\n * A slide-in sheet component that displays available communication channels\n * for selection when setting up bot integrations.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useEffect } from 'react';\nimport { X } from 'lucide-react';\nimport { Button } from './Button';\nimport { Badge } from './Badge';\nimport type { GlobalChannelData } from '../hooks/useUserPlatformConnection';\n\ninterface ChannelSelectorSheetProps {\n /** Controls visibility of the sheet */\n open: boolean;\n /** Callback when sheet is closed */\n onClose: () => void;\n /** Callback when a channel is selected */\n onSelectChannel: (channelId: string, platform: string) => void;\n /** Available channels to display (already filtered by backend) */\n availableChannels: GlobalChannelData[];\n /** Platform icons mapping */\n platformIcons: Record<string, ReactNode>;\n /** Platform metadata mapping */\n platformMeta: Record<\n string,\n { label: string; badgeClass: string; accentBg: string; accentText: string }\n >;\n}\n\nexport const ChannelSelectorSheet: FC<ChannelSelectorSheetProps> = ({\n open,\n onClose,\n onSelectChannel,\n availableChannels,\n platformIcons,\n platformMeta,\n}) => {\n // Lock body scroll when sheet is open\n useEffect(() => {\n if (open) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n return () => {\n document.body.style.overflow = '';\n };\n }, [open]);\n\n // Close on Escape key\n useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && open) {\n onClose();\n }\n };\n window.addEventListener('keydown', handleEscape);\n return () => window.removeEventListener('keydown', handleEscape);\n }, [open, onClose]);\n\n if (!open) return null;\n\n return (\n <>\n {/* Backdrop */}\n <div\n className=\"fixed inset-0 bg-
|
|
1
|
+
{"version":3,"file":"ChannelSelectorSheet.js","names":["_Fragment","ChannelSelectorSheet","useEffect","open","handleEscape","onClose","Fragment","Button","X","availableChannels","platform","channel","meta","platformMeta","icon","platformIcons","onSelectChannel","Badge"],"sources":["../../../src/botlit/components/ChannelSelectorSheet.tsx"],"sourcesContent":["/**\n * ChannelSelectorSheet\n * @module botlit/components\n *\n * A slide-in sheet component that displays available communication channels\n * for selection when setting up bot integrations.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useEffect } from 'react';\nimport { X } from 'lucide-react';\nimport { Button } from './Button';\nimport { Badge } from './Badge';\nimport type { GlobalChannelData } from '../hooks/useUserPlatformConnection';\n\ninterface ChannelSelectorSheetProps {\n /** Controls visibility of the sheet */\n open: boolean;\n /** Callback when sheet is closed */\n onClose: () => void;\n /** Callback when a channel is selected */\n onSelectChannel: (channelId: string, platform: string) => void;\n /** Available channels to display (already filtered by backend) */\n availableChannels: GlobalChannelData[];\n /** Platform icons mapping */\n platformIcons: Record<string, ReactNode>;\n /** Platform metadata mapping */\n platformMeta: Record<\n string,\n { label: string; badgeClass: string; accentBg: string; accentText: string }\n >;\n}\n\nexport const ChannelSelectorSheet: FC<ChannelSelectorSheetProps> = ({\n open,\n onClose,\n onSelectChannel,\n availableChannels,\n platformIcons,\n platformMeta,\n}) => {\n // Lock body scroll when sheet is open\n useEffect(() => {\n if (open) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n return () => {\n document.body.style.overflow = '';\n };\n }, [open]);\n\n // Close on Escape key\n useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && open) {\n onClose();\n }\n };\n window.addEventListener('keydown', handleEscape);\n return () => window.removeEventListener('keydown', handleEscape);\n }, [open, onClose]);\n\n if (!open) return null;\n\n return (\n <>\n {/* Backdrop */}\n <div\n className=\"fixed inset-0 bg-bg-overlay z-40 transition-opacity duration-300\"\n onClick={onClose}\n aria-hidden=\"true\"\n />\n\n {/* Sheet */}\n <div\n className=\"fixed inset-y-0 right-0 z-50 w-full sm:max-w-md bg-bg-surface shadow-xl transform transition-transform duration-300 ease-out flex flex-col\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"channel-selector-title\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-subtle shrink-0\">\n <div>\n <h2 id=\"channel-selector-title\" className=\"text-lg font-semibold\">\n Select a Channel\n </h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Choose a platform to connect to your bot\n </p>\n </div>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={onClose}\n className=\"shrink-0\"\n aria-label=\"Close\"\n >\n <X size={18} />\n </Button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto px-6 py-4\">\n {availableChannels.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <p className=\"text-sm text-text-secondary\">No channels available to configure.</p>\n <p className=\"text-xs text-text-secondary mt-2\">\n All available channels have already been connected to this bot.\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {availableChannels.map((channel) => {\n const platform = channel.platform as string;\n const meta = platformMeta[platform] || {\n label: platform,\n badgeClass: 'bg-bg-sunken text-text-secondary',\n accentBg: 'bg-accent',\n accentText: 'text-text-primary',\n };\n const icon = platformIcons[platform];\n\n return (\n <button\n key={channel.id}\n onClick={() => {\n onSelectChannel(channel.id, platform);\n onClose();\n }}\n className=\"w-full text-left p-4 rounded-lg border border-border-subtle bg-bg-surface hover:bg-bg-sunken/50 transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2\"\n >\n <div className=\"flex items-start gap-3\">\n {/* Icon */}\n <div className={`p-2 rounded-md ${meta.accentBg} shrink-0`}>\n <span className={meta.accentText}>{icon}</span>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-1\">\n <h3 className=\"font-medium text-sm\">{channel.name}</h3>\n <Badge className={`text-xs ${meta.badgeClass}`}>{meta.label}</Badge>\n </div>\n {channel.description && (\n <p className=\"text-xs text-text-secondary line-clamp-2\">\n {channel.description}\n </p>\n )}\n </div>\n\n {/* Arrow indicator */}\n <div className=\"shrink-0 self-center text-text-secondary\">\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M6 12L10 8L6 4\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </div>\n </div>\n </button>\n );\n })}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"px-6 py-4 border-t border-border-subtle shrink-0\">\n <Button variant=\"outline\" onClick={onClose} className=\"w-full\">\n Cancel\n </Button>\n </div>\n </div>\n </>\n );\n};\n"],"mappings":";;;;;;AAiCA,IAAaC,KAAuD,EAClE,SACA,YACA,oBACA,sBACA,kBACA,uBAGAC,SACMC,IACF,SAAS,KAAK,MAAM,WAAW,WAE/B,SAAS,KAAK,MAAM,WAAW,UAEpB;CACX,SAAS,KAAK,MAAM,WAAW;AACjC,IACC,CAACA,CAAI,CAAC,GAGTD,QAAgB;CACd,IAAME,KAAgB,MAAqB;EACzC,AAAI,EAAE,QAAQ,YAAYD,KACxBE,EAAQ;CAEZ;CAEA,OADA,OAAO,iBAAiB,WAAWD,CAAY,SAClC,OAAO,oBAAoB,WAAWA,CAAY;AACjE,GAAG,CAACD,GAAME,CAAO,CAAC,GAEbF,IAGH,kBAAAH,GAAA,EAAA,UAAA,CAEE,kBAAC,OAAD;CACE,WAAU;CACV,SAASK;CACT,eAAY;AACb,CAAA,GAGD,kBAAC,OAAD;CACE,WAAU;CACV,MAAK;CACL,cAAW;CACX,mBAAgB;CAJlB,UAAA;EAOE,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,IAAG;IAAyB,WAAU;IAAwB,UAAA;GAE9D,CAAA,GACJ,kBAAC,KAAD;IAAG,WAAU;IAAqC,UAAA;GAE/C,CAAA,CACA,EAAA,CAAA,GACL,kBAACE,GAAD;IACE,SAAQ;IACR,MAAK;IACL,SAASF;IACT,WAAU;IACV,cAAW;IAEX,UAAA,kBAACG,GAAD,EAAG,MAAM,GAAK,CAAA;GACR,CAAA,CACL;;EAGL,kBAAC,OAAD;GAAK,WAAU;GACZ,UAAAC,EAAkB,WAAW,IAC5B,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;KAA8B,UAAA;IAAsC,CAAA,GACjF,kBAAC,KAAD;KAAG,WAAU;KAAmC,UAAA;IAE7C,CAAA,CACA;GAEL,CAAA,IAAA,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAAA,EAAkB,KAAK,MAAY;KAClC,IAAMC,IAAWC,EAAQ,UACnBC,IAAOC,EAAaH,MAAa;MACrC,OAAOA;MACP,YAAY;MACZ,UAAU;MACV,YAAY;KACd,GACMI,IAAOC,EAAcL;KAE3B,OACE,kBAAC,UAAD;MAEE,eAAe;OAEb,AADAM,EAAgBL,EAAQ,IAAID,CAAQ,GACpCL,EAAQ;MACV;MACA,WAAU;MAEV,UAAA,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QAEE,kBAAC,OAAD;SAAK,WAAW,kBAAkBO,EAAK,SAAS;SAC9C,UAAA,kBAAC,QAAD;UAAM,WAAWA,EAAK;UAAa,UAAAE;SAAW,CAAA;QAC3C,CAAA;QAGL,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;WAAuB,UAAAH,EAAQ;UAAS,CAAA,GACtD,kBAACM,GAAD;WAAO,WAAW,WAAWL,EAAK;WAAe,UAAAA,EAAK;UAAa,CAAA,CAChE;SACJ,CAAA,GAAAD,EAAQ,eACP,kBAAC,KAAD;UAAG,WAAU;UACV,UAAAA,EAAQ;SACR,CAAA,CAEF;;QAGL,kBAAC,OAAD;SAAK,WAAU;SACb,UAAA,kBAAC,OAAD;UACE,OAAM;UACN,QAAO;UACP,SAAQ;UACR,MAAK;UACL,OAAM;UAEN,UAAA,kBAAC,QAAD;WACE,GAAE;WACF,QAAO;WACP,aAAY;WACZ,eAAc;WACd,gBAAe;UAChB,CAAA;SACE,CAAA;QACF,CAAA;OACF;;KACC,GA7CDA,EAAQ,EA6CP;IAEZ,CAAC;GACE,CAAA;EAEJ,CAAA;EAGL,kBAAC,OAAD;GAAK,WAAU;GACb,UAAA,kBAACJ,GAAD;IAAQ,SAAQ;IAAU,SAASF;IAAS,WAAU;IAAS,UAAA;GAEvD,CAAA;EACL,CAAA;CACF;AACL,CAAA,CAAA,EAAA,CAAA,IAzHc"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { Button as t } from "./Button.js";
|
|
3
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
4
|
+
// src/botlit/components/LoadMore.tsx
|
|
5
|
+
var i = ({ hasMore: i, loadingMore: a, onLoadMore: o, loadedCount: s }) => {
|
|
6
|
+
let c = e();
|
|
7
|
+
return i ? /* @__PURE__ */ r("div", {
|
|
8
|
+
className: "flex flex-col items-center gap-2 pt-4",
|
|
9
|
+
children: [/* @__PURE__ */ n(t, {
|
|
10
|
+
variant: "outline",
|
|
11
|
+
onClick: o,
|
|
12
|
+
isLoading: a,
|
|
13
|
+
children: c("botlit.common.loadMore", "Load more")
|
|
14
|
+
}), /* @__PURE__ */ n("p", {
|
|
15
|
+
className: "text-xs text-text-secondary tabular-nums",
|
|
16
|
+
children: c("botlit.common.loadedCount", "Showing {{count}}", { count: s })
|
|
17
|
+
})]
|
|
18
|
+
}) : null;
|
|
19
|
+
};
|
|
20
|
+
export { i as LoadMore };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=LoadMore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadMore.js","names":["LoadMore","t","useTr","hasMore","Button","onLoadMore","loadingMore","loadedCount"],"sources":["../../../src/botlit/components/LoadMore.tsx"],"sourcesContent":["/**\n * LoadMore Component\n * @module botlit/components\n *\n * Load-more control for offset-paginated lists. Renders nothing until the\n * list reports more pages (`hasMore`), so pages stay clean for short lists.\n */\n\nimport type { FC } from 'react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { Button } from './Button';\n\ninterface LoadMoreProps {\n hasMore: boolean;\n loadingMore: boolean;\n onLoadMore: () => void;\n loadedCount: number;\n}\n\nexport const LoadMore: FC<LoadMoreProps> = ({ hasMore, loadingMore, onLoadMore, loadedCount }) => {\n const t = useTr();\n\n if (!hasMore) return null;\n\n return (\n <div className=\"flex flex-col items-center gap-2 pt-4\">\n <Button variant=\"outline\" onClick={onLoadMore} isLoading={loadingMore}>\n {t('botlit.common.loadMore', 'Load more')}\n </Button>\n <p className=\"text-xs text-text-secondary tabular-nums\">\n {t('botlit.common.loadedCount', 'Showing {{count}}', { count: loadedCount })}\n </p>\n </div>\n );\n};\n"],"mappings":";;;;AAmBA,IAAaA,KAA+B,EAAE,YAAS,gBAAa,eAAY,qBAAkB;CAChG,IAAMC,IAAIC,EAAM;CAIhB,OAFKC,IAGH,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAACC,GAAD;GAAQ,SAAQ;GAAU,SAASC;GAAY,WAAWC;GACvD,UAAAL,EAAE,0BAA0B,WAAW;EAClC,CAAA,GACR,kBAAC,KAAD;GAAG,WAAU;GACV,UAAAA,EAAE,6BAA6B,qBAAqB,EAAE,OAAOM,EAAY,CAAC;EAC1E,CAAA,CACA;C,KAVc;AAYvB"}
|
|
@@ -12,3 +12,4 @@ export { ErrorState } from './ErrorState';
|
|
|
12
12
|
export { ChannelSelectorSheet } from './ChannelSelectorSheet';
|
|
13
13
|
export { RouteLoader, type RouteLoaderProps } from './RouteLoader';
|
|
14
14
|
export { WorkspaceRequired, type WorkspaceRequiredProps } from './WorkspaceRequired';
|
|
15
|
+
export { LoadMore } from './LoadMore';
|
|
@@ -11,6 +11,21 @@ export interface QueryResult<T> {
|
|
|
11
11
|
error: Error | undefined;
|
|
12
12
|
refetch: () => Promise<void>;
|
|
13
13
|
}
|
|
14
|
+
export interface PaginatedQueryResult<T> extends QueryResult<T> {
|
|
15
|
+
/** True while the next page is being appended. */
|
|
16
|
+
loadingMore: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* True when the most recent page came back full, i.e. another page may
|
|
19
|
+
* exist. False before the first page resolves and once a short page
|
|
20
|
+
* (or an empty one) marks the end of the list.
|
|
21
|
+
*/
|
|
22
|
+
hasMore: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Fetches the next page (current row count as offset) and appends it.
|
|
25
|
+
* No-op while a fetch is in flight or when `hasMore` is false.
|
|
26
|
+
*/
|
|
27
|
+
loadMore: () => Promise<void>;
|
|
28
|
+
}
|
|
14
29
|
export interface MutationState<T> {
|
|
15
30
|
data: T | undefined;
|
|
16
31
|
loading: boolean;
|
|
@@ -36,6 +51,38 @@ export interface WorkspaceQueryOptions<TData, TResult> {
|
|
|
36
51
|
* collection apart from a failed request.
|
|
37
52
|
*/
|
|
38
53
|
export declare function useWorkspaceQuery<TData, TResult>(options: WorkspaceQueryOptions<TData, TResult>): QueryResult<TResult>;
|
|
54
|
+
export interface WorkspacePaginatedQueryOptions<TData, TRow, TResult> {
|
|
55
|
+
/** Generated GraphQL document (see `src/generated/wspace-operations.ts`). */
|
|
56
|
+
document: DocumentNode;
|
|
57
|
+
/**
|
|
58
|
+
* Base variables (filters, ids, …) WITHOUT `pagination` — the hook adds
|
|
59
|
+
* `{ limit: pageSize, offset }` per fetch. Must be referentially stable;
|
|
60
|
+
* a new identity resets the accumulation back to page one.
|
|
61
|
+
*/
|
|
62
|
+
variables: Record<string, unknown> | undefined;
|
|
63
|
+
/** Page size for each fetch. */
|
|
64
|
+
pageSize: number;
|
|
65
|
+
/** Extracts the raw row list from a page payload; missing data yields []. */
|
|
66
|
+
extractRows: (data: TData | undefined) => TRow[] | undefined;
|
|
67
|
+
/** Projects the ACCUMULATED raw rows onto the record shape the UI consumes. */
|
|
68
|
+
select: (rows: TRow[]) => TResult;
|
|
69
|
+
/** Value held while disabled or before the first successful read. */
|
|
70
|
+
fallback: TResult;
|
|
71
|
+
/** When false the request is skipped (e.g. no workspace id yet). */
|
|
72
|
+
enabled: boolean;
|
|
73
|
+
/** Used when the thrown value is not an Error. */
|
|
74
|
+
errorMessage: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Offset-paginated variant of `useWorkspaceQuery` for unbounded lists.
|
|
78
|
+
*
|
|
79
|
+
* `refetch()` restarts at offset 0 and REPLACES the accumulated rows;
|
|
80
|
+
* `loadMore()` fetches the next page and appends it. A page that returns
|
|
81
|
+
* fewer rows than `pageSize` marks the list exhausted (`hasMore` false).
|
|
82
|
+
* `select` runs over the accumulated raw rows, so client-side narrowing
|
|
83
|
+
* (e.g. executions search) sees every loaded page, not just the last one.
|
|
84
|
+
*/
|
|
85
|
+
export declare function useWorkspacePaginatedQuery<TData, TRow, TResult>(options: WorkspacePaginatedQueryOptions<TData, TRow, TResult>): PaginatedQueryResult<TResult>;
|
|
39
86
|
export interface WorkspaceMutationOptions<TData, TInput, TResult> {
|
|
40
87
|
/** Generated GraphQL document (see `src/generated/wspace-operations.ts`). */
|
|
41
88
|
document: DocumentNode;
|
|
@@ -57,6 +57,64 @@ function f(e) {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
function p(e) {
|
|
60
|
+
let { document: t, variables: i, pageSize: s, enabled: l, errorMessage: f } = e, p = c(), m = d(e.select), h = d(e.extractRows), [g, _] = o([]), v = a([]), [y, b] = o(e.fallback), [x, S] = o(!1), [C, w] = o(!1), [T, E] = o(!0), [D, O] = o(), k = n((e) => {
|
|
61
|
+
v.current = e, _(e), b(m.current(e));
|
|
62
|
+
}, [m]), A = n(async (e, n) => {
|
|
63
|
+
n ? w(!0) : S(!0), O(void 0);
|
|
64
|
+
try {
|
|
65
|
+
let r = await p.query({
|
|
66
|
+
query: t,
|
|
67
|
+
variables: {
|
|
68
|
+
...i,
|
|
69
|
+
pagination: {
|
|
70
|
+
limit: s,
|
|
71
|
+
offset: e
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
fetchPolicy: "network-only"
|
|
75
|
+
}), a = r.error;
|
|
76
|
+
if (a != null) throw u(a, f);
|
|
77
|
+
let o = h.current(r.data ?? void 0) ?? [];
|
|
78
|
+
k(n ? [...v.current, ...o] : o), E(o.length < s);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
O(u(e, f));
|
|
81
|
+
} finally {
|
|
82
|
+
n ? w(!1) : S(!1);
|
|
83
|
+
}
|
|
84
|
+
}, [
|
|
85
|
+
p,
|
|
86
|
+
t,
|
|
87
|
+
i,
|
|
88
|
+
s,
|
|
89
|
+
f,
|
|
90
|
+
h,
|
|
91
|
+
k
|
|
92
|
+
]), j = n(async () => {
|
|
93
|
+
l ? await A(0, !1) : (k([]), E(!0), O(void 0));
|
|
94
|
+
}, [
|
|
95
|
+
l,
|
|
96
|
+
A,
|
|
97
|
+
k
|
|
98
|
+
]), M = n(async () => {
|
|
99
|
+
l && !T && await A(v.current.length, !0);
|
|
100
|
+
}, [
|
|
101
|
+
l,
|
|
102
|
+
T,
|
|
103
|
+
A
|
|
104
|
+
]);
|
|
105
|
+
return r(() => {
|
|
106
|
+
j();
|
|
107
|
+
}, [j]), {
|
|
108
|
+
data: y,
|
|
109
|
+
loading: x,
|
|
110
|
+
loadingMore: C,
|
|
111
|
+
hasMore: !T,
|
|
112
|
+
error: D,
|
|
113
|
+
refetch: j,
|
|
114
|
+
loadMore: M
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function m(e) {
|
|
60
118
|
let { document: t, errorMessage: r } = e, i = c(), a = d(e.variables), s = d(e.select), [l, f] = o({
|
|
61
119
|
data: void 0,
|
|
62
120
|
loading: !1,
|
|
@@ -97,6 +155,6 @@ function p(e) {
|
|
|
97
155
|
a
|
|
98
156
|
]), l];
|
|
99
157
|
}
|
|
100
|
-
export { c as useWorkspaceClient, l as useWorkspaceId,
|
|
158
|
+
export { c as useWorkspaceClient, l as useWorkspaceId, m as useWorkspaceMutation, p as useWorkspacePaginatedQuery, f as useWorkspaceQuery };
|
|
101
159
|
|
|
102
160
|
//# sourceMappingURL=workspaceGateway.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaceGateway.js","names":["useWorkspaceClient","useGatewayClient","useWorkspaceId","useBotlit","toError","message","extractGraphqlFailureMessage","cause","fallbackMessage","useLatest","ref","useRef","value","useLayoutEffect","useWorkspaceQuery","options","client","selectRef","fallbackRef","data","setData","useState","loading","setLoading","error","setError","refetch","useCallback","enabled","result","document","operationError","errorMessage","queryError","variables","useEffect","useWorkspaceMutation","variablesRef","state","setState","input","selected","mutationError","failure"],"sources":["../../../../src/botlit/hooks/internal/workspaceGateway.ts"],"sourcesContent":["/**\n * Workspace gateway hook primitives\n * @module botlit/hooks/internal\n *\n * Every BotManor hook talks to the workspace public gateway through the Apollo\n * client the shell installs (`useGatewayClient('workspace')`) — never through a\n * client of its own. These primitives capture the exact shape used by the\n * reference hooks (`useAgents`, `useBots`): `network-only` reads, an explicit\n * `Error` in state rather than a thrown promise, and a `refetch` the caller can\n * run after a mutation.\n */\n\nimport type { ApolloClient, DocumentNode } from '@apollo/client';\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useBotlit } from '../../providers/BotlitProvider';\nimport { extractGraphqlFailureMessage } from '../../utils/errors';\n\nexport type WorkspaceClient = ApolloClient;\n\n/** Apollo client for the workspace gateway, provided by the host shell. */\nexport function useWorkspaceClient(): WorkspaceClient {\n return useGatewayClient('workspace');\n}\n\n/** Current workspace id from the BotManor context (undefined until the shell resolves it). */\nexport function useWorkspaceId(): string | undefined {\n return useBotlit().workspaceId;\n}\n\nexport interface QueryResult<T> {\n data: T;\n loading: boolean;\n /** Set only when the request FAILED. An empty `data` with no `error` means \"no records yet\". */\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\nexport interface MutationState<T> {\n data: T | undefined;\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Normalises any mutation failure to an Error with the server's\n * user-actionable message where one exists.\n *\n * Apollo Client v4 does not reject with a plain Error: failures arrive as\n * `{ networkError }` / `{ graphQLErrors }` objects, and the batched fetch\n * layer attaches the structured GraphQL errors as `.errors` on the\n * networkError. `extractGraphqlFailureMessage` unwraps all of those; only\n * when nothing user-actionable is present do we fall back to the hook's\n * generic `errorMessage`.\n */\nfunction toError(cause: unknown, fallbackMessage: string): Error {\n const message = extractGraphqlFailureMessage(cause);\n if (message) {\n // Preserve the original rejection on the chain so error monitoring can\n // still reach the structured GraphQL errors (extensions.code, requestId…).\n return new Error(message, { cause });\n }\n if (cause instanceof Error) return cause;\n return new Error(fallbackMessage, { cause });\n}\n\n/** Keeps a mutable ref pointing at the latest value without re-running effects. */\nfunction useLatest<T>(value: T): { readonly current: T } {\n const ref = useRef(value);\n useLayoutEffect(() => {\n ref.current = value;\n }, [value]);\n return ref;\n}\n\nexport interface WorkspaceQueryOptions<TData, TResult> {\n /** Generated GraphQL document (see `src/generated/wspace-operations.ts`). */\n document: DocumentNode;\n /** Must be referentially stable — memoize at the call site. */\n variables: Record<string, unknown> | undefined;\n /** Projects the raw payload onto the record shape the UI consumes. */\n select: (data: TData | undefined) => TResult;\n /** Value held while disabled or before the first successful read. */\n fallback: TResult;\n /** When false the request is skipped (e.g. no workspace id / no entity id yet). */\n enabled: boolean;\n /** Used when the thrown value is not an Error. */\n errorMessage: string;\n}\n\n/**\n * Reads from the workspace gateway and keeps `{ data, loading, error, refetch }`\n * in sync. `error` is reserved for real failures so callers can tell an empty\n * collection apart from a failed request.\n */\nexport function useWorkspaceQuery<TData, TResult>(\n options: WorkspaceQueryOptions<TData, TResult>\n): QueryResult<TResult> {\n const { document, variables, enabled, errorMessage } = options;\n const client = useWorkspaceClient();\n const selectRef = useLatest(options.select);\n const fallbackRef = useLatest(options.fallback);\n\n const [data, setData] = useState<TResult>(options.fallback);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n\n const refetch = useCallback(async () => {\n if (!enabled) {\n setData(fallbackRef.current);\n setError(undefined);\n return;\n }\n\n setLoading(true);\n setError(undefined);\n try {\n const result = await client.query<TData>({\n query: document,\n variables,\n fetchPolicy: 'network-only',\n });\n // Apollo v4 resolves (not rejects) with `result.error` set on GraphQL\n // errors — same handling as useWorkspaceMutation.\n const operationError = (result as { error?: unknown }).error;\n if (operationError != null) {\n throw toError(operationError, errorMessage);\n }\n setData(selectRef.current(result.data ?? undefined));\n } catch (queryError) {\n setData(fallbackRef.current);\n setError(toError(queryError, errorMessage));\n } finally {\n setLoading(false);\n }\n // fallbackRef/selectRef are stable refs by construction\n }, [client, document, variables, enabled, errorMessage, fallbackRef, selectRef]);\n\n useEffect(() => {\n void refetch();\n }, [refetch]);\n\n return { data, loading, error, refetch };\n}\n\nexport interface WorkspaceMutationOptions<TData, TInput, TResult> {\n /** Generated GraphQL document (see `src/generated/wspace-operations.ts`). */\n document: DocumentNode;\n /** Maps the caller's input onto GraphQL variables. */\n variables: (input: TInput) => Record<string, unknown>;\n /** Projects the raw payload onto the record shape the UI consumes. */\n select: (data: TData | undefined) => TResult | undefined;\n /** Thrown (and stored) when the payload is missing or the request fails. */\n errorMessage: string;\n}\n\n/**\n * Writes to the workspace gateway. Returns the `[mutate, state]` tuple used by\n * the reference hooks. `mutate` REJECTS on failure so callers cannot mistake a\n * failed write for a successful one.\n */\nexport function useWorkspaceMutation<TData, TInput, TResult>(\n options: WorkspaceMutationOptions<TData, TInput, TResult>\n): readonly [(input: TInput) => Promise<TResult>, MutationState<TResult>] {\n const { document, errorMessage } = options;\n const client = useWorkspaceClient();\n const variablesRef = useLatest(options.variables);\n const selectRef = useLatest(options.select);\n\n const [state, setState] = useState<MutationState<TResult>>({\n data: undefined,\n loading: false,\n error: undefined,\n });\n\n const mutate = useCallback(\n async (input: TInput): Promise<TResult> => {\n setState({ data: undefined, loading: true, error: undefined });\n try {\n const result = await client.mutate<TData>({\n mutation: document,\n variables: variablesRef.current(input),\n });\n // Apollo v4 does not reject on GraphQL errors by default: the mutation\n // RESOLVES with `data` undefined and the failure on `result.error`\n // (CombinedGraphQLErrors / networkError). Surface it like a rejection\n // so callers see the server's field-level message, not the fallback.\n const operationError = (result as { error?: unknown }).error;\n if (operationError != null) {\n throw toError(operationError, errorMessage);\n }\n const selected = selectRef.current(result.data ?? undefined);\n if (selected === undefined) {\n throw new Error(errorMessage);\n }\n setState({ data: selected, loading: false, error: undefined });\n return selected;\n } catch (mutationError) {\n const failure = toError(mutationError, errorMessage);\n setState({ data: undefined, loading: false, error: failure });\n throw failure;\n }\n },\n [client, document, errorMessage, selectRef, variablesRef]\n );\n\n return [mutate, state] as const;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workspaceGateway.js","names":["useWorkspaceClient","useGatewayClient","useWorkspaceId","useBotlit","toError","message","extractGraphqlFailureMessage","cause","fallbackMessage","useLatest","ref","useRef","value","useLayoutEffect","useWorkspaceQuery","options","client","selectRef","fallbackRef","data","setData","useState","loading","setLoading","error","setError","refetch","useCallback","enabled","result","document","operationError","errorMessage","queryError","variables","useEffect","useWorkspacePaginatedQuery","extractRowsRef","rows","setRows","rowsRef","loadingMore","setLoadingMore","exhausted","setExhausted","replaceRows","next","fetchPage","append","pageSize","pageRows","loadMore","useWorkspaceMutation","variablesRef","state","setState","input","selected","mutationError","failure"],"sources":["../../../../src/botlit/hooks/internal/workspaceGateway.ts"],"sourcesContent":["/**\n * Workspace gateway hook primitives\n * @module botlit/hooks/internal\n *\n * Every BotManor hook talks to the workspace public gateway through the Apollo\n * client the shell installs (`useGatewayClient('workspace')`) — never through a\n * client of its own. These primitives capture the exact shape used by the\n * reference hooks (`useAgents`, `useBots`): `network-only` reads, an explicit\n * `Error` in state rather than a thrown promise, and a `refetch` the caller can\n * run after a mutation.\n */\n\nimport type { ApolloClient, DocumentNode } from '@apollo/client';\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useBotlit } from '../../providers/BotlitProvider';\nimport { extractGraphqlFailureMessage } from '../../utils/errors';\n\nexport type WorkspaceClient = ApolloClient;\n\n/** Apollo client for the workspace gateway, provided by the host shell. */\nexport function useWorkspaceClient(): WorkspaceClient {\n return useGatewayClient('workspace');\n}\n\n/** Current workspace id from the BotManor context (undefined until the shell resolves it). */\nexport function useWorkspaceId(): string | undefined {\n return useBotlit().workspaceId;\n}\n\nexport interface QueryResult<T> {\n data: T;\n loading: boolean;\n /** Set only when the request FAILED. An empty `data` with no `error` means \"no records yet\". */\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\nexport interface PaginatedQueryResult<T> extends QueryResult<T> {\n /** True while the next page is being appended. */\n loadingMore: boolean;\n /**\n * True when the most recent page came back full, i.e. another page may\n * exist. False before the first page resolves and once a short page\n * (or an empty one) marks the end of the list.\n */\n hasMore: boolean;\n /**\n * Fetches the next page (current row count as offset) and appends it.\n * No-op while a fetch is in flight or when `hasMore` is false.\n */\n loadMore: () => Promise<void>;\n}\n\nexport interface MutationState<T> {\n data: T | undefined;\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Normalises any mutation failure to an Error with the server's\n * user-actionable message where one exists.\n *\n * Apollo Client v4 does not reject with a plain Error: failures arrive as\n * `{ networkError }` / `{ graphQLErrors }` objects, and the batched fetch\n * layer attaches the structured GraphQL errors as `.errors` on the\n * networkError. `extractGraphqlFailureMessage` unwraps all of those; only\n * when nothing user-actionable is present do we fall back to the hook's\n * generic `errorMessage`.\n */\nfunction toError(cause: unknown, fallbackMessage: string): Error {\n const message = extractGraphqlFailureMessage(cause);\n if (message) {\n // Preserve the original rejection on the chain so error monitoring can\n // still reach the structured GraphQL errors (extensions.code, requestId…).\n return new Error(message, { cause });\n }\n if (cause instanceof Error) return cause;\n return new Error(fallbackMessage, { cause });\n}\n\n/** Keeps a mutable ref pointing at the latest value without re-running effects. */\nfunction useLatest<T>(value: T): { readonly current: T } {\n const ref = useRef(value);\n useLayoutEffect(() => {\n ref.current = value;\n }, [value]);\n return ref;\n}\n\nexport interface WorkspaceQueryOptions<TData, TResult> {\n /** Generated GraphQL document (see `src/generated/wspace-operations.ts`). */\n document: DocumentNode;\n /** Must be referentially stable — memoize at the call site. */\n variables: Record<string, unknown> | undefined;\n /** Projects the raw payload onto the record shape the UI consumes. */\n select: (data: TData | undefined) => TResult;\n /** Value held while disabled or before the first successful read. */\n fallback: TResult;\n /** When false the request is skipped (e.g. no workspace id / no entity id yet). */\n enabled: boolean;\n /** Used when the thrown value is not an Error. */\n errorMessage: string;\n}\n\n/**\n * Reads from the workspace gateway and keeps `{ data, loading, error, refetch }`\n * in sync. `error` is reserved for real failures so callers can tell an empty\n * collection apart from a failed request.\n */\nexport function useWorkspaceQuery<TData, TResult>(\n options: WorkspaceQueryOptions<TData, TResult>\n): QueryResult<TResult> {\n const { document, variables, enabled, errorMessage } = options;\n const client = useWorkspaceClient();\n const selectRef = useLatest(options.select);\n const fallbackRef = useLatest(options.fallback);\n\n const [data, setData] = useState<TResult>(options.fallback);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n\n const refetch = useCallback(async () => {\n if (!enabled) {\n setData(fallbackRef.current);\n setError(undefined);\n return;\n }\n\n setLoading(true);\n setError(undefined);\n try {\n const result = await client.query<TData>({\n query: document,\n variables,\n fetchPolicy: 'network-only',\n });\n // Apollo v4 resolves (not rejects) with `result.error` set on GraphQL\n // errors — same handling as useWorkspaceMutation.\n const operationError = (result as { error?: unknown }).error;\n if (operationError != null) {\n throw toError(operationError, errorMessage);\n }\n setData(selectRef.current(result.data ?? undefined));\n } catch (queryError) {\n setData(fallbackRef.current);\n setError(toError(queryError, errorMessage));\n } finally {\n setLoading(false);\n }\n // fallbackRef/selectRef are stable refs by construction\n }, [client, document, variables, enabled, errorMessage, fallbackRef, selectRef]);\n\n useEffect(() => {\n void refetch();\n }, [refetch]);\n\n return { data, loading, error, refetch };\n}\n\nexport interface WorkspacePaginatedQueryOptions<TData, TRow, TResult> {\n /** Generated GraphQL document (see `src/generated/wspace-operations.ts`). */\n document: DocumentNode;\n /**\n * Base variables (filters, ids, …) WITHOUT `pagination` — the hook adds\n * `{ limit: pageSize, offset }` per fetch. Must be referentially stable;\n * a new identity resets the accumulation back to page one.\n */\n variables: Record<string, unknown> | undefined;\n /** Page size for each fetch. */\n pageSize: number;\n /** Extracts the raw row list from a page payload; missing data yields []. */\n extractRows: (data: TData | undefined) => TRow[] | undefined;\n /** Projects the ACCUMULATED raw rows onto the record shape the UI consumes. */\n select: (rows: TRow[]) => TResult;\n /** Value held while disabled or before the first successful read. */\n fallback: TResult;\n /** When false the request is skipped (e.g. no workspace id yet). */\n enabled: boolean;\n /** Used when the thrown value is not an Error. */\n errorMessage: string;\n}\n\n/**\n * Offset-paginated variant of `useWorkspaceQuery` for unbounded lists.\n *\n * `refetch()` restarts at offset 0 and REPLACES the accumulated rows;\n * `loadMore()` fetches the next page and appends it. A page that returns\n * fewer rows than `pageSize` marks the list exhausted (`hasMore` false).\n * `select` runs over the accumulated raw rows, so client-side narrowing\n * (e.g. executions search) sees every loaded page, not just the last one.\n */\nexport function useWorkspacePaginatedQuery<TData, TRow, TResult>(\n options: WorkspacePaginatedQueryOptions<TData, TRow, TResult>\n): PaginatedQueryResult<TResult> {\n const { document, variables, pageSize, enabled, errorMessage } = options;\n const client = useWorkspaceClient();\n const selectRef = useLatest(options.select);\n const extractRowsRef = useLatest(options.extractRows);\n\n const [rows, setRows] = useState<TRow[]>([]);\n const rowsRef = useRef<TRow[]>([]);\n const [data, setData] = useState<TResult>(options.fallback);\n const [loading, setLoading] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const [exhausted, setExhausted] = useState(true);\n const [error, setError] = useState<Error | undefined>();\n\n const replaceRows = useCallback(\n (next: TRow[]) => {\n rowsRef.current = next;\n setRows(next);\n setData(selectRef.current(next));\n },\n [selectRef]\n );\n\n const fetchPage = useCallback(\n async (offset: number, append: boolean) => {\n if (append) setLoadingMore(true);\n else setLoading(true);\n setError(undefined);\n try {\n const result = await client.query<TData>({\n query: document,\n variables: { ...variables, pagination: { limit: pageSize, offset } },\n fetchPolicy: 'network-only',\n });\n const operationError = (result as { error?: unknown }).error;\n if (operationError != null) {\n throw toError(operationError, errorMessage);\n }\n const pageRows = extractRowsRef.current(result.data ?? undefined) ?? [];\n replaceRows(append ? [...rowsRef.current, ...pageRows] : pageRows);\n setExhausted(pageRows.length < pageSize);\n } catch (queryError) {\n setError(toError(queryError, errorMessage));\n } finally {\n if (append) setLoadingMore(false);\n else setLoading(false);\n }\n },\n [client, document, variables, pageSize, errorMessage, extractRowsRef, replaceRows]\n );\n\n const refetch = useCallback(async () => {\n if (!enabled) {\n replaceRows([]);\n setExhausted(true);\n setError(undefined);\n return;\n }\n await fetchPage(0, false);\n }, [enabled, fetchPage, replaceRows]);\n\n const loadMore = useCallback(async () => {\n if (!enabled || exhausted) return;\n await fetchPage(rowsRef.current.length, true);\n }, [enabled, exhausted, fetchPage]);\n\n useEffect(() => {\n void refetch();\n }, [refetch]);\n\n return { data, loading, loadingMore, hasMore: !exhausted, error, refetch, loadMore };\n}\n\nexport interface WorkspaceMutationOptions<TData, TInput, TResult> {\n /** Generated GraphQL document (see `src/generated/wspace-operations.ts`). */\n document: DocumentNode;\n /** Maps the caller's input onto GraphQL variables. */\n variables: (input: TInput) => Record<string, unknown>;\n /** Projects the raw payload onto the record shape the UI consumes. */\n select: (data: TData | undefined) => TResult | undefined;\n /** Thrown (and stored) when the payload is missing or the request fails. */\n errorMessage: string;\n}\n\n/**\n * Writes to the workspace gateway. Returns the `[mutate, state]` tuple used by\n * the reference hooks. `mutate` REJECTS on failure so callers cannot mistake a\n * failed write for a successful one.\n */\nexport function useWorkspaceMutation<TData, TInput, TResult>(\n options: WorkspaceMutationOptions<TData, TInput, TResult>\n): readonly [(input: TInput) => Promise<TResult>, MutationState<TResult>] {\n const { document, errorMessage } = options;\n const client = useWorkspaceClient();\n const variablesRef = useLatest(options.variables);\n const selectRef = useLatest(options.select);\n\n const [state, setState] = useState<MutationState<TResult>>({\n data: undefined,\n loading: false,\n error: undefined,\n });\n\n const mutate = useCallback(\n async (input: TInput): Promise<TResult> => {\n setState({ data: undefined, loading: true, error: undefined });\n try {\n const result = await client.mutate<TData>({\n mutation: document,\n variables: variablesRef.current(input),\n });\n // Apollo v4 does not reject on GraphQL errors by default: the mutation\n // RESOLVES with `data` undefined and the failure on `result.error`\n // (CombinedGraphQLErrors / networkError). Surface it like a rejection\n // so callers see the server's field-level message, not the fallback.\n const operationError = (result as { error?: unknown }).error;\n if (operationError != null) {\n throw toError(operationError, errorMessage);\n }\n const selected = selectRef.current(result.data ?? undefined);\n if (selected === undefined) {\n throw new Error(errorMessage);\n }\n setState({ data: selected, loading: false, error: undefined });\n return selected;\n } catch (mutationError) {\n const failure = toError(mutationError, errorMessage);\n setState({ data: undefined, loading: false, error: failure });\n throw failure;\n }\n },\n [client, document, errorMessage, selectRef, variablesRef]\n );\n\n return [mutate, state] as const;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { UpdateBotmanorAgentInput } from '../../generated/wspace-types';
|
|
2
2
|
import { BaseFilter } from '../types';
|
|
3
3
|
import { GraphQLInput } from './internal/mutationInput';
|
|
4
|
+
import { PaginatedQueryResult } from './internal/workspaceGateway';
|
|
4
5
|
export interface AgentRecord {
|
|
5
6
|
id: string;
|
|
6
7
|
name: string;
|
|
@@ -72,12 +73,7 @@ export declare const agentKeys: {
|
|
|
72
73
|
details: () => readonly ["agents", "detail"];
|
|
73
74
|
detail: (id: string) => readonly ["agents", "detail", string];
|
|
74
75
|
};
|
|
75
|
-
export declare function useAgents(filter?: BaseFilter):
|
|
76
|
-
data: AgentRecord[];
|
|
77
|
-
loading: boolean;
|
|
78
|
-
error: Error | undefined;
|
|
79
|
-
refetch: () => Promise<void>;
|
|
80
|
-
};
|
|
76
|
+
export declare function useAgents(filter?: BaseFilter): PaginatedQueryResult<AgentRecord[]>;
|
|
81
77
|
export declare function useAgent(agentId: string | undefined): {
|
|
82
78
|
data: AgentRecord | null;
|
|
83
79
|
loading: boolean;
|