@ekanos/sdk 0.7.1 → 0.9.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/README.md +37 -6
- package/api-report.md +25 -2
- package/dist/components/base-activation-dialog.js +2 -2
- package/dist/components/base-activation-dialog.js.map +1 -1
- package/dist/components/base-activation-form.js +2 -2
- package/dist/components/base-activation-form.js.map +1 -1
- package/dist/components/base-marketplace-tile.js +4 -2
- package/dist/components/base-marketplace-tile.js.map +1 -1
- package/dist/components/widgets/ai-prompt-chip.js +4 -2
- package/dist/components/widgets/ai-prompt-chip.js.map +1 -1
- package/dist/components/widgets/widget-ask-bar.js +3 -1
- package/dist/components/widgets/widget-ask-bar.js.map +1 -1
- package/dist/components/widgets/widget-state-components.js +4 -4
- package/dist/components/widgets/widget-state-components.js.map +1 -1
- package/dist/components/widgets/widget.d.ts +1 -6
- package/dist/components/widgets/widget.js +8 -15
- package/dist/components/widgets/widget.js.map +1 -1
- package/dist/hooks/partner-oauth.js +4 -1
- package/dist/hooks/partner-oauth.js.map +1 -1
- package/dist/integration/define-agent-event.d.ts +18 -0
- package/dist/integration/define-agent-event.js +20 -0
- package/dist/integration/define-agent-event.js.map +1 -0
- package/dist/integration/event-surfaces.d.ts +5 -2
- package/dist/integration/event-surfaces.js +36 -9
- package/dist/integration/event-surfaces.js.map +1 -1
- package/dist/integration/index.d.ts +5 -2
- package/dist/integration/index.js +7 -1
- package/dist/integration/index.js.map +1 -1
- package/dist/types/integration.d.ts +3 -3
- package/dist/types/integration.js.map +1 -1
- package/dist/types/workspace-target.d.ts +6 -0
- package/dist/types/workspace-target.js.map +1 -1
- package/eslint.design-preset.mjs +7 -5
- package/eslint.design-rules.mjs +60 -3
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget-state-components.js","sourceRoot":"","sources":["../../../src/components/widgets/widget-state-components.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react';\n\nimport Link from 'next/link.js';\n\nimport { Button } from '@ekanos/ui/button';\nimport { Icon } from '@ekanos/ui/icon';\nimport { Skeleton } from '@ekanos/ui/skeleton';\nimport { Trans } from '@ekanos/ui/trans';\nimport { cn } from '@ekanos/ui/utils';\n\n/**\n * The one action a state view offers: \"Connect QuickBooks\", \"Ask an admin\",\n * \"Create a deal\". A link with `href`, otherwise a button running `onClick`.\n */\nexport interface WidgetStateAction {\n label: ReactNode;\n href?: string;\n /** Open `href` in a new tab. */\n external?: boolean;\n onClick?: () => void;\n}\n\n/** The body shape a loading skeleton stands in for. */\nexport type WidgetSkeletonVariant = 'rows' | 'stat' | 'chart' | 'schedule';\n\n/** Why an active integration still can't show this widget's data. */\nexport type WidgetInactiveReason =\n | 'not-connected'\n | 'no-access'\n | 'setup-needed';\n\nexport interface WidgetEmptyStateProps {\n /**\n * `positive` (the default) says nothing needs the owner: \"You're all caught\n * up\". `neutral` is for an empty list that isn't good news, like a search\n * with no results.\n */\n tone?: 'positive' | 'neutral';\n title?: ReactNode;\n description?: ReactNode;\n action?: WidgetStateAction;\n}\n\nexport interface WidgetInactiveStateProps {\n reason?: WidgetInactiveReason;\n /** The integration's name, for the default copy (\"Connect QuickBooks\"). */\n integrationName?: string;\n title?: ReactNode;\n /** One sentence on what connecting or access gives the owner. */\n description?: ReactNode;\n action?: WidgetStateAction;\n}\n\nfunction StateAction({\n action,\n variant = 'default',\n}: {\n action: WidgetStateAction;\n variant?: 'default' | 'outline';\n}) {\n if (action.href) {\n const link = action.external ? (\n <a href={action.href} target=\"_blank\" rel=\"noopener noreferrer\" />\n ) : (\n <Link href={action.href} />\n );\n\n return (\n <Button size=\"sm\" variant={variant} render={link} nativeButton={false}>\n {action.label}\n </Button>\n );\n }\n\n return (\n <Button size=\"sm\" variant={variant} type=\"button\" onClick={action.onClick}>\n {action.label}\n </Button>\n );\n}\n\n/**\n * The shared shape of every non-data state: an icon, a title, one sentence\n * and at most one action, left-aligned in the body's padding.\n */\nfunction StateNote({\n icon,\n title,\n description,\n action,\n actionVariant,\n role,\n testId,\n}: {\n icon: string;\n title: ReactNode;\n description?: ReactNode;\n action?: WidgetStateAction;\n actionVariant?: 'default' | 'outline';\n role?: 'alert' | 'status';\n testId: string;\n}) {\n return (\n <div\n role={role}\n className=\"flex flex-1 flex-col items-start gap-3 px-5 pt-2 pb-5\"\n data-test={testId}\n >\n <Icon name={icon} className=\"text-muted-foreground h-6 w-6\" aria-hidden />\n <div className=\"space-y-1\">\n <p className=\"text-foreground text-base font-medium\">{title}</p>\n {description ? (\n <p className=\"text-muted-foreground max-w-prose text-sm\">\n {description}\n </p>\n ) : null}\n </div>\n {action ? <StateAction action={action} variant={actionVariant} /> : null}\n </div>\n );\n}\n\n/**\n * The empty state for a widget with nothing to show. Positive by default:\n * an empty review queue is good news, so it reassures instead of apologizing.\n */\nexport function WidgetEmptyState({\n tone = 'positive',\n title,\n description,\n action,\n}: WidgetEmptyStateProps) {\n const positive = tone === 'positive';\n\n return (\n <StateNote\n icon={positive ? 'fa-light fa-circle-check' : 'fa-light fa-inbox'}\n title={\n title ??\n (positive ? (\n <Trans\n i18nKey=\"dashboard:widgetEmptyPositive\"\n defaults=\"You're all caught up\"\n />\n ) : (\n <Trans\n i18nKey=\"dashboard:widgetEmptyNeutral\"\n defaults=\"Nothing here yet\"\n />\n ))\n }\n description={description}\n action={action}\n actionVariant=\"outline\"\n role=\"status\"\n testId={`widget-empty-${tone}`}\n />\n );\n}\n\nconst INACTIVE_ICON: Record<WidgetInactiveReason, string> = {\n 'not-connected': 'fa-light fa-plug',\n 'no-access': 'fa-light fa-user-group',\n 'setup-needed': 'fa-light fa-sliders',\n};\n\n/**\n * Why the widget can't show data. `not-connected` offers one Connect button;\n * `no-access` is a calm note that points to an admin, never a lock screen;\n * `setup-needed` points to the integration's settings. Without a `reason` it\n * keeps the original one-line \"Widget inactive\".\n */\nexport function WidgetInactiveState({\n reason,\n integrationName,\n title,\n description,\n action,\n}: WidgetInactiveStateProps = {}) {\n if (!reason) {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-muted-foreground text-sm\">\n {title ?? (\n <Trans\n i18nKey=\"dashboard:widgetInactive\"\n defaults=\"Widget inactive\"\n />\n )}\n </p>\n </div>\n );\n }\n\n const name = integrationName ?? 'this integration';\n\n const defaults: Record<\n WidgetInactiveReason,\n { title: ReactNode; description: ReactNode }\n > = {\n 'not-connected': {\n title: (\n <Trans\n i18nKey=\"dashboard:widgetNotConnectedTitle\"\n defaults=\"Connect {{name}}\"\n values={{ name }}\n />\n ),\n description: (\n <Trans\n i18nKey=\"dashboard:widgetNotConnectedBody\"\n defaults=\"Connect {{name}} and this card fills in with your own data.\"\n values={{ name }}\n />\n ),\n },\n 'no-access': {\n title: (\n <Trans\n i18nKey=\"dashboard:widgetNoAccessTitle\"\n defaults=\"Shared with admins for now\"\n />\n ),\n description: (\n <Trans\n i18nKey=\"dashboard:widgetNoAccessBody\"\n defaults=\"Your role doesn't include this data yet. Ask an admin if you need it.\"\n />\n ),\n },\n 'setup-needed': {\n title: (\n <Trans\n i18nKey=\"dashboard:widgetSetupNeededTitle\"\n defaults=\"Finish setting up {{name}}\"\n values={{ name }}\n />\n ),\n description: (\n <Trans\n i18nKey=\"dashboard:widgetSetupNeededBody\"\n defaults=\"One more step and this card starts showing data.\"\n />\n ),\n },\n };\n\n return (\n <StateNote\n icon={INACTIVE_ICON[reason]}\n title={title ?? defaults[reason].title}\n description={description ?? defaults[reason].description}\n action={action}\n actionVariant={reason === 'no-access' ? 'outline' : 'default'}\n testId={`widget-inactive-${reason}`}\n />\n );\n}\n\n/**\n * Loading, drawn as the body it stands in for so the card doesn't jump when\n * data lands. Rows suit lists and feeds, `stat` a headline figure, `chart` a\n * chart, `schedule` a day of events.\n */\nexport function WidgetLoadingSkeleton({\n variant = 'rows',\n}: {\n variant?: WidgetSkeletonVariant;\n}) {\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n className=\"flex flex-1 flex-col\"\n data-test={`widget-skeleton-${variant}`}\n >\n <span className=\"sr-only\">\n <Trans i18nKey=\"dashboard:widgetLoading\" defaults=\"Loading...\" />\n </span>\n {variant === 'stat' ? (\n <div className=\"space-y-2 px-5 pt-2 pb-5\">\n <Skeleton className=\"h-3 w-24\" />\n <Skeleton className=\"h-8 w-40\" />\n <Skeleton className=\"h-3 w-32\" />\n </div>\n ) : variant === 'chart' ? (\n <div className=\"space-y-3 px-5 pt-2 pb-5\">\n <Skeleton className=\"h-8 w-40\" />\n <Skeleton className=\"h-60 w-full\" />\n </div>\n ) : (\n <div className=\"divide-border/50 divide-y\">\n {[0, 1, 2].map((row) => (\n <div\n key={row}\n className={cn(\n 'px-5 py-3',\n variant === 'schedule' ? 'flex items-start gap-3' : 'space-y-2',\n )}\n >\n {variant === 'schedule' ? (\n <>\n <Skeleton className=\"h-4 w-14 shrink-0\" />\n <div className=\"flex-1 space-y-2\">\n <Skeleton className=\"h-4 w-2/3\" />\n <Skeleton className=\"h-3 w-1/3\" />\n </div>\n </>\n ) : (\n <>\n <Skeleton className=\"h-4 w-2/3\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport function WidgetErrorState({\n title,\n description,\n onRetry,\n retrying = false,\n}: {\n title?: ReactNode;\n description?: ReactNode;\n onRetry?: () => void;\n retrying?: boolean;\n}) {\n return (\n <div\n role=\"alert\"\n className=\"flex flex-col items-center justify-center gap-3 px-5 py-8 text-center\"\n >\n <div className=\"bg-destructive/10 flex h-10 w-10 items-center justify-center rounded-full\">\n <Icon\n name=\"fa-light fa-circle-exclamation\"\n className=\"text-destructive h-5 w-5\"\n aria-hidden\n />\n </div>\n <div className=\"space-y-1\">\n <p className=\"text-foreground text-sm font-medium\">\n {title ?? (\n <Trans\n i18nKey=\"dashboard:widgetErrorTitle\"\n defaults=\"Couldn't load this widget\"\n />\n )}\n </p>\n <p className=\"text-muted-foreground text-sm\">\n {description ? (\n description\n ) : onRetry ? (\n <Trans\n i18nKey=\"dashboard:widgetErrorBodyRetry\"\n defaults=\"Try again, or check back in a few minutes.\"\n />\n ) : (\n <Trans\n i18nKey=\"dashboard:widgetErrorBody\"\n defaults=\"Check back in a few minutes.\"\n />\n )}\n </p>\n </div>\n {onRetry ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={onRetry}\n disabled={retrying}\n data-test=\"widget-error-retry\"\n >\n <Icon\n name=\"fa-light fa-arrows-rotate\"\n className={cn('h-4 w-4', retrying && 'animate-spin')}\n aria-hidden\n />\n <Trans i18nKey=\"dashboard:widgetRetry\" defaults=\"Try again\" />\n </Button>\n ) : null}\n </div>\n );\n}\n\nexport function WidgetDataLoading() {\n return (\n <div className=\"flex flex-1 items-center justify-center py-12\">\n <Icon\n name=\"fa-light fa-spinner-third\"\n className=\"text-muted-foreground h-8 w-8 animate-spin\"\n />\n </div>\n );\n}\n\nexport function WidgetDisabledState() {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <Icon\n name=\"fa-light fa-ban\"\n className=\"text-muted-foreground mb-4 h-8 w-8\"\n />\n <p className=\"text-muted-foreground text-sm\">\n <Trans i18nKey=\"dashboard:widgetDisabled\" defaults=\"Widget Disabled\" />\n </p>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"widget-state-components.js","sourceRoot":"","sources":["../../../src/components/widgets/widget-state-components.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react';\n\nimport Link from 'next/link.js';\n\nimport { Button } from '@ekanos/ui/button';\nimport { Icon } from '@ekanos/ui/icon';\nimport { Skeleton } from '@ekanos/ui/skeleton';\nimport { Trans } from '@ekanos/ui/trans';\nimport { cn } from '@ekanos/ui/utils';\n\n/**\n * The one action a state view offers: \"Connect QuickBooks\", \"Ask an admin\",\n * \"Create a deal\". A link with `href`, otherwise a button running `onClick`.\n */\nexport interface WidgetStateAction {\n label: ReactNode;\n href?: string;\n /** Open `href` in a new tab. */\n external?: boolean;\n onClick?: () => void;\n}\n\n/** The body shape a loading skeleton stands in for. */\nexport type WidgetSkeletonVariant = 'rows' | 'stat' | 'chart' | 'schedule';\n\n/** Why an active integration still can't show this widget's data. */\nexport type WidgetInactiveReason =\n | 'not-connected'\n | 'no-access'\n | 'setup-needed';\n\nexport interface WidgetEmptyStateProps {\n /**\n * `positive` (the default) says nothing needs the owner: \"You're all caught\n * up\". `neutral` is for an empty list that isn't good news, like a search\n * with no results.\n */\n tone?: 'positive' | 'neutral';\n title?: ReactNode;\n description?: ReactNode;\n action?: WidgetStateAction;\n}\n\nexport interface WidgetInactiveStateProps {\n reason?: WidgetInactiveReason;\n /** The integration's name, for the default copy (\"Connect QuickBooks\"). */\n integrationName?: string;\n title?: ReactNode;\n /** One sentence on what connecting or access gives the owner. */\n description?: ReactNode;\n action?: WidgetStateAction;\n}\n\nfunction StateAction({\n action,\n variant = 'default',\n}: {\n action: WidgetStateAction;\n variant?: 'default' | 'outline';\n}) {\n if (action.href) {\n const link = action.external ? (\n <a href={action.href} target=\"_blank\" rel=\"noopener noreferrer\" />\n ) : (\n <Link href={action.href} />\n );\n\n return (\n <Button size=\"sm\" variant={variant} render={link} nativeButton={false}>\n {action.label}\n </Button>\n );\n }\n\n return (\n <Button size=\"sm\" variant={variant} type=\"button\" onClick={action.onClick}>\n {action.label}\n </Button>\n );\n}\n\n/**\n * The shared shape of every non-data state: an icon, a title, one sentence\n * and at most one action, left-aligned in the body's padding.\n */\nfunction StateNote({\n icon,\n title,\n description,\n action,\n actionVariant,\n role,\n testId,\n}: {\n icon: string;\n title: ReactNode;\n description?: ReactNode;\n action?: WidgetStateAction;\n actionVariant?: 'default' | 'outline';\n role?: 'alert' | 'status';\n testId: string;\n}) {\n return (\n <div\n role={role}\n className=\"flex flex-1 flex-col items-start gap-3 px-5 pt-2 pb-5\"\n data-test={testId}\n >\n <Icon name={icon} className=\"text-muted-foreground h-6 w-6\" aria-hidden />\n <div className=\"space-y-1\">\n {/* oxlint-disable-next-line design/no-raw-text-size -- state-note title at 16px/500 (text-base font-medium); no tier pairs 500 at 16px (text-title bakes 600, text-body bakes 400) */}\n <p className=\"text-foreground text-base font-medium\">{title}</p>\n {description ? (\n <p className=\"text-muted-foreground text-meta max-w-prose\">\n {description}\n </p>\n ) : null}\n </div>\n {action ? <StateAction action={action} variant={actionVariant} /> : null}\n </div>\n );\n}\n\n/**\n * The empty state for a widget with nothing to show. Positive by default:\n * an empty review queue is good news, so it reassures instead of apologizing.\n */\nexport function WidgetEmptyState({\n tone = 'positive',\n title,\n description,\n action,\n}: WidgetEmptyStateProps) {\n const positive = tone === 'positive';\n\n return (\n <StateNote\n icon={positive ? 'fa-light fa-circle-check' : 'fa-light fa-inbox'}\n title={\n title ??\n (positive ? (\n <Trans\n i18nKey=\"dashboard:widgetEmptyPositive\"\n defaults=\"You're all caught up\"\n />\n ) : (\n <Trans\n i18nKey=\"dashboard:widgetEmptyNeutral\"\n defaults=\"Nothing here yet\"\n />\n ))\n }\n description={description}\n action={action}\n actionVariant=\"outline\"\n role=\"status\"\n testId={`widget-empty-${tone}`}\n />\n );\n}\n\nconst INACTIVE_ICON: Record<WidgetInactiveReason, string> = {\n 'not-connected': 'fa-light fa-plug',\n 'no-access': 'fa-light fa-user-group',\n 'setup-needed': 'fa-light fa-sliders',\n};\n\n/**\n * Why the widget can't show data. `not-connected` offers one Connect button;\n * `no-access` is a calm note that points to an admin, never a lock screen;\n * `setup-needed` points to the integration's settings. Without a `reason` it\n * keeps the original one-line \"Widget inactive\".\n */\nexport function WidgetInactiveState({\n reason,\n integrationName,\n title,\n description,\n action,\n}: WidgetInactiveStateProps = {}) {\n if (!reason) {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-muted-foreground text-meta\">\n {title ?? (\n <Trans\n i18nKey=\"dashboard:widgetInactive\"\n defaults=\"Widget inactive\"\n />\n )}\n </p>\n </div>\n );\n }\n\n const name = integrationName ?? 'this integration';\n\n const defaults: Record<\n WidgetInactiveReason,\n { title: ReactNode; description: ReactNode }\n > = {\n 'not-connected': {\n title: (\n <Trans\n i18nKey=\"dashboard:widgetNotConnectedTitle\"\n defaults=\"Connect {{name}}\"\n values={{ name }}\n />\n ),\n description: (\n <Trans\n i18nKey=\"dashboard:widgetNotConnectedBody\"\n defaults=\"Connect {{name}} and this card fills in with your own data.\"\n values={{ name }}\n />\n ),\n },\n 'no-access': {\n title: (\n <Trans\n i18nKey=\"dashboard:widgetNoAccessTitle\"\n defaults=\"Shared with admins for now\"\n />\n ),\n description: (\n <Trans\n i18nKey=\"dashboard:widgetNoAccessBody\"\n defaults=\"Your role doesn't include this data yet. Ask an admin if you need it.\"\n />\n ),\n },\n 'setup-needed': {\n title: (\n <Trans\n i18nKey=\"dashboard:widgetSetupNeededTitle\"\n defaults=\"Finish setting up {{name}}\"\n values={{ name }}\n />\n ),\n description: (\n <Trans\n i18nKey=\"dashboard:widgetSetupNeededBody\"\n defaults=\"One more step and this card starts showing data.\"\n />\n ),\n },\n };\n\n return (\n <StateNote\n icon={INACTIVE_ICON[reason]}\n title={title ?? defaults[reason].title}\n description={description ?? defaults[reason].description}\n action={action}\n actionVariant={reason === 'no-access' ? 'outline' : 'default'}\n testId={`widget-inactive-${reason}`}\n />\n );\n}\n\n/**\n * Loading, drawn as the body it stands in for so the card doesn't jump when\n * data lands. Rows suit lists and feeds, `stat` a headline figure, `chart` a\n * chart, `schedule` a day of events.\n */\nexport function WidgetLoadingSkeleton({\n variant = 'rows',\n}: {\n variant?: WidgetSkeletonVariant;\n}) {\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n className=\"flex flex-1 flex-col\"\n data-test={`widget-skeleton-${variant}`}\n >\n <span className=\"sr-only\">\n <Trans i18nKey=\"dashboard:widgetLoading\" defaults=\"Loading...\" />\n </span>\n {variant === 'stat' ? (\n <div className=\"space-y-2 px-5 pt-2 pb-5\">\n <Skeleton className=\"h-3 w-24\" />\n <Skeleton className=\"h-8 w-40\" />\n <Skeleton className=\"h-3 w-32\" />\n </div>\n ) : variant === 'chart' ? (\n <div className=\"space-y-3 px-5 pt-2 pb-5\">\n <Skeleton className=\"h-8 w-40\" />\n <Skeleton className=\"h-60 w-full\" />\n </div>\n ) : (\n <div className=\"divide-border/50 divide-y\">\n {[0, 1, 2].map((row) => (\n <div\n key={row}\n className={cn(\n 'px-5 py-3',\n variant === 'schedule' ? 'flex items-start gap-3' : 'space-y-2',\n )}\n >\n {variant === 'schedule' ? (\n <>\n <Skeleton className=\"h-4 w-14 shrink-0\" />\n <div className=\"flex-1 space-y-2\">\n <Skeleton className=\"h-4 w-2/3\" />\n <Skeleton className=\"h-3 w-1/3\" />\n </div>\n </>\n ) : (\n <>\n <Skeleton className=\"h-4 w-2/3\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport function WidgetErrorState({\n title,\n description,\n onRetry,\n retrying = false,\n}: {\n title?: ReactNode;\n description?: ReactNode;\n onRetry?: () => void;\n retrying?: boolean;\n}) {\n return (\n <div\n role=\"alert\"\n className=\"flex flex-col items-center justify-center gap-3 px-5 py-8 text-center\"\n >\n <div className=\"bg-destructive/10 flex h-10 w-10 items-center justify-center rounded-full\">\n <Icon\n name=\"fa-light fa-circle-exclamation\"\n className=\"text-destructive h-5 w-5\"\n aria-hidden\n />\n </div>\n <div className=\"space-y-1\">\n <p className=\"text-foreground text-label\">\n {title ?? (\n <Trans\n i18nKey=\"dashboard:widgetErrorTitle\"\n defaults=\"Couldn't load this widget\"\n />\n )}\n </p>\n <p className=\"text-muted-foreground text-meta\">\n {description ? (\n description\n ) : onRetry ? (\n <Trans\n i18nKey=\"dashboard:widgetErrorBodyRetry\"\n defaults=\"Try again, or check back in a few minutes.\"\n />\n ) : (\n <Trans\n i18nKey=\"dashboard:widgetErrorBody\"\n defaults=\"Check back in a few minutes.\"\n />\n )}\n </p>\n </div>\n {onRetry ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={onRetry}\n disabled={retrying}\n data-test=\"widget-error-retry\"\n >\n <Icon\n name=\"fa-light fa-arrows-rotate\"\n className={cn('h-4 w-4', retrying && 'animate-spin')}\n aria-hidden\n />\n <Trans i18nKey=\"dashboard:widgetRetry\" defaults=\"Try again\" />\n </Button>\n ) : null}\n </div>\n );\n}\n\nexport function WidgetDataLoading() {\n return (\n <div className=\"flex flex-1 items-center justify-center py-12\">\n <Icon\n name=\"fa-light fa-spinner-third\"\n className=\"text-muted-foreground h-8 w-8 animate-spin\"\n />\n </div>\n );\n}\n\nexport function WidgetDisabledState() {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <Icon\n name=\"fa-light fa-ban\"\n className=\"text-muted-foreground mb-4 h-8 w-8\"\n />\n <p className=\"text-muted-foreground text-meta\">\n <Trans i18nKey=\"dashboard:widgetDisabled\" defaults=\"Widget Disabled\" />\n </p>\n </div>\n );\n}\n"]}
|
|
@@ -33,7 +33,7 @@ export type WidgetHeaderTrailing = {
|
|
|
33
33
|
/** Accessible name; defaults to "Period". */
|
|
34
34
|
label?: string;
|
|
35
35
|
};
|
|
36
|
-
declare function WidgetHeader({ title, subtitle, description, context, trailing,
|
|
36
|
+
declare function WidgetHeader({ title, subtitle, description, context, trailing, menu, children, }: {
|
|
37
37
|
/** Override the provider's title — useful when the rendered name differs from the config name. */
|
|
38
38
|
title?: string;
|
|
39
39
|
/**
|
|
@@ -51,11 +51,6 @@ declare function WidgetHeader({ title, subtitle, description, context, trailing,
|
|
|
51
51
|
context?: ReactNode;
|
|
52
52
|
/** At most one control on the right: a "View all ›" link or a period switch. */
|
|
53
53
|
trailing?: WidgetHeaderTrailing;
|
|
54
|
-
/**
|
|
55
|
-
* Draw the integration's logo left of the title (the default) when the
|
|
56
|
-
* host supplies one. Pass `false` only for a card that mixes sources.
|
|
57
|
-
*/
|
|
58
|
-
sourceMark?: boolean;
|
|
59
54
|
/**
|
|
60
55
|
* `Widget.MenuItem`s and `Widget.MenuChoice`s placed at the top of the
|
|
61
56
|
* header's ⋯ menu: occasional actions ("Open in QuickBooks") and secondary
|
|
@@ -46,7 +46,7 @@ class WidgetErrorBoundary extends Component {
|
|
|
46
46
|
}
|
|
47
47
|
render() {
|
|
48
48
|
if (this.state.hasError) {
|
|
49
|
-
return (_jsxs("div", { role: "alert", className: "flex flex-1 flex-col items-center justify-center gap-3 px-5 py-8 text-center", children: [_jsx("div", { className: "bg-destructive/10 flex h-10 w-10 items-center justify-center rounded-full", children: _jsx(Icon, { name: "fa-light fa-circle-exclamation", className: "text-destructive h-5 w-5", "aria-hidden": true }) }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-foreground text-
|
|
49
|
+
return (_jsxs("div", { role: "alert", className: "flex flex-1 flex-col items-center justify-center gap-3 px-5 py-8 text-center", children: [_jsx("div", { className: "bg-destructive/10 flex h-10 w-10 items-center justify-center rounded-full", children: _jsx(Icon, { name: "fa-light fa-circle-exclamation", className: "text-destructive h-5 w-5", "aria-hidden": true }) }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-foreground text-label", children: _jsx(Trans, { i18nKey: "dashboard:widgetCrashedTitle", defaults: "This widget ran into a problem" }) }), _jsx("p", { className: "text-muted-foreground text-meta max-w-xs", children: _jsx(Trans, { i18nKey: "dashboard:widgetCrashedBody", defaults: "Try again. If it keeps happening, reload the page." }) })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: this.retry, "data-test": "widget-crash-retry", children: [_jsx(Icon, { name: "fa-light fa-arrows-rotate", className: "h-4 w-4", "aria-hidden": true }), _jsx(Trans, { i18nKey: "dashboard:widgetRetry", defaults: "Try again" })] })] }));
|
|
50
50
|
}
|
|
51
51
|
return this.props.children;
|
|
52
52
|
}
|
|
@@ -111,20 +111,13 @@ const VERDICT_BADGE_TONE = {
|
|
|
111
111
|
*/
|
|
112
112
|
function VerdictLine({ verdict }) {
|
|
113
113
|
if (verdict.tone === 'clear') {
|
|
114
|
-
return (_jsxs("p", { className: "text-muted-foreground flex min-w-0 items-center gap-1.5
|
|
114
|
+
return (_jsxs("p", { className: "text-muted-foreground text-meta flex min-w-0 items-center gap-1.5", "data-test": "widget-verdict", "data-verdict": "clear", children: [_jsx(Icon, { name: "fa-light fa-circle-check", className: "h-3.5 w-3.5 shrink-0", "aria-hidden": true }), _jsx("span", { className: "truncate", children: verdict.sentence })] }));
|
|
115
115
|
}
|
|
116
116
|
return (_jsx(StatusBadge, { tone: VERDICT_BADGE_TONE[verdict.tone], className: "mt-0.5 max-w-full", "data-test": "widget-verdict", "data-verdict": verdict.tone, children: _jsx("span", { className: "truncate", children: verdict.sentence }) }));
|
|
117
117
|
}
|
|
118
|
-
/** The integration's logo at `size-5`, or its initial when it has none. */
|
|
119
|
-
function SourceMark({ name, logoPath }) {
|
|
120
|
-
if (!logoPath) {
|
|
121
|
-
return (_jsx("span", { "aria-hidden": true, className: "bg-muted text-muted-foreground mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-sm text-xs font-semibold", children: name.charAt(0).toUpperCase() }));
|
|
122
|
-
}
|
|
123
|
-
return (_jsx("img", { src: logoPath, alt: "", className: "mt-0.5 size-5 shrink-0 object-contain", "data-test": "widget-source-mark" }));
|
|
124
|
-
}
|
|
125
118
|
/** Widgets already warned about `children` beside `trailing`, so each warns once. */
|
|
126
119
|
const warnedChildrenWithTrailing = new Set();
|
|
127
|
-
function WidgetHeader({ title, subtitle, description, context, trailing,
|
|
120
|
+
function WidgetHeader({ title, subtitle, description, context, trailing, menu, children, }) {
|
|
128
121
|
var _a;
|
|
129
122
|
const { meta, state, data } = useWidget();
|
|
130
123
|
const effectiveTitle = title !== null && title !== void 0 ? title : meta.title;
|
|
@@ -141,8 +134,8 @@ function WidgetHeader({ title, subtitle, description, context, trailing, sourceM
|
|
|
141
134
|
}
|
|
142
135
|
if (!effectiveTitle)
|
|
143
136
|
return null;
|
|
144
|
-
const titleNode = effectiveDescription ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { render: _jsx(CardTitle, { className: "cursor-help truncate
|
|
145
|
-
return (_jsx(CardHeader, { className: "flex-shrink-0 px-5 pt-4 pb-3", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [meta.isCollapsible ? _jsx(CollapseButton, {}) : null,
|
|
137
|
+
const titleNode = effectiveDescription ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { render: _jsx(CardTitle, { className: "text-title cursor-help truncate" }), children: effectiveTitle }), _jsx(TooltipContent, { children: _jsx("p", { children: effectiveDescription }) })] }) }), _jsx("span", { className: "sr-only", children: effectiveDescription })] })) : (_jsx(CardTitle, { className: "text-title truncate", children: effectiveTitle }));
|
|
138
|
+
return (_jsx(CardHeader, { className: "flex-shrink-0 px-5 pt-4 pb-3", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [meta.isCollapsible ? _jsx(CollapseButton, {}) : null, _jsxs("div", { className: "min-w-0 space-y-0.5", children: [titleNode, verdict && verdict.tone !== 'clear' ? (_jsx(VerdictLine, { verdict: verdict })) : contextLine ? (_jsxs("p", { className: "text-muted-foreground text-meta truncate", "data-test": "widget-header-context", children: [contextLine, verdict ? (_jsxs(_Fragment, { children: [' · ', _jsx("span", { "data-test": "widget-verdict", "data-verdict": "clear", children: verdict.sentence })] })) : null] })) : verdict ? (_jsx(VerdictLine, { verdict: verdict })) : null] })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [trailing ? _jsx(HeaderTrailing, { trailing: trailing }) : null, children, _jsx(WidgetMenu, { items: menu })] })] }) }));
|
|
146
139
|
}
|
|
147
140
|
/**
|
|
148
141
|
* The actions every widget shares, whatever it renders: open its full
|
|
@@ -180,7 +173,7 @@ function WidgetMenuItem({ icon, href, external = false, onSelect, disabled, chil
|
|
|
180
173
|
*/
|
|
181
174
|
function WidgetMenuChoice({ icon, label, value, options, onChange, }) {
|
|
182
175
|
const current = options.find((option) => option.value === value);
|
|
183
|
-
return (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Icon, { name: icon !== null && icon !== void 0 ? icon : 'fa-light fa-sliders', "aria-hidden": true }), _jsx("span", { className: "flex-1", children: label }), current ? (_jsx("span", { className: "text-muted-foreground text-
|
|
176
|
+
return (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Icon, { name: icon !== null && icon !== void 0 ? icon : 'fa-light fa-sliders', "aria-hidden": true }), _jsx("span", { className: "flex-1", children: label }), current ? (_jsx("span", { className: "text-muted-foreground text-fine", children: current.label })) : null] }), _jsx(DropdownMenuSubContent, { className: "min-w-44", children: _jsx(DropdownMenuRadioGroup, { value: value, onValueChange: (next) => {
|
|
184
177
|
const match = options.find((option) => option.value === next);
|
|
185
178
|
if (match)
|
|
186
179
|
onChange(match.value);
|
|
@@ -195,7 +188,7 @@ function WidgetFreshness() {
|
|
|
195
188
|
const updated = useRelativeTime(data === null || data === void 0 ? void 0 : data.updatedAt);
|
|
196
189
|
if (state.state !== 'active' || state.collapsed || !updated)
|
|
197
190
|
return null;
|
|
198
|
-
return (_jsx("div", { className: "text-muted-foreground border-border/60 flex shrink-0 items-center gap-1.5 border-t px-5 py-2.5
|
|
191
|
+
return (_jsx("div", { className: "text-muted-foreground border-border/60 text-fine flex shrink-0 items-center gap-1.5 border-t px-5 py-2.5", "aria-live": "polite", children: (data === null || data === void 0 ? void 0 : data.refreshing) ? (_jsxs(_Fragment, { children: [_jsx(Icon, { name: "fa-light fa-arrows-rotate", className: "h-3 w-3 animate-spin", "aria-hidden": true }), _jsx(Trans, { i18nKey: "dashboard:widgetRefreshing", defaults: "Refreshing\u2026" })] })) : (_jsx(Trans, { i18nKey: "dashboard:widgetUpdated", defaults: "Updated {{time}}", values: { time: updated } })) }));
|
|
199
192
|
}
|
|
200
193
|
// ─── State-gated content ────────────────────────────────────────────────────
|
|
201
194
|
function CollapsibleBody({ collapsed, maxContentHeight, widgetId, children, }) {
|
|
@@ -351,7 +344,7 @@ function WidgetHealthFooter() {
|
|
|
351
344
|
((_b = meta.health) === null || _b === void 0 ? void 0 : _b.status) === 'degraded';
|
|
352
345
|
if (!shouldShow)
|
|
353
346
|
return null;
|
|
354
|
-
return (_jsxs("div", { className: "text-warning border-warning/30 bg-warning/5 flex items-center gap-2 border-t px-4 py-2
|
|
347
|
+
return (_jsxs("div", { className: "text-warning border-warning/30 bg-warning/5 text-fine flex items-center gap-2 border-t px-4 py-2", children: [_jsx(Icon, { name: "fa-light fa-circle-exclamation", className: "h-4 w-4 shrink-0", "aria-hidden": true }), _jsx("span", { children: lastSynced ? (_jsx(Trans, { i18nKey: "common:integrationHealth.degradedWidgetNoticeSince", defaults: "Data may be out of date. Last synced {{time}}.", values: { time: lastSynced } })) : (_jsx(Trans, { i18nKey: "common:integrationHealth.degradedWidgetNotice", defaults: "This integration is degraded \u2014 data may be stale." })) })] }));
|
|
355
348
|
}
|
|
356
349
|
function toEpochMs(value) {
|
|
357
350
|
if (value === null || value === undefined)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget.js","sourceRoot":"","sources":["../../../src/components/widgets/widget.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport {\n Component,\n type CSSProperties,\n type ReactNode,\n use,\n useMemo,\n} from 'react';\n\nimport Link from 'next/link.js';\n\nimport { Button } from '@ekanos/ui/button';\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n CardTitle,\n} from '@ekanos/ui/card';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '@ekanos/ui/dropdown-menu';\nimport { Icon } from '@ekanos/ui/icon';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@ekanos/ui/select';\nimport { StatusBadge } from '@ekanos/ui/status-badge';\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '@ekanos/ui/tooltip';\nimport { Trans } from '@ekanos/ui/trans';\nimport { cn } from '@ekanos/ui/utils';\n\nimport type { WidgetAskContext } from '../../types/widget-ask-context';\nimport type { WidgetVerdict } from '../../types/widget-verdict';\nimport { useAnimatedHeight } from './use-animated-height';\nimport { useRelativeTime } from './use-relative-time';\nimport { WidgetAskBar } from './widget-ask-bar';\nimport { WidgetContext, type WidgetRenderState } from './widget-context';\nimport {\n WidgetDisabledState,\n WidgetEmptyState,\n type WidgetEmptyStateProps,\n WidgetErrorState,\n WidgetInactiveState,\n type WidgetInactiveStateProps,\n WidgetLoadingSkeleton,\n type WidgetSkeletonVariant,\n} from './widget-state-components';\n\n// ─── Error Boundary ─────────────────────────────────────────────────────────\n\nclass WidgetErrorBoundary extends Component<\n { widgetId: string; onRetry?: () => void; children: ReactNode },\n { hasError: boolean }\n> {\n state = { hasError: false };\n static getDerivedStateFromError() {\n return { hasError: true };\n }\n componentDidCatch(error: Error, info: React.ErrorInfo) {\n console.error(`Widget \"${this.props.widgetId}\" crashed:`, error, info);\n }\n retry = () => {\n this.props.onRetry?.();\n this.setState({ hasError: false });\n };\n render() {\n if (this.state.hasError) {\n return (\n <div\n role=\"alert\"\n className=\"flex flex-1 flex-col items-center justify-center gap-3 px-5 py-8 text-center\"\n >\n <div className=\"bg-destructive/10 flex h-10 w-10 items-center justify-center rounded-full\">\n <Icon\n name=\"fa-light fa-circle-exclamation\"\n className=\"text-destructive h-5 w-5\"\n aria-hidden\n />\n </div>\n <div className=\"space-y-1\">\n <p className=\"text-foreground text-sm font-medium\">\n <Trans\n i18nKey=\"dashboard:widgetCrashedTitle\"\n defaults=\"This widget ran into a problem\"\n />\n </p>\n <p className=\"text-muted-foreground max-w-xs text-sm\">\n <Trans\n i18nKey=\"dashboard:widgetCrashedBody\"\n defaults=\"Try again. If it keeps happening, reload the page.\"\n />\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={this.retry}\n data-test=\"widget-crash-retry\"\n >\n <Icon\n name=\"fa-light fa-arrows-rotate\"\n className=\"h-4 w-4\"\n aria-hidden\n />\n <Trans i18nKey=\"dashboard:widgetRetry\" defaults=\"Try again\" />\n </Button>\n </div>\n );\n }\n return this.props.children;\n }\n}\n\n// ─── Hook ───────────────────────────────────────────────────────────────────\n\nfunction useWidget() {\n const ctx = use(WidgetContext);\n if (!ctx) {\n throw new Error(\n 'Widget.* compound components must be rendered inside a widget provider. ' +\n 'On the Ekanos dashboard the host supplies one. Anywhere else — your own ' +\n 'app, a story, a component test — wrap the widget in ' +\n \"`WidgetPreviewProvider` from '@ekanos/sdk/components'.\",\n );\n }\n return ctx;\n}\n\n/**\n * The verdict the widget reported through `Widget.DataState`, for a body that\n * wants to echo it (an empty state, a summary row). `undefined` before the\n * widget has one.\n */\nexport function useWidgetVerdict(): WidgetVerdict | undefined {\n return useWidget().data?.verdict;\n}\n\n// ─── Card ───────────────────────────────────────────────────────────────────\n\nfunction WidgetCard({ children }: { children: ReactNode }) {\n const { meta } = useWidget();\n return (\n <Card\n className={cn(\n // Neutralize the shadcn card's own py-6/gap-6 box model — widget\n // chrome owns all vertical spacing via its header/content/footer.\n 'shadow-widget @container/widget relative flex h-full flex-col gap-0 overflow-hidden rounded-lg border-0 py-0',\n meta.className,\n )}\n data-widget-id={meta.widgetId}\n >\n {children}\n <WidgetFreshness />\n </Card>\n );\n}\n\n// ─── Header ─────────────────────────────────────────────────────────────────\n\nfunction CollapseButton() {\n const { state, actions, meta } = useWidget();\n return (\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={actions.toggleCollapsed}\n disabled={state.pendingCollapse}\n className=\"bg-background dark:bg-secondary relative h-6 w-6 rounded-full before:absolute before:top-1/2 before:left-1/2 before:h-11 before:w-11 before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']\"\n aria-expanded={!state.collapsed}\n aria-controls={`widget-content-${meta.widgetId}`}\n aria-label={\n state.collapsed\n ? `Expand ${meta.title ?? 'widget'}`\n : `Collapse ${meta.title ?? 'widget'}`\n }\n >\n <Icon\n name=\"fa-light fa-chevron-right\"\n className={cn(\n 'ease-out-strong h-4 w-4 transition-transform duration-200 motion-reduce:transition-none',\n !state.collapsed && 'rotate-90',\n )}\n />\n </Button>\n );\n}\n\n/**\n * The header's one trailing control (the design system's widget rules): a \"View all ›\"\n * link to where the data lives, or a period switch. Everything else goes in\n * the ⋯ menu.\n */\nexport type WidgetHeaderTrailing =\n | {\n kind: 'link';\n href: string;\n label: ReactNode;\n /** Open in a new tab. */\n external?: boolean;\n }\n | {\n kind: 'period';\n value: string;\n options: ReadonlyArray<{ value: string; label: string }>;\n onChange: (value: string) => void;\n /** Accessible name; defaults to \"Period\". */\n label?: string;\n };\n\nfunction HeaderTrailing({ trailing }: { trailing: WidgetHeaderTrailing }) {\n if (trailing.kind === 'link') {\n const link = trailing.external ? (\n <a href={trailing.href} target=\"_blank\" rel=\"noopener noreferrer\" />\n ) : (\n <Link href={trailing.href} />\n );\n\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n render={link}\n nativeButton={false}\n data-test=\"widget-header-link\"\n >\n {trailing.label}\n <Icon\n name=\"fa-light fa-chevron-right\"\n className=\"h-3 w-3\"\n aria-hidden\n />\n </Button>\n );\n }\n\n return (\n <Select\n value={trailing.value}\n onValueChange={(value) => {\n if (typeof value === 'string') trailing.onChange(value);\n }}\n >\n <SelectTrigger\n size=\"sm\"\n aria-label={trailing.label ?? 'Period'}\n data-test=\"widget-header-period\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {trailing.options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nconst VERDICT_BADGE_TONE = {\n attention: 'warning',\n critical: 'destructive',\n degraded: 'warning',\n unknown: 'neutral',\n} as const;\n\n/**\n * The verdict under the title. Anything but `clear` is a status pill, the\n * words carrying the tone and the dot confirming it; `clear` reads muted,\n * with a check, because good news shouldn't shout.\n */\nfunction VerdictLine({ verdict }: { verdict: WidgetVerdict }) {\n if (verdict.tone === 'clear') {\n return (\n <p\n className=\"text-muted-foreground flex min-w-0 items-center gap-1.5 text-sm\"\n data-test=\"widget-verdict\"\n data-verdict=\"clear\"\n >\n <Icon\n name=\"fa-light fa-circle-check\"\n className=\"h-3.5 w-3.5 shrink-0\"\n aria-hidden\n />\n <span className=\"truncate\">{verdict.sentence}</span>\n </p>\n );\n }\n\n return (\n <StatusBadge\n tone={VERDICT_BADGE_TONE[verdict.tone]}\n className=\"mt-0.5 max-w-full\"\n data-test=\"widget-verdict\"\n data-verdict={verdict.tone}\n >\n <span className=\"truncate\">{verdict.sentence}</span>\n </StatusBadge>\n );\n}\n\n/** The integration's logo at `size-5`, or its initial when it has none. */\nfunction SourceMark({ name, logoPath }: { name: string; logoPath?: string }) {\n if (!logoPath) {\n return (\n <span\n aria-hidden\n className=\"bg-muted text-muted-foreground mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-sm text-xs font-semibold\"\n >\n {name.charAt(0).toUpperCase()}\n </span>\n );\n }\n\n return (\n // eslint-disable-next-line @next/next/no-img-element -- a bundled integration logo; next/image would need the host's loader config.\n <img\n src={logoPath}\n alt=\"\"\n className=\"mt-0.5 size-5 shrink-0 object-contain\"\n data-test=\"widget-source-mark\"\n />\n );\n}\n\n/** Widgets already warned about `children` beside `trailing`, so each warns once. */\nconst warnedChildrenWithTrailing = new Set<string>();\n\nfunction WidgetHeader({\n title,\n subtitle,\n description,\n context,\n trailing,\n sourceMark = true,\n menu,\n children,\n}: {\n /** Override the provider's title — useful when the rendered name differs from the config name. */\n title?: string;\n /**\n * Override the provider's subtitle. It renders as the context line when\n * `context` is not set.\n */\n subtitle?: string;\n /** Override the provider's description — useful for widget-computed values. */\n description?: string;\n /**\n * One muted line under the title that frames the data: \"This month\",\n * \"As of today\", \"3 accounts\". A verdict that needs the owner replaces it;\n * a clear one joins it (\"This month · All caught up\").\n */\n context?: ReactNode;\n /** At most one control on the right: a \"View all ›\" link or a period switch. */\n trailing?: WidgetHeaderTrailing;\n /**\n * Draw the integration's logo left of the title (the default) when the\n * host supplies one. Pass `false` only for a card that mixes sources.\n */\n sourceMark?: boolean;\n /**\n * `Widget.MenuItem`s and `Widget.MenuChoice`s placed at the top of the\n * header's ⋯ menu: occasional actions (\"Open in QuickBooks\") and secondary\n * settings (a comparison basis) that don't earn the one trailing slot.\n */\n menu?: ReactNode;\n /**\n * @deprecated Free controls beside the ⋯ menu. Use `trailing` for the one\n * control a header carries and `menu` for the rest; `children` goes away\n * in a later major release.\n */\n children?: ReactNode;\n}) {\n const { meta, state, data } = useWidget();\n const effectiveTitle = title ?? meta.title;\n const effectiveDescription = description ?? meta.description;\n const contextLine = context ?? subtitle ?? meta.subtitle;\n // A verdict speaks for data on screen, so it shows only while active.\n const verdict = state.state === 'active' ? data?.verdict : undefined;\n\n if (\n process.env.NODE_ENV !== 'production' &&\n children &&\n trailing &&\n !warnedChildrenWithTrailing.has(meta.widgetId)\n ) {\n warnedChildrenWithTrailing.add(meta.widgetId);\n console.warn(\n `Widget.Header (${meta.widgetId}): pass \\`trailing\\` or \\`children\\`, not both. \\`children\\` is deprecated; move other controls into \\`menu\\`.`,\n );\n }\n\n if (!effectiveTitle) return null;\n\n const titleNode = effectiveDescription ? (\n <>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger\n render={\n <CardTitle className=\"cursor-help truncate text-base font-semibold\" />\n }\n >\n {effectiveTitle}\n </TooltipTrigger>\n <TooltipContent>\n <p>{effectiveDescription}</p>\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <span className=\"sr-only\">{effectiveDescription}</span>\n </>\n ) : (\n <CardTitle className=\"truncate text-base font-semibold\">\n {effectiveTitle}\n </CardTitle>\n );\n\n return (\n <CardHeader className=\"flex-shrink-0 px-5 pt-4 pb-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n {meta.isCollapsible ? <CollapseButton /> : null}\n {sourceMark && meta.source ? (\n <SourceMark\n name={meta.source.name}\n logoPath={meta.source.logoPath}\n />\n ) : null}\n <div className=\"min-w-0 space-y-0.5\">\n {titleNode}\n {verdict && verdict.tone !== 'clear' ? (\n <VerdictLine verdict={verdict} />\n ) : contextLine ? (\n <p\n className=\"text-muted-foreground truncate text-sm\"\n data-test=\"widget-header-context\"\n >\n {contextLine}\n {verdict ? (\n <>\n {' · '}\n <span data-test=\"widget-verdict\" data-verdict=\"clear\">\n {verdict.sentence}\n </span>\n </>\n ) : null}\n </p>\n ) : verdict ? (\n <VerdictLine verdict={verdict} />\n ) : null}\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {trailing ? <HeaderTrailing trailing={trailing} /> : null}\n {children}\n <WidgetMenu items={menu} />\n </div>\n </div>\n </CardHeader>\n );\n}\n\n/**\n * The actions every widget shares, whatever it renders: open its full\n * surface, reload it, pin or unpin it. Each item needs something from the host\n * or the widget's data layer, so the menu shows only what applies — and\n * nothing at all when nothing does.\n */\nfunction WidgetMenu({ items }: { items?: ReactNode }) {\n const { state, actions, meta, data } = useWidget();\n const refresh = state.state === 'active' ? data?.refresh : undefined;\n const hasNavigation = !!items || !!meta.openHref || !!refresh;\n\n if (!hasNavigation && !meta.isPinnable) return null;\n\n const title = meta.title ?? 'widget';\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"text-muted-foreground hover:text-foreground\"\n aria-label={`Actions for ${title}`}\n data-test={`widget-menu-${meta.widgetId}`}\n />\n }\n >\n <Icon name=\"fa-light fa-ellipsis\" className=\"h-4 w-4\" aria-hidden />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-48\">\n {items}\n {meta.openHref ? (\n <DropdownMenuItem render={<Link href={meta.openHref} />}>\n <Icon name=\"fa-light fa-arrow-up-right-from-square\" aria-hidden />\n <Trans\n i18nKey=\"dashboard:widgetOpenInWorkspace\"\n defaults=\"Open in workspace\"\n />\n </DropdownMenuItem>\n ) : null}\n {refresh ? (\n <DropdownMenuItem onClick={refresh} disabled={data?.refreshing}>\n <Icon name=\"fa-light fa-arrows-rotate\" aria-hidden />\n <Trans i18nKey=\"dashboard:widgetRefresh\" defaults=\"Refresh\" />\n </DropdownMenuItem>\n ) : null}\n {hasNavigation && meta.isPinnable ? <DropdownMenuSeparator /> : null}\n {meta.isPinnable ? (\n <DropdownMenuItem\n onClick={actions.togglePinned}\n disabled={state.pendingPin}\n data-test={`widget-menu-pin-${meta.widgetId}`}\n >\n <Icon\n name={\n state.pinned\n ? 'fa-light fa-thumbtack-slash'\n : 'fa-light fa-thumbtack'\n }\n aria-hidden\n />\n {state.pinned ? (\n <Trans\n i18nKey=\"dashboard:widgetUnpin\"\n defaults=\"Unpin from board\"\n />\n ) : (\n <Trans i18nKey=\"dashboard:widgetPin\" defaults=\"Pin to board\" />\n )}\n </DropdownMenuItem>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\n/**\n * One widget-specific entry in the header's ⋯ menu; pass it through\n * `Widget.Header`'s `menu` prop. With `href` it is a link, opened in a new tab\n * when `external`; otherwise it runs `onSelect`.\n */\nfunction WidgetMenuItem({\n icon,\n href,\n external = false,\n onSelect,\n disabled,\n children,\n}: {\n /** A Font Awesome class, e.g. `fa-light fa-envelope`. */\n icon?: string;\n href?: string;\n external?: boolean;\n onSelect?: () => void;\n disabled?: boolean;\n children: ReactNode;\n}) {\n const link = !href ? undefined : external ? (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\" />\n ) : (\n <Link href={href} />\n );\n\n return (\n <DropdownMenuItem render={link} onClick={onSelect} disabled={disabled}>\n <Icon\n name={\n icon ??\n (external\n ? 'fa-light fa-arrow-up-right-from-square'\n : 'fa-light fa-arrow-right')\n }\n aria-hidden\n />\n {children}\n </DropdownMenuItem>\n );\n}\n\n/**\n * A setting that doesn't earn the header's one trailing slot (a comparison\n * basis, an as-of month), as a submenu of choices in the ⋯ menu. Pass it\n * through `Widget.Header`'s `menu` prop.\n */\nfunction WidgetMenuChoice<Value extends string>({\n icon,\n label,\n value,\n options,\n onChange,\n}: {\n /** A Font Awesome class, e.g. `fa-light fa-scale-balanced`. */\n icon?: string;\n label: ReactNode;\n value: Value;\n options: ReadonlyArray<{ value: Value; label: ReactNode }>;\n onChange: (value: Value) => void;\n}) {\n const current = options.find((option) => option.value === value);\n\n return (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n <Icon name={icon ?? 'fa-light fa-sliders'} aria-hidden />\n <span className=\"flex-1\">{label}</span>\n {current ? (\n <span className=\"text-muted-foreground text-xs\">{current.label}</span>\n ) : null}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"min-w-44\">\n <DropdownMenuRadioGroup\n value={value}\n onValueChange={(next) => {\n const match = options.find((option) => option.value === next);\n if (match) onChange(match.value);\n }}\n >\n {options.map((option) => (\n <DropdownMenuRadioItem key={option.value} value={option.value}>\n {option.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n );\n}\n\n/**\n * \"Updated 3 minutes ago\" under an active widget whose data layer reports\n * when it last loaded (see `Widget.DataState`'s `updatedAt`).\n */\nfunction WidgetFreshness() {\n const { state, data } = useWidget();\n const updated = useRelativeTime(data?.updatedAt);\n\n if (state.state !== 'active' || state.collapsed || !updated) return null;\n\n return (\n <div\n className=\"text-muted-foreground border-border/60 flex shrink-0 items-center gap-1.5 border-t px-5 py-2.5 text-xs\"\n aria-live=\"polite\"\n >\n {data?.refreshing ? (\n <>\n <Icon\n name=\"fa-light fa-arrows-rotate\"\n className=\"h-3 w-3 animate-spin\"\n aria-hidden\n />\n <Trans i18nKey=\"dashboard:widgetRefreshing\" defaults=\"Refreshing…\" />\n </>\n ) : (\n <Trans\n i18nKey=\"dashboard:widgetUpdated\"\n defaults=\"Updated {{time}}\"\n values={{ time: updated }}\n />\n )}\n </div>\n );\n}\n\n// ─── State-gated content ────────────────────────────────────────────────────\n\nfunction CollapsibleBody({\n collapsed,\n maxContentHeight,\n widgetId,\n children,\n}: {\n collapsed: boolean;\n maxContentHeight?: string;\n widgetId: string;\n children: ReactNode;\n}) {\n return (\n <div\n className={cn(\n 'ease-out-strong grid min-h-0 flex-1 transition-all duration-300 motion-reduce:transition-none',\n collapsed ? 'grid-rows-[0fr]' : 'grid-rows-[1fr]',\n )}\n >\n <div className=\"min-h-0 overflow-hidden\">\n <CardContent\n id={`widget-content-${widgetId}`}\n className={cn(\n 'flex h-full min-h-0 flex-1 flex-col overflow-y-auto p-0',\n 'ease-out-strong transition-opacity duration-200 motion-reduce:transition-none',\n collapsed ? 'opacity-0' : 'opacity-100',\n maxContentHeight && 'max-h-(--widget-max-height)',\n )}\n style={\n maxContentHeight\n ? ({ '--widget-max-height': maxContentHeight } as CSSProperties)\n : undefined\n }\n >\n {children}\n </CardContent>\n </div>\n </div>\n );\n}\n\nfunction StaticBody({\n maxContentHeight,\n children,\n}: {\n maxContentHeight?: string;\n children: ReactNode;\n}) {\n const { innerRef, height } = useAnimatedHeight();\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n <div\n className={cn(\n 'h-(--body-height) overflow-hidden transition-all duration-250 ease-out motion-reduce:transition-none',\n maxContentHeight && 'max-h-(--widget-max-height)',\n )}\n style={\n {\n '--body-height': height === 'auto' ? 'auto' : `${height}px`,\n '--widget-max-height': maxContentHeight,\n } as CSSProperties\n }\n >\n <div ref={innerRef}>{children}</div>\n </div>\n </CardContent>\n );\n}\n\n/**\n * The standard padded body for content rendered inside `Widget.Active`.\n *\n * `Widget.Active`'s own `CardContent` renders `p-0` — deliberately, so a\n * full-bleed chart or table can run edge-to-edge inside the card. That means\n * ordinary content (text, rows, a KPI readout) is left flush against the\n * card's sides while `Widget.Header` stays inset (`px-5`) unless the body\n * supplies its own padding. `Widget.Body` is that padding: `px-5` matches\n * the header's horizontal inset so header and body align, `pt-4 pb-5`\n * balances the header's `pb-3`/card's bottom edge.\n *\n * Every widget wraps its content in it:\n *\n * ```tsx\n * <Widget.Active>\n * <Widget.Body>{/* text, a Stat, a form *\\/}</Widget.Body>\n * </Widget.Active>\n * ```\n *\n * Pass `flush` when the content runs to the card's edges: edge-to-edge rows\n * that pad themselves, a chart, a table with its own header row, an image.\n */\nfunction WidgetBody({\n flush = false,\n className,\n children,\n}: {\n /**\n * Drop the padding for content that runs to the card's edges: edge-to-edge\n * rows (which pad themselves, `px-5 py-3`), a full-bleed chart, an image.\n */\n flush?: boolean;\n className?: string;\n children: ReactNode;\n}) {\n return (\n <div\n className={cn(\n 'flex flex-1 flex-col',\n flush ? 'pb-2' : 'gap-4 px-5 pt-4 pb-5',\n className,\n )}\n data-flush={flush || undefined}\n >\n {children}\n </div>\n );\n}\n\n/**\n * Renders `children` once the widget's state is `active`. Its `CardContent`\n * is flush (`p-0`); wrap content in `Widget.Body`, which pads it to line up\n * with the header, or `Widget.Body flush` for edge-to-edge rows and charts.\n * Never hand-roll a `px-*` wrapper.\n */\nfunction WidgetActive({ children }: { children: ReactNode }) {\n const { state, meta, data } = useWidget();\n if (state.state !== 'active') return null;\n const body = (\n <WidgetErrorBoundary widgetId={meta.widgetId} onRetry={data?.refresh}>\n {children}\n </WidgetErrorBoundary>\n );\n return meta.isCollapsible ? (\n <CollapsibleBody\n collapsed={state.collapsed}\n maxContentHeight={meta.maxContentHeight}\n widgetId={meta.widgetId}\n >\n {body}\n </CollapsibleBody>\n ) : (\n <StaticBody maxContentHeight={meta.maxContentHeight}>{body}</StaticBody>\n );\n}\n\n/**\n * Shows while the widget loads: a skeleton shaped like its body (`variant`,\n * `rows` by default), never a spinner.\n */\nfunction WidgetLoading({\n variant,\n children,\n}: {\n variant?: WidgetSkeletonVariant;\n children?: ReactNode;\n}) {\n const { state } = useWidget();\n if (state.state !== 'loading') return null;\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n {children ?? <WidgetLoadingSkeleton variant={variant} />}\n </CardContent>\n );\n}\n\n/**\n * Shows when the widget's data failed to load. Offers Try again whenever the\n * widget reports `onRefresh` through `Widget.DataState`. `title` and\n * `description` replace the default copy with something specific (\"Couldn't\n * load your inbox\").\n */\nfunction WidgetError({\n title,\n description,\n children,\n}: {\n title?: ReactNode;\n description?: ReactNode;\n children?: ReactNode;\n}) {\n const { state, data } = useWidget();\n if (state.state !== 'error') return null;\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n {children ?? (\n <WidgetErrorState\n title={title}\n description={description}\n onRetry={data?.refresh}\n retrying={data?.refreshing}\n />\n )}\n </CardContent>\n );\n}\n\n/**\n * Shows when the widget can't reach its data. Give it a `reason`:\n * `not-connected` (one Connect button in `action`), `no-access` (a calm note\n * pointing to an admin) or `setup-needed`. `integrationName` defaults to the\n * host's source name.\n */\nfunction WidgetInactive({\n children,\n integrationName,\n ...props\n}: WidgetInactiveStateProps & { children?: ReactNode }) {\n const { state, meta } = useWidget();\n if (state.state !== 'inactive') return null;\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n {children ?? (\n <WidgetInactiveState\n integrationName={integrationName ?? meta.source?.name}\n {...props}\n />\n )}\n </CardContent>\n );\n}\n\n/**\n * What an active widget shows when it has nothing to list. Render it inside\n * `Widget.Active`. Positive by default (\"You're all caught up\"), because an\n * empty queue is good news; `tone=\"neutral\"` for an empty search.\n */\nfunction WidgetEmpty(props: WidgetEmptyStateProps) {\n return <WidgetEmptyState {...props} />;\n}\n\nfunction WidgetDisabled({ children }: { children?: ReactNode }) {\n const { state } = useWidget();\n if (state.state !== 'disabled') return null;\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n {children ?? <WidgetDisabledState />}\n </CardContent>\n );\n}\n\n// ─── Footers ────────────────────────────────────────────────────────────────\n\nfunction WidgetFooter({ children }: { children: ReactNode }) {\n const { state, meta } = useWidget();\n if (state.state !== 'active') return null;\n return (\n <CardFooter\n className={cn(\n 'ease-out-strong flex-shrink-0 pb-6 transition-opacity duration-200 motion-reduce:transition-none',\n meta.isCollapsible && state.collapsed\n ? 'pointer-events-none opacity-0'\n : 'opacity-100',\n )}\n aria-hidden={meta.isCollapsible && state.collapsed}\n >\n {children}\n </CardFooter>\n );\n}\n\nfunction WidgetActions({ children }: { children: ReactNode }) {\n return (\n <CardFooter className=\"bg-muted flex-shrink-0 justify-end border-t py-3\">\n {children}\n </CardFooter>\n );\n}\n\nfunction WidgetAskFooter({ context }: { context?: WidgetAskContext }) {\n const { state, meta, data } = useWidget();\n const effectiveContext = context ?? meta.askContext;\n const shouldShow =\n (meta.aiFooterEnabled || !!effectiveContext) &&\n state.state === 'active' &&\n !state.collapsed;\n if (!shouldShow) return null;\n const base = effectiveContext ?? {\n widgetId: meta.widgetId,\n title: meta.title ?? meta.widgetId,\n snapshot: null,\n suggestedPrompt: `Tell me about ${meta.title ?? 'this widget'}.`,\n };\n return <WidgetAskBar context={withVerdict(base, data?.verdict)} />;\n}\n\n/**\n * Adds the widget's own conclusion to what the assistant sees, so an answer\n * starts from the verdict on the card instead of re-deriving it.\n */\nfunction withVerdict(\n context: WidgetAskContext,\n verdict: WidgetVerdict | undefined,\n): WidgetAskContext {\n if (!verdict) return context;\n return {\n ...context,\n snapshot: {\n ...context.snapshot,\n verdict: {\n tone: verdict.tone,\n sentence: verdict.sentence,\n ...(verdict.count !== undefined ? { count: verdict.count } : {}),\n },\n },\n };\n}\n\n/**\n * Renders a subtle \"data may be stale\" banner when the widget's integration is\n * `degraded` (but still active). Unhealthy integrations route to `disabled`\n * instead, so this only fires for the degraded middle ground.\n */\nfunction WidgetHealthFooter() {\n const { state, meta } = useWidget();\n const lastSuccessAt = toEpochMs(meta.health?.lastSuccessAt);\n const lastSynced = useRelativeTime(lastSuccessAt);\n const shouldShow =\n state.state === 'active' &&\n !state.collapsed &&\n meta.health?.status === 'degraded';\n if (!shouldShow) return null;\n return (\n <div className=\"text-warning border-warning/30 bg-warning/5 flex items-center gap-2 border-t px-4 py-2 text-xs\">\n <Icon\n name=\"fa-light fa-circle-exclamation\"\n className=\"h-4 w-4 shrink-0\"\n aria-hidden\n />\n <span>\n {lastSynced ? (\n <Trans\n i18nKey=\"common:integrationHealth.degradedWidgetNoticeSince\"\n defaults=\"Data may be out of date. Last synced {{time}}.\"\n values={{ time: lastSynced }}\n />\n ) : (\n <Trans\n i18nKey=\"common:integrationHealth.degradedWidgetNotice\"\n defaults=\"This integration is degraded — data may be stale.\"\n />\n )}\n </span>\n </div>\n );\n}\n\nfunction toEpochMs(\n value: number | string | Date | null | undefined,\n): number | undefined {\n if (value === null || value === undefined) return undefined;\n const ms =\n value instanceof Date ? value.getTime() : new Date(value).getTime();\n return Number.isFinite(ms) && ms > 0 ? ms : undefined;\n}\n\n// ─── Data state override ────────────────────────────────────────────────────\n\n/**\n * Wraps children in a sub-provider that overrides the effective render state\n * with the widget's own data loading / error state. Only overrides when the\n * parent state is `active` — so the integration's `inactive` / `disabled` /\n * `loading` (initial) states still win.\n *\n * Read the parent integration state BEFORE wrapping in this component if you\n * need it to gate data fetching:\n *\n * ```tsx\n * const { state } = use(WidgetContext);\n * const integrationActive = state.state === 'active';\n * const { isLoading, isError } = useMyData(accountId, integrationActive);\n * return (\n * <Widget.DataState loading={isLoading} error={isError}>\n * <Widget.Card>…</Widget.Card>\n * </Widget.DataState>\n * );\n * ```\n *\n * Pass `onRefresh`, `refreshing` and `updatedAt` to give the widget the\n * shared Refresh menu item, a Try again button on its error view, and an\n * \"Updated 3 minutes ago\" footer. With TanStack Query:\n *\n * ```tsx\n * <Widget.DataState\n * loading={query.isLoading}\n * error={query.isError}\n * onRefresh={() => void query.refetch()}\n * refreshing={query.isFetching}\n * updatedAt={query.dataUpdatedAt}\n * >\n * ```\n */\nfunction WidgetDataState({\n loading,\n error,\n inactive,\n onRefresh,\n refreshing = false,\n updatedAt,\n verdict,\n children,\n}: {\n loading?: boolean;\n error?: boolean;\n /**\n * Force the `inactive` render state even though the integration itself is\n * active. Use for widget-level \"not connected\" states (e.g. the product is\n * activated but the upstream OAuth connection is missing). Takes priority\n * over `loading` / `error`.\n */\n inactive?: boolean;\n /** Reloads the widget's data. */\n onRefresh?: () => void;\n /** A reload is in flight. */\n refreshing?: boolean;\n /**\n * When the data on screen last loaded successfully: epoch milliseconds, an\n * ISO string or a `Date`. `0` means never, which TanStack Query's\n * `dataUpdatedAt` reports before the first success.\n */\n updatedAt?: number | string | Date | null;\n /**\n * The widget's verdict on its data (the design system's widget rules): `{ tone,\n * sentence, count? }`. Compute it with the same function the integration's\n * server `getAttention` uses, so the card and home's Needs you agree.\n */\n verdict?: WidgetVerdict;\n children: ReactNode;\n}) {\n const parent = useWidget();\n const overrideState: WidgetRenderState =\n parent.state.state !== 'active'\n ? parent.state.state\n : inactive\n ? 'inactive'\n : loading\n ? 'loading'\n : error\n ? 'error'\n : 'active';\n const updatedAtMs = toEpochMs(updatedAt);\n const reportsData =\n onRefresh !== undefined ||\n updatedAtMs !== undefined ||\n verdict !== undefined;\n const value = useMemo(\n () => ({\n state: { ...parent.state, state: overrideState },\n actions: parent.actions,\n meta: parent.meta,\n data: reportsData\n ? { updatedAt: updatedAtMs, refreshing, refresh: onRefresh, verdict }\n : parent.data,\n }),\n [\n parent.state,\n parent.actions,\n parent.meta,\n parent.data,\n overrideState,\n reportsData,\n updatedAtMs,\n refreshing,\n onRefresh,\n verdict,\n ],\n );\n return <WidgetContext value={value}>{children}</WidgetContext>;\n}\n\n// ─── Compound export ────────────────────────────────────────────────────────\n\nexport const Widget = {\n Card: WidgetCard,\n Header: WidgetHeader,\n MenuItem: WidgetMenuItem,\n MenuChoice: WidgetMenuChoice,\n DataState: WidgetDataState,\n Active: WidgetActive,\n Body: WidgetBody,\n Empty: WidgetEmpty,\n Loading: WidgetLoading,\n Error: WidgetError,\n Inactive: WidgetInactive,\n Disabled: WidgetDisabled,\n Footer: WidgetFooter,\n Actions: WidgetActions,\n AskFooter: WidgetAskFooter,\n HealthFooter: WidgetHealthFooter,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"widget.js","sourceRoot":"","sources":["../../../src/components/widgets/widget.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport {\n Component,\n type CSSProperties,\n type ReactNode,\n use,\n useMemo,\n} from 'react';\n\nimport Link from 'next/link.js';\n\nimport { Button } from '@ekanos/ui/button';\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n CardTitle,\n} from '@ekanos/ui/card';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '@ekanos/ui/dropdown-menu';\nimport { Icon } from '@ekanos/ui/icon';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@ekanos/ui/select';\nimport { StatusBadge } from '@ekanos/ui/status-badge';\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '@ekanos/ui/tooltip';\nimport { Trans } from '@ekanos/ui/trans';\nimport { cn } from '@ekanos/ui/utils';\n\nimport type { WidgetAskContext } from '../../types/widget-ask-context';\nimport type { WidgetVerdict } from '../../types/widget-verdict';\nimport { useAnimatedHeight } from './use-animated-height';\nimport { useRelativeTime } from './use-relative-time';\nimport { WidgetAskBar } from './widget-ask-bar';\nimport { WidgetContext, type WidgetRenderState } from './widget-context';\nimport {\n WidgetDisabledState,\n WidgetEmptyState,\n type WidgetEmptyStateProps,\n WidgetErrorState,\n WidgetInactiveState,\n type WidgetInactiveStateProps,\n WidgetLoadingSkeleton,\n type WidgetSkeletonVariant,\n} from './widget-state-components';\n\n// ─── Error Boundary ─────────────────────────────────────────────────────────\n\nclass WidgetErrorBoundary extends Component<\n { widgetId: string; onRetry?: () => void; children: ReactNode },\n { hasError: boolean }\n> {\n state = { hasError: false };\n static getDerivedStateFromError() {\n return { hasError: true };\n }\n componentDidCatch(error: Error, info: React.ErrorInfo) {\n console.error(`Widget \"${this.props.widgetId}\" crashed:`, error, info);\n }\n retry = () => {\n this.props.onRetry?.();\n this.setState({ hasError: false });\n };\n render() {\n if (this.state.hasError) {\n return (\n <div\n role=\"alert\"\n className=\"flex flex-1 flex-col items-center justify-center gap-3 px-5 py-8 text-center\"\n >\n <div className=\"bg-destructive/10 flex h-10 w-10 items-center justify-center rounded-full\">\n <Icon\n name=\"fa-light fa-circle-exclamation\"\n className=\"text-destructive h-5 w-5\"\n aria-hidden\n />\n </div>\n <div className=\"space-y-1\">\n <p className=\"text-foreground text-label\">\n <Trans\n i18nKey=\"dashboard:widgetCrashedTitle\"\n defaults=\"This widget ran into a problem\"\n />\n </p>\n <p className=\"text-muted-foreground text-meta max-w-xs\">\n <Trans\n i18nKey=\"dashboard:widgetCrashedBody\"\n defaults=\"Try again. If it keeps happening, reload the page.\"\n />\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={this.retry}\n data-test=\"widget-crash-retry\"\n >\n <Icon\n name=\"fa-light fa-arrows-rotate\"\n className=\"h-4 w-4\"\n aria-hidden\n />\n <Trans i18nKey=\"dashboard:widgetRetry\" defaults=\"Try again\" />\n </Button>\n </div>\n );\n }\n return this.props.children;\n }\n}\n\n// ─── Hook ───────────────────────────────────────────────────────────────────\n\nfunction useWidget() {\n const ctx = use(WidgetContext);\n if (!ctx) {\n throw new Error(\n 'Widget.* compound components must be rendered inside a widget provider. ' +\n 'On the Ekanos dashboard the host supplies one. Anywhere else — your own ' +\n 'app, a story, a component test — wrap the widget in ' +\n \"`WidgetPreviewProvider` from '@ekanos/sdk/components'.\",\n );\n }\n return ctx;\n}\n\n/**\n * The verdict the widget reported through `Widget.DataState`, for a body that\n * wants to echo it (an empty state, a summary row). `undefined` before the\n * widget has one.\n */\nexport function useWidgetVerdict(): WidgetVerdict | undefined {\n return useWidget().data?.verdict;\n}\n\n// ─── Card ───────────────────────────────────────────────────────────────────\n\nfunction WidgetCard({ children }: { children: ReactNode }) {\n const { meta } = useWidget();\n return (\n <Card\n className={cn(\n // Neutralize the shadcn card's own py-6/gap-6 box model — widget\n // chrome owns all vertical spacing via its header/content/footer.\n 'shadow-widget @container/widget relative flex h-full flex-col gap-0 overflow-hidden rounded-lg border-0 py-0',\n meta.className,\n )}\n data-widget-id={meta.widgetId}\n >\n {children}\n <WidgetFreshness />\n </Card>\n );\n}\n\n// ─── Header ─────────────────────────────────────────────────────────────────\n\nfunction CollapseButton() {\n const { state, actions, meta } = useWidget();\n return (\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={actions.toggleCollapsed}\n disabled={state.pendingCollapse}\n className=\"bg-background dark:bg-secondary relative h-6 w-6 rounded-full before:absolute before:top-1/2 before:left-1/2 before:h-11 before:w-11 before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']\"\n aria-expanded={!state.collapsed}\n aria-controls={`widget-content-${meta.widgetId}`}\n aria-label={\n state.collapsed\n ? `Expand ${meta.title ?? 'widget'}`\n : `Collapse ${meta.title ?? 'widget'}`\n }\n >\n <Icon\n name=\"fa-light fa-chevron-right\"\n className={cn(\n 'ease-out-strong h-4 w-4 transition-transform duration-200 motion-reduce:transition-none',\n !state.collapsed && 'rotate-90',\n )}\n />\n </Button>\n );\n}\n\n/**\n * The header's one trailing control (the design system's widget rules): a \"View all ›\"\n * link to where the data lives, or a period switch. Everything else goes in\n * the ⋯ menu.\n */\nexport type WidgetHeaderTrailing =\n | {\n kind: 'link';\n href: string;\n label: ReactNode;\n /** Open in a new tab. */\n external?: boolean;\n }\n | {\n kind: 'period';\n value: string;\n options: ReadonlyArray<{ value: string; label: string }>;\n onChange: (value: string) => void;\n /** Accessible name; defaults to \"Period\". */\n label?: string;\n };\n\nfunction HeaderTrailing({ trailing }: { trailing: WidgetHeaderTrailing }) {\n if (trailing.kind === 'link') {\n const link = trailing.external ? (\n <a href={trailing.href} target=\"_blank\" rel=\"noopener noreferrer\" />\n ) : (\n <Link href={trailing.href} />\n );\n\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n render={link}\n nativeButton={false}\n data-test=\"widget-header-link\"\n >\n {trailing.label}\n <Icon\n name=\"fa-light fa-chevron-right\"\n className=\"h-3 w-3\"\n aria-hidden\n />\n </Button>\n );\n }\n\n return (\n <Select\n value={trailing.value}\n onValueChange={(value) => {\n if (typeof value === 'string') trailing.onChange(value);\n }}\n >\n <SelectTrigger\n size=\"sm\"\n aria-label={trailing.label ?? 'Period'}\n data-test=\"widget-header-period\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {trailing.options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nconst VERDICT_BADGE_TONE = {\n attention: 'warning',\n critical: 'destructive',\n degraded: 'warning',\n unknown: 'neutral',\n} as const;\n\n/**\n * The verdict under the title. Anything but `clear` is a status pill, the\n * words carrying the tone and the dot confirming it; `clear` reads muted,\n * with a check, because good news shouldn't shout.\n */\nfunction VerdictLine({ verdict }: { verdict: WidgetVerdict }) {\n if (verdict.tone === 'clear') {\n return (\n <p\n className=\"text-muted-foreground text-meta flex min-w-0 items-center gap-1.5\"\n data-test=\"widget-verdict\"\n data-verdict=\"clear\"\n >\n <Icon\n name=\"fa-light fa-circle-check\"\n className=\"h-3.5 w-3.5 shrink-0\"\n aria-hidden\n />\n <span className=\"truncate\">{verdict.sentence}</span>\n </p>\n );\n }\n\n return (\n <StatusBadge\n tone={VERDICT_BADGE_TONE[verdict.tone]}\n className=\"mt-0.5 max-w-full\"\n data-test=\"widget-verdict\"\n data-verdict={verdict.tone}\n >\n <span className=\"truncate\">{verdict.sentence}</span>\n </StatusBadge>\n );\n}\n\n/** Widgets already warned about `children` beside `trailing`, so each warns once. */\nconst warnedChildrenWithTrailing = new Set<string>();\n\nfunction WidgetHeader({\n title,\n subtitle,\n description,\n context,\n trailing,\n menu,\n children,\n}: {\n /** Override the provider's title — useful when the rendered name differs from the config name. */\n title?: string;\n /**\n * Override the provider's subtitle. It renders as the context line when\n * `context` is not set.\n */\n subtitle?: string;\n /** Override the provider's description — useful for widget-computed values. */\n description?: string;\n /**\n * One muted line under the title that frames the data: \"This month\",\n * \"As of today\", \"3 accounts\". A verdict that needs the owner replaces it;\n * a clear one joins it (\"This month · All caught up\").\n */\n context?: ReactNode;\n /** At most one control on the right: a \"View all ›\" link or a period switch. */\n trailing?: WidgetHeaderTrailing;\n /**\n * `Widget.MenuItem`s and `Widget.MenuChoice`s placed at the top of the\n * header's ⋯ menu: occasional actions (\"Open in QuickBooks\") and secondary\n * settings (a comparison basis) that don't earn the one trailing slot.\n */\n menu?: ReactNode;\n /**\n * @deprecated Free controls beside the ⋯ menu. Use `trailing` for the one\n * control a header carries and `menu` for the rest; `children` goes away\n * in a later major release.\n */\n children?: ReactNode;\n}) {\n const { meta, state, data } = useWidget();\n const effectiveTitle = title ?? meta.title;\n const effectiveDescription = description ?? meta.description;\n const contextLine = context ?? subtitle ?? meta.subtitle;\n // A verdict speaks for data on screen, so it shows only while active.\n const verdict = state.state === 'active' ? data?.verdict : undefined;\n\n if (\n process.env.NODE_ENV !== 'production' &&\n children &&\n trailing &&\n !warnedChildrenWithTrailing.has(meta.widgetId)\n ) {\n warnedChildrenWithTrailing.add(meta.widgetId);\n console.warn(\n `Widget.Header (${meta.widgetId}): pass \\`trailing\\` or \\`children\\`, not both. \\`children\\` is deprecated; move other controls into \\`menu\\`.`,\n );\n }\n\n if (!effectiveTitle) return null;\n\n const titleNode = effectiveDescription ? (\n <>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger\n render={<CardTitle className=\"text-title cursor-help truncate\" />}\n >\n {effectiveTitle}\n </TooltipTrigger>\n <TooltipContent>\n <p>{effectiveDescription}</p>\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <span className=\"sr-only\">{effectiveDescription}</span>\n </>\n ) : (\n <CardTitle className=\"text-title truncate\">{effectiveTitle}</CardTitle>\n );\n\n return (\n <CardHeader className=\"flex-shrink-0 px-5 pt-4 pb-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2.5\">\n {meta.isCollapsible ? <CollapseButton /> : null}\n <div className=\"min-w-0 space-y-0.5\">\n {titleNode}\n {verdict && verdict.tone !== 'clear' ? (\n <VerdictLine verdict={verdict} />\n ) : contextLine ? (\n <p\n className=\"text-muted-foreground text-meta truncate\"\n data-test=\"widget-header-context\"\n >\n {contextLine}\n {verdict ? (\n <>\n {' · '}\n <span data-test=\"widget-verdict\" data-verdict=\"clear\">\n {verdict.sentence}\n </span>\n </>\n ) : null}\n </p>\n ) : verdict ? (\n <VerdictLine verdict={verdict} />\n ) : null}\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {trailing ? <HeaderTrailing trailing={trailing} /> : null}\n {children}\n <WidgetMenu items={menu} />\n </div>\n </div>\n </CardHeader>\n );\n}\n\n/**\n * The actions every widget shares, whatever it renders: open its full\n * surface, reload it, pin or unpin it. Each item needs something from the host\n * or the widget's data layer, so the menu shows only what applies — and\n * nothing at all when nothing does.\n */\nfunction WidgetMenu({ items }: { items?: ReactNode }) {\n const { state, actions, meta, data } = useWidget();\n const refresh = state.state === 'active' ? data?.refresh : undefined;\n const hasNavigation = !!items || !!meta.openHref || !!refresh;\n\n if (!hasNavigation && !meta.isPinnable) return null;\n\n const title = meta.title ?? 'widget';\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"text-muted-foreground hover:text-foreground\"\n aria-label={`Actions for ${title}`}\n data-test={`widget-menu-${meta.widgetId}`}\n />\n }\n >\n <Icon name=\"fa-light fa-ellipsis\" className=\"h-4 w-4\" aria-hidden />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-48\">\n {items}\n {meta.openHref ? (\n <DropdownMenuItem render={<Link href={meta.openHref} />}>\n <Icon name=\"fa-light fa-arrow-up-right-from-square\" aria-hidden />\n <Trans\n i18nKey=\"dashboard:widgetOpenInWorkspace\"\n defaults=\"Open in workspace\"\n />\n </DropdownMenuItem>\n ) : null}\n {refresh ? (\n <DropdownMenuItem onClick={refresh} disabled={data?.refreshing}>\n <Icon name=\"fa-light fa-arrows-rotate\" aria-hidden />\n <Trans i18nKey=\"dashboard:widgetRefresh\" defaults=\"Refresh\" />\n </DropdownMenuItem>\n ) : null}\n {hasNavigation && meta.isPinnable ? <DropdownMenuSeparator /> : null}\n {meta.isPinnable ? (\n <DropdownMenuItem\n onClick={actions.togglePinned}\n disabled={state.pendingPin}\n data-test={`widget-menu-pin-${meta.widgetId}`}\n >\n <Icon\n name={\n state.pinned\n ? 'fa-light fa-thumbtack-slash'\n : 'fa-light fa-thumbtack'\n }\n aria-hidden\n />\n {state.pinned ? (\n <Trans\n i18nKey=\"dashboard:widgetUnpin\"\n defaults=\"Unpin from board\"\n />\n ) : (\n <Trans i18nKey=\"dashboard:widgetPin\" defaults=\"Pin to board\" />\n )}\n </DropdownMenuItem>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\n/**\n * One widget-specific entry in the header's ⋯ menu; pass it through\n * `Widget.Header`'s `menu` prop. With `href` it is a link, opened in a new tab\n * when `external`; otherwise it runs `onSelect`.\n */\nfunction WidgetMenuItem({\n icon,\n href,\n external = false,\n onSelect,\n disabled,\n children,\n}: {\n /** A Font Awesome class, e.g. `fa-light fa-envelope`. */\n icon?: string;\n href?: string;\n external?: boolean;\n onSelect?: () => void;\n disabled?: boolean;\n children: ReactNode;\n}) {\n const link = !href ? undefined : external ? (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\" />\n ) : (\n <Link href={href} />\n );\n\n return (\n <DropdownMenuItem render={link} onClick={onSelect} disabled={disabled}>\n <Icon\n name={\n icon ??\n (external\n ? 'fa-light fa-arrow-up-right-from-square'\n : 'fa-light fa-arrow-right')\n }\n aria-hidden\n />\n {children}\n </DropdownMenuItem>\n );\n}\n\n/**\n * A setting that doesn't earn the header's one trailing slot (a comparison\n * basis, an as-of month), as a submenu of choices in the ⋯ menu. Pass it\n * through `Widget.Header`'s `menu` prop.\n */\nfunction WidgetMenuChoice<Value extends string>({\n icon,\n label,\n value,\n options,\n onChange,\n}: {\n /** A Font Awesome class, e.g. `fa-light fa-scale-balanced`. */\n icon?: string;\n label: ReactNode;\n value: Value;\n options: ReadonlyArray<{ value: Value; label: ReactNode }>;\n onChange: (value: Value) => void;\n}) {\n const current = options.find((option) => option.value === value);\n\n return (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n <Icon name={icon ?? 'fa-light fa-sliders'} aria-hidden />\n <span className=\"flex-1\">{label}</span>\n {current ? (\n <span className=\"text-muted-foreground text-fine\">\n {current.label}\n </span>\n ) : null}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"min-w-44\">\n <DropdownMenuRadioGroup\n value={value}\n onValueChange={(next) => {\n const match = options.find((option) => option.value === next);\n if (match) onChange(match.value);\n }}\n >\n {options.map((option) => (\n <DropdownMenuRadioItem key={option.value} value={option.value}>\n {option.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n );\n}\n\n/**\n * \"Updated 3 minutes ago\" under an active widget whose data layer reports\n * when it last loaded (see `Widget.DataState`'s `updatedAt`).\n */\nfunction WidgetFreshness() {\n const { state, data } = useWidget();\n const updated = useRelativeTime(data?.updatedAt);\n\n if (state.state !== 'active' || state.collapsed || !updated) return null;\n\n return (\n <div\n className=\"text-muted-foreground border-border/60 text-fine flex shrink-0 items-center gap-1.5 border-t px-5 py-2.5\"\n aria-live=\"polite\"\n >\n {data?.refreshing ? (\n <>\n <Icon\n name=\"fa-light fa-arrows-rotate\"\n className=\"h-3 w-3 animate-spin\"\n aria-hidden\n />\n <Trans i18nKey=\"dashboard:widgetRefreshing\" defaults=\"Refreshing…\" />\n </>\n ) : (\n <Trans\n i18nKey=\"dashboard:widgetUpdated\"\n defaults=\"Updated {{time}}\"\n values={{ time: updated }}\n />\n )}\n </div>\n );\n}\n\n// ─── State-gated content ────────────────────────────────────────────────────\n\nfunction CollapsibleBody({\n collapsed,\n maxContentHeight,\n widgetId,\n children,\n}: {\n collapsed: boolean;\n maxContentHeight?: string;\n widgetId: string;\n children: ReactNode;\n}) {\n return (\n <div\n className={cn(\n 'ease-out-strong grid min-h-0 flex-1 transition-all duration-300 motion-reduce:transition-none',\n collapsed ? 'grid-rows-[0fr]' : 'grid-rows-[1fr]',\n )}\n >\n <div className=\"min-h-0 overflow-hidden\">\n <CardContent\n id={`widget-content-${widgetId}`}\n className={cn(\n 'flex h-full min-h-0 flex-1 flex-col overflow-y-auto p-0',\n 'ease-out-strong transition-opacity duration-200 motion-reduce:transition-none',\n collapsed ? 'opacity-0' : 'opacity-100',\n maxContentHeight && 'max-h-(--widget-max-height)',\n )}\n style={\n maxContentHeight\n ? ({ '--widget-max-height': maxContentHeight } as CSSProperties)\n : undefined\n }\n >\n {children}\n </CardContent>\n </div>\n </div>\n );\n}\n\nfunction StaticBody({\n maxContentHeight,\n children,\n}: {\n maxContentHeight?: string;\n children: ReactNode;\n}) {\n const { innerRef, height } = useAnimatedHeight();\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n <div\n className={cn(\n 'h-(--body-height) overflow-hidden transition-all duration-250 ease-out motion-reduce:transition-none',\n maxContentHeight && 'max-h-(--widget-max-height)',\n )}\n style={\n {\n '--body-height': height === 'auto' ? 'auto' : `${height}px`,\n '--widget-max-height': maxContentHeight,\n } as CSSProperties\n }\n >\n <div ref={innerRef}>{children}</div>\n </div>\n </CardContent>\n );\n}\n\n/**\n * The standard padded body for content rendered inside `Widget.Active`.\n *\n * `Widget.Active`'s own `CardContent` renders `p-0` — deliberately, so a\n * full-bleed chart or table can run edge-to-edge inside the card. That means\n * ordinary content (text, rows, a KPI readout) is left flush against the\n * card's sides while `Widget.Header` stays inset (`px-5`) unless the body\n * supplies its own padding. `Widget.Body` is that padding: `px-5` matches\n * the header's horizontal inset so header and body align, `pt-4 pb-5`\n * balances the header's `pb-3`/card's bottom edge.\n *\n * Every widget wraps its content in it:\n *\n * ```tsx\n * <Widget.Active>\n * <Widget.Body>{/* text, a Stat, a form *\\/}</Widget.Body>\n * </Widget.Active>\n * ```\n *\n * Pass `flush` when the content runs to the card's edges: edge-to-edge rows\n * that pad themselves, a chart, a table with its own header row, an image.\n */\nfunction WidgetBody({\n flush = false,\n className,\n children,\n}: {\n /**\n * Drop the padding for content that runs to the card's edges: edge-to-edge\n * rows (which pad themselves, `px-5 py-3`), a full-bleed chart, an image.\n */\n flush?: boolean;\n className?: string;\n children: ReactNode;\n}) {\n return (\n <div\n className={cn(\n 'flex flex-1 flex-col',\n flush ? 'pb-2' : 'gap-4 px-5 pt-4 pb-5',\n className,\n )}\n data-flush={flush || undefined}\n >\n {children}\n </div>\n );\n}\n\n/**\n * Renders `children` once the widget's state is `active`. Its `CardContent`\n * is flush (`p-0`); wrap content in `Widget.Body`, which pads it to line up\n * with the header, or `Widget.Body flush` for edge-to-edge rows and charts.\n * Never hand-roll a `px-*` wrapper.\n */\nfunction WidgetActive({ children }: { children: ReactNode }) {\n const { state, meta, data } = useWidget();\n if (state.state !== 'active') return null;\n const body = (\n <WidgetErrorBoundary widgetId={meta.widgetId} onRetry={data?.refresh}>\n {children}\n </WidgetErrorBoundary>\n );\n return meta.isCollapsible ? (\n <CollapsibleBody\n collapsed={state.collapsed}\n maxContentHeight={meta.maxContentHeight}\n widgetId={meta.widgetId}\n >\n {body}\n </CollapsibleBody>\n ) : (\n <StaticBody maxContentHeight={meta.maxContentHeight}>{body}</StaticBody>\n );\n}\n\n/**\n * Shows while the widget loads: a skeleton shaped like its body (`variant`,\n * `rows` by default), never a spinner.\n */\nfunction WidgetLoading({\n variant,\n children,\n}: {\n variant?: WidgetSkeletonVariant;\n children?: ReactNode;\n}) {\n const { state } = useWidget();\n if (state.state !== 'loading') return null;\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n {children ?? <WidgetLoadingSkeleton variant={variant} />}\n </CardContent>\n );\n}\n\n/**\n * Shows when the widget's data failed to load. Offers Try again whenever the\n * widget reports `onRefresh` through `Widget.DataState`. `title` and\n * `description` replace the default copy with something specific (\"Couldn't\n * load your inbox\").\n */\nfunction WidgetError({\n title,\n description,\n children,\n}: {\n title?: ReactNode;\n description?: ReactNode;\n children?: ReactNode;\n}) {\n const { state, data } = useWidget();\n if (state.state !== 'error') return null;\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n {children ?? (\n <WidgetErrorState\n title={title}\n description={description}\n onRetry={data?.refresh}\n retrying={data?.refreshing}\n />\n )}\n </CardContent>\n );\n}\n\n/**\n * Shows when the widget can't reach its data. Give it a `reason`:\n * `not-connected` (one Connect button in `action`), `no-access` (a calm note\n * pointing to an admin) or `setup-needed`. `integrationName` defaults to the\n * host's source name.\n */\nfunction WidgetInactive({\n children,\n integrationName,\n ...props\n}: WidgetInactiveStateProps & { children?: ReactNode }) {\n const { state, meta } = useWidget();\n if (state.state !== 'inactive') return null;\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n {children ?? (\n <WidgetInactiveState\n integrationName={integrationName ?? meta.source?.name}\n {...props}\n />\n )}\n </CardContent>\n );\n}\n\n/**\n * What an active widget shows when it has nothing to list. Render it inside\n * `Widget.Active`. Positive by default (\"You're all caught up\"), because an\n * empty queue is good news; `tone=\"neutral\"` for an empty search.\n */\nfunction WidgetEmpty(props: WidgetEmptyStateProps) {\n return <WidgetEmptyState {...props} />;\n}\n\nfunction WidgetDisabled({ children }: { children?: ReactNode }) {\n const { state } = useWidget();\n if (state.state !== 'disabled') return null;\n return (\n <CardContent className=\"flex min-h-0 flex-1 flex-col p-0\">\n {children ?? <WidgetDisabledState />}\n </CardContent>\n );\n}\n\n// ─── Footers ────────────────────────────────────────────────────────────────\n\nfunction WidgetFooter({ children }: { children: ReactNode }) {\n const { state, meta } = useWidget();\n if (state.state !== 'active') return null;\n return (\n <CardFooter\n className={cn(\n 'ease-out-strong flex-shrink-0 pb-6 transition-opacity duration-200 motion-reduce:transition-none',\n meta.isCollapsible && state.collapsed\n ? 'pointer-events-none opacity-0'\n : 'opacity-100',\n )}\n aria-hidden={meta.isCollapsible && state.collapsed}\n >\n {children}\n </CardFooter>\n );\n}\n\nfunction WidgetActions({ children }: { children: ReactNode }) {\n return (\n <CardFooter className=\"bg-muted flex-shrink-0 justify-end border-t py-3\">\n {children}\n </CardFooter>\n );\n}\n\nfunction WidgetAskFooter({ context }: { context?: WidgetAskContext }) {\n const { state, meta, data } = useWidget();\n const effectiveContext = context ?? meta.askContext;\n const shouldShow =\n (meta.aiFooterEnabled || !!effectiveContext) &&\n state.state === 'active' &&\n !state.collapsed;\n if (!shouldShow) return null;\n const base = effectiveContext ?? {\n widgetId: meta.widgetId,\n title: meta.title ?? meta.widgetId,\n snapshot: null,\n suggestedPrompt: `Tell me about ${meta.title ?? 'this widget'}.`,\n };\n return <WidgetAskBar context={withVerdict(base, data?.verdict)} />;\n}\n\n/**\n * Adds the widget's own conclusion to what the assistant sees, so an answer\n * starts from the verdict on the card instead of re-deriving it.\n */\nfunction withVerdict(\n context: WidgetAskContext,\n verdict: WidgetVerdict | undefined,\n): WidgetAskContext {\n if (!verdict) return context;\n return {\n ...context,\n snapshot: {\n ...context.snapshot,\n verdict: {\n tone: verdict.tone,\n sentence: verdict.sentence,\n ...(verdict.count !== undefined ? { count: verdict.count } : {}),\n },\n },\n };\n}\n\n/**\n * Renders a subtle \"data may be stale\" banner when the widget's integration is\n * `degraded` (but still active). Unhealthy integrations route to `disabled`\n * instead, so this only fires for the degraded middle ground.\n */\nfunction WidgetHealthFooter() {\n const { state, meta } = useWidget();\n const lastSuccessAt = toEpochMs(meta.health?.lastSuccessAt);\n const lastSynced = useRelativeTime(lastSuccessAt);\n const shouldShow =\n state.state === 'active' &&\n !state.collapsed &&\n meta.health?.status === 'degraded';\n if (!shouldShow) return null;\n return (\n <div className=\"text-warning border-warning/30 bg-warning/5 text-fine flex items-center gap-2 border-t px-4 py-2\">\n <Icon\n name=\"fa-light fa-circle-exclamation\"\n className=\"h-4 w-4 shrink-0\"\n aria-hidden\n />\n <span>\n {lastSynced ? (\n <Trans\n i18nKey=\"common:integrationHealth.degradedWidgetNoticeSince\"\n defaults=\"Data may be out of date. Last synced {{time}}.\"\n values={{ time: lastSynced }}\n />\n ) : (\n <Trans\n i18nKey=\"common:integrationHealth.degradedWidgetNotice\"\n defaults=\"This integration is degraded — data may be stale.\"\n />\n )}\n </span>\n </div>\n );\n}\n\nfunction toEpochMs(\n value: number | string | Date | null | undefined,\n): number | undefined {\n if (value === null || value === undefined) return undefined;\n const ms =\n value instanceof Date ? value.getTime() : new Date(value).getTime();\n return Number.isFinite(ms) && ms > 0 ? ms : undefined;\n}\n\n// ─── Data state override ────────────────────────────────────────────────────\n\n/**\n * Wraps children in a sub-provider that overrides the effective render state\n * with the widget's own data loading / error state. Only overrides when the\n * parent state is `active` — so the integration's `inactive` / `disabled` /\n * `loading` (initial) states still win.\n *\n * Read the parent integration state BEFORE wrapping in this component if you\n * need it to gate data fetching:\n *\n * ```tsx\n * const { state } = use(WidgetContext);\n * const integrationActive = state.state === 'active';\n * const { isLoading, isError } = useMyData(accountId, integrationActive);\n * return (\n * <Widget.DataState loading={isLoading} error={isError}>\n * <Widget.Card>…</Widget.Card>\n * </Widget.DataState>\n * );\n * ```\n *\n * Pass `onRefresh`, `refreshing` and `updatedAt` to give the widget the\n * shared Refresh menu item, a Try again button on its error view, and an\n * \"Updated 3 minutes ago\" footer. With TanStack Query:\n *\n * ```tsx\n * <Widget.DataState\n * loading={query.isLoading}\n * error={query.isError}\n * onRefresh={() => void query.refetch()}\n * refreshing={query.isFetching}\n * updatedAt={query.dataUpdatedAt}\n * >\n * ```\n */\nfunction WidgetDataState({\n loading,\n error,\n inactive,\n onRefresh,\n refreshing = false,\n updatedAt,\n verdict,\n children,\n}: {\n loading?: boolean;\n error?: boolean;\n /**\n * Force the `inactive` render state even though the integration itself is\n * active. Use for widget-level \"not connected\" states (e.g. the product is\n * activated but the upstream OAuth connection is missing). Takes priority\n * over `loading` / `error`.\n */\n inactive?: boolean;\n /** Reloads the widget's data. */\n onRefresh?: () => void;\n /** A reload is in flight. */\n refreshing?: boolean;\n /**\n * When the data on screen last loaded successfully: epoch milliseconds, an\n * ISO string or a `Date`. `0` means never, which TanStack Query's\n * `dataUpdatedAt` reports before the first success.\n */\n updatedAt?: number | string | Date | null;\n /**\n * The widget's verdict on its data (the design system's widget rules): `{ tone,\n * sentence, count? }`. Compute it with the same function the integration's\n * server `getAttention` uses, so the card and home's Needs you agree.\n */\n verdict?: WidgetVerdict;\n children: ReactNode;\n}) {\n const parent = useWidget();\n const overrideState: WidgetRenderState =\n parent.state.state !== 'active'\n ? parent.state.state\n : inactive\n ? 'inactive'\n : loading\n ? 'loading'\n : error\n ? 'error'\n : 'active';\n const updatedAtMs = toEpochMs(updatedAt);\n const reportsData =\n onRefresh !== undefined ||\n updatedAtMs !== undefined ||\n verdict !== undefined;\n const value = useMemo(\n () => ({\n state: { ...parent.state, state: overrideState },\n actions: parent.actions,\n meta: parent.meta,\n data: reportsData\n ? { updatedAt: updatedAtMs, refreshing, refresh: onRefresh, verdict }\n : parent.data,\n }),\n [\n parent.state,\n parent.actions,\n parent.meta,\n parent.data,\n overrideState,\n reportsData,\n updatedAtMs,\n refreshing,\n onRefresh,\n verdict,\n ],\n );\n return <WidgetContext value={value}>{children}</WidgetContext>;\n}\n\n// ─── Compound export ────────────────────────────────────────────────────────\n\nexport const Widget = {\n Card: WidgetCard,\n Header: WidgetHeader,\n MenuItem: WidgetMenuItem,\n MenuChoice: WidgetMenuChoice,\n DataState: WidgetDataState,\n Active: WidgetActive,\n Body: WidgetBody,\n Empty: WidgetEmpty,\n Loading: WidgetLoading,\n Error: WidgetError,\n Inactive: WidgetInactive,\n Disabled: WidgetDisabled,\n Footer: WidgetFooter,\n Actions: WidgetActions,\n AskFooter: WidgetAskFooter,\n HealthFooter: WidgetHealthFooter,\n};\n"]}
|
|
@@ -24,11 +24,14 @@ export function partnerOAuthErrorParam(integrationSlug) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
const PARTNER_OAUTH_ERROR_MESSAGES = {
|
|
27
|
-
|
|
27
|
+
// A declined grant is proof the consent screen worked. It is the one code
|
|
28
|
+
// here that is not a fault, so it does not get fault-shaped copy.
|
|
29
|
+
denied: 'Nothing was connected, and nothing changed at the provider. Connect again whenever you are ready.',
|
|
28
30
|
provider_error: 'The provider refused the connection request. Trying again won’t help; contact support if it keeps happening.',
|
|
29
31
|
expired: 'The connection attempt expired. Try connecting again.',
|
|
30
32
|
invalid: 'The connection attempt could not be verified. Try connecting again.',
|
|
31
33
|
unauthorized: 'You need to be signed in as the person who started the connection.',
|
|
34
|
+
permission_lost: 'You no longer have permission to connect apps for this team. Ask an owner or an admin to connect it.',
|
|
32
35
|
not_configured: 'This integration is not configured for your workspace yet. Ask an administrator to add its OAuth credentials.',
|
|
33
36
|
exchange_failed: 'The provider rejected the connection. Try again, or contact support if it keeps failing.',
|
|
34
37
|
save_failed: 'Connected, but the credentials could not be saved. Try again.',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partner-oauth.js","sourceRoot":"","sources":["../../src/hooks/partner-oauth.ts"],"names":[],"mappings":"AAiCA,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAW7C,yEAAyE;AACzE,MAAM,UAAU,uBAAuB,CACrC,eAAuB,EACvB,OAAO,GAA4B,EAAE;;IAErC,MAAM,IAAI,SAAG,OAAO,CAAC,OAAO,mCAAI,gBAAgB,CAAC;IACjD,OAAO,GAAG,IAAI,IAAI,eAAe,gBAAgB,CAAC;AACpD,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,qBAAqB,CACnC,eAAuB,EACvB,OAAwD;;IAExD,MAAM,IAAI,SAAG,OAAO,CAAC,OAAO,mCAAI,gBAAgB,CAAC;IACjD,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACpE,OAAO,GAAG,IAAI,IAAI,eAAe,iBAAiB,KAAK,EAAE,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,eAAuB;IAEvB,OAAO;QACL,IAAI,EAAE,GAAG,eAAe,cAAc;QACtC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,wBAAC,4BAA4B,CAAC,KAAK,CAAC,mCAAI,IAAI,GAAA;KAClE,CAAC;AACJ,CAAC;AAED,MAAM,4BAA4B,GAA2B;IAC3D,MAAM,
|
|
1
|
+
{"version":3,"file":"partner-oauth.js","sourceRoot":"","sources":["../../src/hooks/partner-oauth.ts"],"names":[],"mappings":"AAiCA,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAW7C,yEAAyE;AACzE,MAAM,UAAU,uBAAuB,CACrC,eAAuB,EACvB,OAAO,GAA4B,EAAE;;IAErC,MAAM,IAAI,SAAG,OAAO,CAAC,OAAO,mCAAI,gBAAgB,CAAC;IACjD,OAAO,GAAG,IAAI,IAAI,eAAe,gBAAgB,CAAC;AACpD,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,qBAAqB,CACnC,eAAuB,EACvB,OAAwD;;IAExD,MAAM,IAAI,SAAG,OAAO,CAAC,OAAO,mCAAI,gBAAgB,CAAC;IACjD,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACpE,OAAO,GAAG,IAAI,IAAI,eAAe,iBAAiB,KAAK,EAAE,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,eAAuB;IAEvB,OAAO;QACL,IAAI,EAAE,GAAG,eAAe,cAAc;QACtC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,wBAAC,4BAA4B,CAAC,KAAK,CAAC,mCAAI,IAAI,GAAA;KAClE,CAAC;AACJ,CAAC;AAED,MAAM,4BAA4B,GAA2B;IAC3D,0EAA0E;IAC1E,kEAAkE;IAClE,MAAM,EACJ,mGAAmG;IACrG,cAAc,EACZ,8GAA8G;IAChH,OAAO,EAAE,uDAAuD;IAChE,OAAO,EACL,qEAAqE;IACvE,YAAY,EACV,oEAAoE;IACtE,eAAe,EACb,sGAAsG;IACxG,cAAc,EACZ,+GAA+G;IACjH,eAAe,EACb,0FAA0F;IAC5F,WAAW,EAAE,+DAA+D;IAC5E,eAAe,EACb,sIAAsI;IACxI,cAAc,EACZ,mJAAmJ;IACrJ,mBAAmB,EACjB,mHAAmH;CACtH,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,eAAuB,EACvB,OAAO,GAA4B,EAAE;IAErC,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QAC7B,MAAM,GAAG,GAAG,qBAAqB,CAAC,eAAe,kCAC5C,OAAO,KACV,SAAS,IACT,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE;YAC3C,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;SACxC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,uBAAuB,eAAe,wBAAwB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,IAAI,CACzG,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAE/C,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CACb,QAAQ,eAAe,gDAAgD,CACxE,CAAC;QACJ,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAC9B,KAAc;IAEd,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAQ,KAA+B,CAAC,OAAO,KAAK,SAAS,CAC9D,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The browser half of the hosted partner OAuth transport.\n *\n * A partner's `oauth` declaration names the provider endpoints and the\n * client-credential secrets; the HOST owns the flow — authorize redirect,\n * `state`, callback, code exchange, and the `onTokens` call — behind three\n * generic routes it serves for every partner integration:\n *\n * GET /api/integrations/[slug]/oauth/connect?accountId=…&returnPath=…\n * GET /api/integrations/[slug]/oauth/callback (the provider redirects here)\n * GET /api/integrations/[slug]/oauth/status?accountId=…\n *\n * These helpers are the only place a partner package should reference those\n * paths, so a route move is one SDK change rather than a hunt through every\n * integration. `OAuthActivationForm`'s `connectPath` default still points at\n * the per-integration `/connect` route first-party packages ship themselves;\n * a partner passes `connectPath: partnerOAuthConnectPath(slug)` explicitly.\n *\n * ```tsx\n * <OAuthActivationForm\n * {...props}\n * config={{ slug, displayName: 'Acme', connectPath: partnerOAuthConnectPath(slug), … }}\n * checkConnection={createPartnerConnectionCheck(slug)}\n * errorParam={partnerOAuthErrorParam(slug)}\n * />\n * ```\n */\nimport type {\n ConnectionCheckAction,\n ConnectionCheckResult,\n OAuthErrorParam,\n} from './use-oauth-connection-status';\n\nconst DEFAULT_BASE_URL = '/api/integrations';\n\nexport interface PartnerOAuthPathOptions {\n /**\n * Overrides the base path. Exists for harnesses and tests that serve the\n * routes from somewhere other than the host's own origin; production code\n * should leave it alone.\n */\n baseUrl?: string;\n}\n\n/** The hosted connect route the activation form sends the browser to. */\nexport function partnerOAuthConnectPath(\n integrationSlug: string,\n options: PartnerOAuthPathOptions = {},\n): string {\n const base = options.baseUrl ?? DEFAULT_BASE_URL;\n return `${base}/${integrationSlug}/oauth/connect`;\n}\n\n/** The hosted status route `createPartnerConnectionCheck` reads. */\nexport function partnerOAuthStatusUrl(\n integrationSlug: string,\n options: PartnerOAuthPathOptions & { accountId: string },\n): string {\n const base = options.baseUrl ?? DEFAULT_BASE_URL;\n const query = new URLSearchParams({ accountId: options.accountId });\n return `${base}/${integrationSlug}/oauth/status?${query}`;\n}\n\n/**\n * The error query param the hosted callback appends on failure:\n * `<slug>_oauth_error=<code>`. Codes are the transport's, so the messages\n * live here rather than in every partner package.\n */\nexport function partnerOAuthErrorParam(\n integrationSlug: string,\n): OAuthErrorParam {\n return {\n name: `${integrationSlug}_oauth_error`,\n toMessage: (value) => PARTNER_OAUTH_ERROR_MESSAGES[value] ?? null,\n };\n}\n\nconst PARTNER_OAUTH_ERROR_MESSAGES: Record<string, string> = {\n // A declined grant is proof the consent screen worked. It is the one code\n // here that is not a fault, so it does not get fault-shaped copy.\n denied:\n 'Nothing was connected, and nothing changed at the provider. Connect again whenever you are ready.',\n provider_error:\n 'The provider refused the connection request. Trying again won’t help; contact support if it keeps happening.',\n expired: 'The connection attempt expired. Try connecting again.',\n invalid:\n 'The connection attempt could not be verified. Try connecting again.',\n unauthorized:\n 'You need to be signed in as the person who started the connection.',\n permission_lost:\n 'You no longer have permission to connect apps for this team. Ask an owner or an admin to connect it.',\n not_configured:\n 'This integration is not configured for your workspace yet. Ask an administrator to add its OAuth credentials.',\n exchange_failed:\n 'The provider rejected the connection. Try again, or contact support if it keeps failing.',\n save_failed: 'Connected, but the credentials could not be saved. Try again.',\n wrong_workspace:\n 'You signed in to a different account or workspace than this one is set up for. Switch to that account at the provider and try again.',\n app_not_set_up:\n 'The provider refused this integration’s app settings, so trying again won’t help. Ask an administrator to check the app’s scopes and credentials.',\n organization_access:\n 'Connect with personal access — this integration only uses your own account. Try again and choose personal access.',\n};\n\n/**\n * A `ConnectionCheckAction` backed by the hosted status route. The route\n * answers 200 with the standard envelope for both outcomes — `{ success:\n * true, data: { obtainedAt } }` or `{ success: false, error }` — and a non-OK\n * status only when the request itself is unauthorized, so a query error\n * surfaces as an error rather than as a misleading \"not connected\".\n */\nexport function createPartnerConnectionCheck(\n integrationSlug: string,\n options: PartnerOAuthPathOptions = {},\n): ConnectionCheckAction {\n return async ({ accountId }) => {\n const url = partnerOAuthStatusUrl(integrationSlug, {\n ...options,\n accountId,\n });\n\n const response = await globalThis.fetch(url, {\n headers: { accept: 'application/json' },\n });\n\n if (!response.ok) {\n throw new Error(\n `Could not read the '${integrationSlug}' connection status (${response.status} ${response.statusText}).`,\n );\n }\n\n const payload: unknown = await response.json();\n\n if (!isConnectionCheckResult(payload)) {\n throw new Error(\n `The '${integrationSlug}' status route returned an unexpected payload.`,\n );\n }\n\n return payload;\n };\n}\n\nfunction isConnectionCheckResult(\n value: unknown,\n): value is ConnectionCheckResult {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { success?: unknown }).success === 'boolean'\n );\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AgentEventDeclaration } from '@ekanos/integration-schema';
|
|
2
|
+
/**
|
|
3
|
+
* Declares one agent event (plan 222) with its functions typed by the
|
|
4
|
+
* payload schema: `matches`, `conversation` and `isOwnActivity` receive the
|
|
5
|
+
* validated payload. Returns the declaration unchanged — `defineIntegration`
|
|
6
|
+
* validates it with the rest of `agentEvents`.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const paymentFailed = defineAgentEvent({
|
|
10
|
+
* id: 'acme-payments.payment_failed',
|
|
11
|
+
* label: 'A payment failed',
|
|
12
|
+
* description: 'A customer payment was declined.',
|
|
13
|
+
* payloadSchema: z.object({ paymentId: z.string(), amount: z.number() }),
|
|
14
|
+
* instructions: 'A payment failed. Work out why and what to tell the customer.',
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function defineAgentEvent<Payload>(event: AgentEventDeclaration<Payload>): AgentEventDeclaration<Payload>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declares one agent event (plan 222) with its functions typed by the
|
|
3
|
+
* payload schema: `matches`, `conversation` and `isOwnActivity` receive the
|
|
4
|
+
* validated payload. Returns the declaration unchanged — `defineIntegration`
|
|
5
|
+
* validates it with the rest of `agentEvents`.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* const paymentFailed = defineAgentEvent({
|
|
9
|
+
* id: 'acme-payments.payment_failed',
|
|
10
|
+
* label: 'A payment failed',
|
|
11
|
+
* description: 'A customer payment was declined.',
|
|
12
|
+
* payloadSchema: z.object({ paymentId: z.string(), amount: z.number() }),
|
|
13
|
+
* instructions: 'A payment failed. Work out why and what to tell the customer.',
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function defineAgentEvent(event) {
|
|
18
|
+
return event;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=define-agent-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-agent-event.js","sourceRoot":"","sources":["../../src/integration/define-agent-event.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAqC;IAErC,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import type { AgentEventDeclaration } from '@ekanos/integration-schema';\n\n/**\n * Declares one agent event (plan 222) with its functions typed by the\n * payload schema: `matches`, `conversation` and `isOwnActivity` receive the\n * validated payload. Returns the declaration unchanged — `defineIntegration`\n * validates it with the rest of `agentEvents`.\n *\n * ```ts\n * const paymentFailed = defineAgentEvent({\n * id: 'acme-payments.payment_failed',\n * label: 'A payment failed',\n * description: 'A customer payment was declined.',\n * payloadSchema: z.object({ paymentId: z.string(), amount: z.number() }),\n * instructions: 'A payment failed. Work out why and what to tell the customer.',\n * });\n * ```\n */\nexport function defineAgentEvent<Payload>(\n event: AgentEventDeclaration<Payload>,\n): AgentEventDeclaration<Payload> {\n return event;\n}\n"]}
|