@shipfox/client-onboarding 23.0.0 → 25.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +28 -0
  3. package/README.md +26 -7
  4. package/dist/components/setup-checklist-body.d.ts +3 -0
  5. package/dist/components/setup-checklist-body.d.ts.map +1 -0
  6. package/dist/components/setup-checklist-body.js +25 -0
  7. package/dist/components/setup-checklist-body.js.map +1 -0
  8. package/dist/components/setup-checklist-completion.d.ts +6 -0
  9. package/dist/components/setup-checklist-completion.d.ts.map +1 -0
  10. package/dist/components/setup-checklist-completion.js +156 -0
  11. package/dist/components/setup-checklist-completion.js.map +1 -0
  12. package/dist/components/setup-checklist-confetti.d.ts +13 -0
  13. package/dist/components/setup-checklist-confetti.d.ts.map +1 -0
  14. package/dist/components/setup-checklist-confetti.js +24 -0
  15. package/dist/components/setup-checklist-confetti.js.map +1 -0
  16. package/dist/components/setup-checklist-host-primitives.d.ts +11 -0
  17. package/dist/components/setup-checklist-host-primitives.d.ts.map +1 -0
  18. package/dist/components/setup-checklist-host-primitives.js +71 -0
  19. package/dist/components/setup-checklist-host-primitives.js.map +1 -0
  20. package/dist/components/setup-checklist-row.d.ts +7 -0
  21. package/dist/components/setup-checklist-row.d.ts.map +1 -0
  22. package/dist/components/setup-checklist-row.js +177 -0
  23. package/dist/components/setup-checklist-row.js.map +1 -0
  24. package/dist/components/setup-checklist-types.d.ts +19 -0
  25. package/dist/components/setup-checklist-types.d.ts.map +1 -0
  26. package/dist/components/setup-checklist-types.js +3 -0
  27. package/dist/components/setup-checklist-types.js.map +1 -0
  28. package/dist/components/setup-checklist.d.ts +5 -0
  29. package/dist/components/setup-checklist.d.ts.map +1 -0
  30. package/dist/components/setup-checklist.js +5 -0
  31. package/dist/components/setup-checklist.js.map +1 -0
  32. package/dist/components/setup-checklist.stories.d.ts +19 -0
  33. package/dist/components/setup-checklist.stories.d.ts.map +1 -0
  34. package/dist/components/setup-checklist.stories.js +305 -0
  35. package/dist/components/setup-checklist.stories.js.map +1 -0
  36. package/dist/components/workspace-setup-checklist.d.ts +3 -0
  37. package/dist/components/workspace-setup-checklist.d.ts.map +1 -0
  38. package/dist/components/workspace-setup-checklist.js +80 -0
  39. package/dist/components/workspace-setup-checklist.js.map +1 -0
  40. package/dist/components/workspace-setup-indicator.d.ts +3 -0
  41. package/dist/components/workspace-setup-indicator.d.ts.map +1 -0
  42. package/dist/components/workspace-setup-indicator.js +133 -0
  43. package/dist/components/workspace-setup-indicator.js.map +1 -0
  44. package/dist/core/setup-checklist.d.ts +2 -0
  45. package/dist/core/setup-checklist.d.ts.map +1 -1
  46. package/dist/core/setup-checklist.js +11 -6
  47. package/dist/core/setup-checklist.js.map +1 -1
  48. package/dist/feature.d.ts +6 -0
  49. package/dist/feature.d.ts.map +1 -0
  50. package/dist/feature.js +19 -0
  51. package/dist/feature.js.map +1 -0
  52. package/dist/hooks/api/setup-checklist.d.ts +13 -0
  53. package/dist/hooks/api/setup-checklist.d.ts.map +1 -0
  54. package/dist/hooks/api/setup-checklist.js +103 -0
  55. package/dist/hooks/api/setup-checklist.js.map +1 -0
  56. package/dist/hooks/use-checklist-analytics.d.ts +6 -0
  57. package/dist/hooks/use-checklist-analytics.d.ts.map +1 -0
  58. package/dist/hooks/use-checklist-analytics.js +51 -0
  59. package/dist/hooks/use-checklist-analytics.js.map +1 -0
  60. package/dist/hooks/use-checklist-dismissal.d.ts +5 -0
  61. package/dist/hooks/use-checklist-dismissal.d.ts.map +1 -0
  62. package/dist/hooks/use-checklist-dismissal.js +31 -0
  63. package/dist/hooks/use-checklist-dismissal.js.map +1 -0
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +1 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/tsconfig.test.tsbuildinfo +1 -1
  69. package/package.json +12 -5
  70. package/src/components/setup-checklist-body.tsx +35 -0
  71. package/src/components/setup-checklist-completion.test.tsx +124 -0
  72. package/src/components/setup-checklist-completion.tsx +165 -0
  73. package/src/components/setup-checklist-confetti.test.ts +19 -0
  74. package/src/components/setup-checklist-confetti.ts +39 -0
  75. package/src/components/setup-checklist-host-primitives.tsx +65 -0
  76. package/src/components/setup-checklist-row.tsx +189 -0
  77. package/src/components/setup-checklist-types.ts +21 -0
  78. package/src/components/setup-checklist.stories.tsx +281 -0
  79. package/src/components/setup-checklist.test.tsx +564 -0
  80. package/src/components/setup-checklist.tsx +8 -0
  81. package/src/components/workspace-setup-checklist.tsx +91 -0
  82. package/src/components/workspace-setup-indicator.tsx +126 -0
  83. package/src/core/setup-checklist.test.ts +2 -2
  84. package/src/core/setup-checklist.ts +19 -10
  85. package/src/feature.ts +16 -0
  86. package/src/hooks/api/setup-checklist.ts +144 -0
  87. package/src/hooks/use-checklist-analytics.ts +60 -0
  88. package/src/hooks/use-checklist-dismissal.ts +30 -0
  89. package/src/index.ts +8 -0
  90. package/tsconfig.build.tsbuildinfo +1 -1
  91. package/vitest.config.ts +2 -0
@@ -0,0 +1,80 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useClientAnalytics, useMaybeActiveWorkspace } from '@shipfox/client-shell/runtime';
3
+ import { Panel, PanelBody } from '@shipfox/react-ui/panel';
4
+ import { useCallback, useState } from 'react';
5
+ import { useSetupChecklistQueryState } from '#hooks/api/setup-checklist.js';
6
+ import { useCompletionTransition, useShownAnalytics } from '#hooks/use-checklist-analytics.js';
7
+ import { useChecklistDismissal } from '#hooks/use-checklist-dismissal.js';
8
+ import { SetupChecklistBody } from './setup-checklist-body.js';
9
+ import { ChecklistHeader, ChecklistSkeleton, checklistCountLabel } from './setup-checklist-host-primitives.js';
10
+ export function WorkspaceSetupChecklist(props = {}) {
11
+ if (props.workspace) {
12
+ return /*#__PURE__*/ _jsx(WorkspaceSetupChecklistForWorkspace, {
13
+ workspace: props.workspace
14
+ }, props.workspace.id);
15
+ }
16
+ return /*#__PURE__*/ _jsx(WorkspaceSetupChecklistFromShell, {});
17
+ }
18
+ function WorkspaceSetupChecklistFromShell() {
19
+ const workspace = useMaybeActiveWorkspace();
20
+ return workspace ? /*#__PURE__*/ _jsx(WorkspaceSetupChecklistForWorkspace, {
21
+ workspace: workspace
22
+ }, workspace.id) : null;
23
+ }
24
+ function WorkspaceSetupChecklistForWorkspace({ workspace }) {
25
+ const dismissal = useChecklistDismissal(workspace.id);
26
+ const queryState = useSetupChecklistQueryState(workspace.id, !dismissal.dismissed);
27
+ const [burstPending, setBurstPending] = useState(false);
28
+ const handleCompleted = useCallback((completed)=>{
29
+ if (completed) setBurstPending(true);
30
+ }, []);
31
+ const showCompletion = useCompletionTransition(queryState, 'panel', handleCompleted);
32
+ const analytics = useClientAnalytics();
33
+ const consumeBurst = useCallback(()=>setBurstPending(false), []);
34
+ const dismiss = useCallback(()=>{
35
+ dismissal.dismiss();
36
+ analytics.capture('onboarding_checklist_dismissed', {
37
+ host: 'panel'
38
+ });
39
+ }, [
40
+ analytics,
41
+ dismissal
42
+ ]);
43
+ const handleAction = useCallback((item)=>{
44
+ analytics.capture('onboarding_checklist_row_clicked', {
45
+ row_id: item.id
46
+ });
47
+ }, [
48
+ analytics
49
+ ]);
50
+ const isVisible = !dismissal.dismissed && queryState.baseSettled && (!queryState.checklist.complete || showCompletion);
51
+ useShownAnalytics('panel', isVisible);
52
+ if (dismissal.dismissed) return null;
53
+ if (queryState.baseSettled && queryState.checklist.complete && !showCompletion) return null;
54
+ return /*#__PURE__*/ _jsx(Panel, {
55
+ asChild: true,
56
+ className: "w-full",
57
+ children: /*#__PURE__*/ _jsxs("section", {
58
+ "aria-label": "Get started",
59
+ children: [
60
+ /*#__PURE__*/ _jsx(ChecklistHeader, {
61
+ count: queryState.baseSettled ? checklistCountLabel(queryState.checklist) : undefined,
62
+ onDismiss: dismiss
63
+ }),
64
+ /*#__PURE__*/ _jsx(PanelBody, {
65
+ children: queryState.baseSettled ? queryState.checklist.complete && !showCompletion ? null : /*#__PURE__*/ _jsx(SetupChecklistBody, {
66
+ checklist: queryState.checklist,
67
+ workspaceSlug: workspace.slug,
68
+ completion: showCompletion,
69
+ showBurst: burstPending,
70
+ onBurstComplete: consumeBurst,
71
+ onAction: handleAction,
72
+ onDone: dismiss
73
+ }) : /*#__PURE__*/ _jsx(ChecklistSkeleton, {})
74
+ })
75
+ ]
76
+ })
77
+ });
78
+ }
79
+
80
+ //# sourceMappingURL=workspace-setup-checklist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/workspace-setup-checklist.tsx"],"sourcesContent":["import {useClientAnalytics, useMaybeActiveWorkspace} from '@shipfox/client-shell/runtime';\nimport {Panel, PanelBody} from '@shipfox/react-ui/panel';\nimport {useCallback, useState} from 'react';\nimport type {SetupChecklistItem} from '#core/setup-checklist.js';\nimport {useSetupChecklistQueryState} from '#hooks/api/setup-checklist.js';\nimport {useCompletionTransition, useShownAnalytics} from '#hooks/use-checklist-analytics.js';\nimport {useChecklistDismissal} from '#hooks/use-checklist-dismissal.js';\nimport {SetupChecklistBody} from './setup-checklist-body.js';\nimport {\n ChecklistHeader,\n ChecklistSkeleton,\n checklistCountLabel,\n} from './setup-checklist-host-primitives.js';\nimport type {WorkspaceReference, WorkspaceSetupHostProps} from './setup-checklist-types.js';\n\nexport function WorkspaceSetupChecklist(props: WorkspaceSetupHostProps = {}) {\n if (props.workspace) {\n return (\n <WorkspaceSetupChecklistForWorkspace key={props.workspace.id} workspace={props.workspace} />\n );\n }\n\n return <WorkspaceSetupChecklistFromShell />;\n}\n\nfunction WorkspaceSetupChecklistFromShell() {\n const workspace = useMaybeActiveWorkspace();\n return workspace ? (\n <WorkspaceSetupChecklistForWorkspace key={workspace.id} workspace={workspace} />\n ) : null;\n}\n\nfunction WorkspaceSetupChecklistForWorkspace({workspace}: {workspace: WorkspaceReference}) {\n const dismissal = useChecklistDismissal(workspace.id);\n const queryState = useSetupChecklistQueryState(workspace.id, !dismissal.dismissed);\n const [burstPending, setBurstPending] = useState(false);\n const handleCompleted = useCallback((completed: boolean) => {\n if (completed) setBurstPending(true);\n }, []);\n const showCompletion = useCompletionTransition(queryState, 'panel', handleCompleted);\n const analytics = useClientAnalytics();\n const consumeBurst = useCallback(() => setBurstPending(false), []);\n\n const dismiss = useCallback(() => {\n dismissal.dismiss();\n analytics.capture('onboarding_checklist_dismissed', {host: 'panel'});\n }, [analytics, dismissal]);\n const handleAction = useCallback(\n (item: SetupChecklistItem) => {\n analytics.capture('onboarding_checklist_row_clicked', {row_id: item.id});\n },\n [analytics],\n );\n const isVisible =\n !dismissal.dismissed &&\n queryState.baseSettled &&\n (!queryState.checklist.complete || showCompletion);\n useShownAnalytics('panel', isVisible);\n\n if (dismissal.dismissed) return null;\n\n if (queryState.baseSettled && queryState.checklist.complete && !showCompletion) return null;\n\n return (\n <Panel asChild className=\"w-full\">\n <section aria-label=\"Get started\">\n <ChecklistHeader\n count={queryState.baseSettled ? checklistCountLabel(queryState.checklist) : undefined}\n onDismiss={dismiss}\n />\n <PanelBody>\n {queryState.baseSettled ? (\n queryState.checklist.complete && !showCompletion ? null : (\n <SetupChecklistBody\n checklist={queryState.checklist}\n workspaceSlug={workspace.slug}\n completion={showCompletion}\n showBurst={burstPending}\n onBurstComplete={consumeBurst}\n onAction={handleAction}\n onDone={dismiss}\n />\n )\n ) : (\n <ChecklistSkeleton />\n )}\n </PanelBody>\n </section>\n </Panel>\n );\n}\n"],"names":["useClientAnalytics","useMaybeActiveWorkspace","Panel","PanelBody","useCallback","useState","useSetupChecklistQueryState","useCompletionTransition","useShownAnalytics","useChecklistDismissal","SetupChecklistBody","ChecklistHeader","ChecklistSkeleton","checklistCountLabel","WorkspaceSetupChecklist","props","workspace","WorkspaceSetupChecklistForWorkspace","id","WorkspaceSetupChecklistFromShell","dismissal","queryState","dismissed","burstPending","setBurstPending","handleCompleted","completed","showCompletion","analytics","consumeBurst","dismiss","capture","host","handleAction","item","row_id","isVisible","baseSettled","checklist","complete","asChild","className","section","aria-label","count","undefined","onDismiss","workspaceSlug","slug","completion","showBurst","onBurstComplete","onAction","onDone"],"mappings":";AAAA,SAAQA,kBAAkB,EAAEC,uBAAuB,QAAO,gCAAgC;AAC1F,SAAQC,KAAK,EAAEC,SAAS,QAAO,0BAA0B;AACzD,SAAQC,WAAW,EAAEC,QAAQ,QAAO,QAAQ;AAE5C,SAAQC,2BAA2B,QAAO,gCAAgC;AAC1E,SAAQC,uBAAuB,EAAEC,iBAAiB,QAAO,oCAAoC;AAC7F,SAAQC,qBAAqB,QAAO,oCAAoC;AACxE,SAAQC,kBAAkB,QAAO,4BAA4B;AAC7D,SACEC,eAAe,EACfC,iBAAiB,EACjBC,mBAAmB,QACd,uCAAuC;AAG9C,OAAO,SAASC,wBAAwBC,QAAiC,CAAC,CAAC;IACzE,IAAIA,MAAMC,SAAS,EAAE;QACnB,qBACE,KAACC;YAA6DD,WAAWD,MAAMC,SAAS;WAA9CD,MAAMC,SAAS,CAACE,EAAE;IAEhE;IAEA,qBAAO,KAACC;AACV;AAEA,SAASA;IACP,MAAMH,YAAYf;IAClB,OAAOe,0BACL,KAACC;QAAuDD,WAAWA;OAAzBA,UAAUE,EAAE,IACpD;AACN;AAEA,SAASD,oCAAoC,EAACD,SAAS,EAAkC;IACvF,MAAMI,YAAYX,sBAAsBO,UAAUE,EAAE;IACpD,MAAMG,aAAaf,4BAA4BU,UAAUE,EAAE,EAAE,CAACE,UAAUE,SAAS;IACjF,MAAM,CAACC,cAAcC,gBAAgB,GAAGnB,SAAS;IACjD,MAAMoB,kBAAkBrB,YAAY,CAACsB;QACnC,IAAIA,WAAWF,gBAAgB;IACjC,GAAG,EAAE;IACL,MAAMG,iBAAiBpB,wBAAwBc,YAAY,SAASI;IACpE,MAAMG,YAAY5B;IAClB,MAAM6B,eAAezB,YAAY,IAAMoB,gBAAgB,QAAQ,EAAE;IAEjE,MAAMM,UAAU1B,YAAY;QAC1BgB,UAAUU,OAAO;QACjBF,UAAUG,OAAO,CAAC,kCAAkC;YAACC,MAAM;QAAO;IACpE,GAAG;QAACJ;QAAWR;KAAU;IACzB,MAAMa,eAAe7B,YACnB,CAAC8B;QACCN,UAAUG,OAAO,CAAC,oCAAoC;YAACI,QAAQD,KAAKhB,EAAE;QAAA;IACxE,GACA;QAACU;KAAU;IAEb,MAAMQ,YACJ,CAAChB,UAAUE,SAAS,IACpBD,WAAWgB,WAAW,IACrB,CAAA,CAAChB,WAAWiB,SAAS,CAACC,QAAQ,IAAIZ,cAAa;IAClDnB,kBAAkB,SAAS4B;IAE3B,IAAIhB,UAAUE,SAAS,EAAE,OAAO;IAEhC,IAAID,WAAWgB,WAAW,IAAIhB,WAAWiB,SAAS,CAACC,QAAQ,IAAI,CAACZ,gBAAgB,OAAO;IAEvF,qBACE,KAACzB;QAAMsC,OAAO;QAACC,WAAU;kBACvB,cAAA,MAACC;YAAQC,cAAW;;8BAClB,KAAChC;oBACCiC,OAAOvB,WAAWgB,WAAW,GAAGxB,oBAAoBQ,WAAWiB,SAAS,IAAIO;oBAC5EC,WAAWhB;;8BAEb,KAAC3B;8BACEkB,WAAWgB,WAAW,GACrBhB,WAAWiB,SAAS,CAACC,QAAQ,IAAI,CAACZ,iBAAiB,qBACjD,KAACjB;wBACC4B,WAAWjB,WAAWiB,SAAS;wBAC/BS,eAAe/B,UAAUgC,IAAI;wBAC7BC,YAAYtB;wBACZuB,WAAW3B;wBACX4B,iBAAiBtB;wBACjBuB,UAAUnB;wBACVoB,QAAQvB;uCAIZ,KAAClB;;;;;AAMb"}
@@ -0,0 +1,3 @@
1
+ import type { WorkspaceSetupHostProps } from './setup-checklist-types.js';
2
+ export declare function WorkspaceSetupIndicator(props?: WorkspaceSetupHostProps): import("react").JSX.Element;
3
+ //# sourceMappingURL=workspace-setup-indicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-setup-indicator.d.ts","sourceRoot":"","sources":["../../src/components/workspace-setup-indicator.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAqB,uBAAuB,EAAC,MAAM,4BAA4B,CAAC;AAE5F,wBAAgB,uBAAuB,CAAC,KAAK,GAAE,uBAA4B,+BAQ1E"}
@@ -0,0 +1,133 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useClientAnalytics, useMaybeActiveWorkspace } from '@shipfox/client-shell/runtime';
3
+ import { Button } from '@shipfox/react-ui/button';
4
+ import { Icon } from '@shipfox/react-ui/icon';
5
+ import { Panel, PanelBody } from '@shipfox/react-ui/panel';
6
+ import { Popover, PopoverContent, PopoverTrigger } from '@shipfox/react-ui/popover';
7
+ import { cn } from '@shipfox/react-ui/utils';
8
+ import { useCallback, useId, useState } from 'react';
9
+ import { useSetupChecklistQueryState } from '#hooks/api/setup-checklist.js';
10
+ import { useCompletionTransition, useShownAnalytics } from '#hooks/use-checklist-analytics.js';
11
+ import { useChecklistDismissal } from '#hooks/use-checklist-dismissal.js';
12
+ import { SetupChecklistBody } from './setup-checklist-body.js';
13
+ import { ChecklistDismissAction, checklistCountLabel } from './setup-checklist-host-primitives.js';
14
+ export function WorkspaceSetupIndicator(props = {}) {
15
+ if (props.workspace) {
16
+ return /*#__PURE__*/ _jsx(WorkspaceSetupIndicatorForWorkspace, {
17
+ workspace: props.workspace
18
+ }, props.workspace.id);
19
+ }
20
+ return /*#__PURE__*/ _jsx(WorkspaceSetupIndicatorFromShell, {});
21
+ }
22
+ function WorkspaceSetupIndicatorFromShell() {
23
+ const workspace = useMaybeActiveWorkspace();
24
+ return workspace ? /*#__PURE__*/ _jsx(WorkspaceSetupIndicatorForWorkspace, {
25
+ workspace: workspace
26
+ }, workspace.id) : null;
27
+ }
28
+ function WorkspaceSetupIndicatorForWorkspace({ workspace }) {
29
+ const dismissal = useChecklistDismissal(workspace.id);
30
+ const queryState = useSetupChecklistQueryState(workspace.id, !dismissal.dismissed);
31
+ const analytics = useClientAnalytics();
32
+ const [open, setOpen] = useState(false);
33
+ const [pulseKey, setPulseKey] = useState(0);
34
+ const [burstPending, setBurstPending] = useState(false);
35
+ const handleCompleted = useCallback((completed)=>{
36
+ if (!completed) return;
37
+ setBurstPending(true);
38
+ if (!open) setPulseKey((key)=>key + 1);
39
+ }, [
40
+ open
41
+ ]);
42
+ const showCompletion = useCompletionTransition(queryState, 'popover', handleCompleted);
43
+ const consumeBurst = useCallback(()=>setBurstPending(false), []);
44
+ const triggerId = useId();
45
+ const dismiss = useCallback(()=>{
46
+ dismissal.dismiss();
47
+ setOpen(false);
48
+ analytics.capture('onboarding_checklist_dismissed', {
49
+ host: 'popover'
50
+ });
51
+ }, [
52
+ analytics,
53
+ dismissal
54
+ ]);
55
+ const handleAction = useCallback((item)=>{
56
+ analytics.capture('onboarding_checklist_row_clicked', {
57
+ row_id: item.id
58
+ });
59
+ setOpen(false);
60
+ }, [
61
+ analytics
62
+ ]);
63
+ const indicatorReady = queryState.completionReady || queryState.checklist.openCount > 0;
64
+ const isVisible = !dismissal.dismissed && queryState.baseSettled && indicatorReady && (!queryState.checklist.complete || showCompletion);
65
+ useShownAnalytics('popover', isVisible);
66
+ if (dismissal.dismissed || !queryState.baseSettled || !indicatorReady) return null;
67
+ if (queryState.checklist.complete && !showCompletion) return null;
68
+ const countLabel = checklistCountLabel(queryState.checklist);
69
+ const ariaLabel = `Get started, ${countLabel}`;
70
+ return /*#__PURE__*/ _jsxs(Popover, {
71
+ modal: false,
72
+ open: open,
73
+ onOpenChange: setOpen,
74
+ children: [
75
+ /*#__PURE__*/ _jsx(PopoverTrigger, {
76
+ asChild: true,
77
+ children: /*#__PURE__*/ _jsxs(Button, {
78
+ type: "button",
79
+ variant: "transparent",
80
+ size: "sm",
81
+ id: triggerId,
82
+ "aria-label": ariaLabel,
83
+ className: "gap-inline",
84
+ children: [
85
+ /*#__PURE__*/ _jsx("span", {
86
+ className: cn('inline-flex items-center', pulseKey > 0 && 'motion-safe:animate-[pulse_1s_ease-in-out_1]'),
87
+ children: /*#__PURE__*/ _jsx(Icon, {
88
+ name: "circleDottedLine",
89
+ size: 16,
90
+ "aria-hidden": "true"
91
+ })
92
+ }, pulseKey),
93
+ /*#__PURE__*/ _jsxs("span", {
94
+ children: [
95
+ "Get started · ",
96
+ countLabel
97
+ ]
98
+ })
99
+ ]
100
+ })
101
+ }),
102
+ /*#__PURE__*/ _jsx(PopoverContent, {
103
+ align: "end",
104
+ className: "w-[360px] max-w-[calc(100vw-32px)] p-0",
105
+ "aria-labelledby": triggerId,
106
+ children: /*#__PURE__*/ _jsx(Panel, {
107
+ asChild: true,
108
+ children: /*#__PURE__*/ _jsxs("section", {
109
+ "aria-label": "Get started",
110
+ children: [
111
+ /*#__PURE__*/ _jsx(PanelBody, {
112
+ children: /*#__PURE__*/ _jsx(SetupChecklistBody, {
113
+ checklist: queryState.checklist,
114
+ workspaceSlug: workspace.slug,
115
+ completion: showCompletion,
116
+ showBurst: burstPending,
117
+ onBurstComplete: consumeBurst,
118
+ onAction: handleAction,
119
+ onDone: dismiss
120
+ })
121
+ }),
122
+ /*#__PURE__*/ _jsx(ChecklistDismissAction, {
123
+ onDismiss: dismiss
124
+ })
125
+ ]
126
+ })
127
+ })
128
+ })
129
+ ]
130
+ });
131
+ }
132
+
133
+ //# sourceMappingURL=workspace-setup-indicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/workspace-setup-indicator.tsx"],"sourcesContent":["import {useClientAnalytics, useMaybeActiveWorkspace} from '@shipfox/client-shell/runtime';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Panel, PanelBody} from '@shipfox/react-ui/panel';\nimport {Popover, PopoverContent, PopoverTrigger} from '@shipfox/react-ui/popover';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useCallback, useId, useState} from 'react';\nimport type {SetupChecklistItem} from '#core/setup-checklist.js';\nimport {useSetupChecklistQueryState} from '#hooks/api/setup-checklist.js';\nimport {useCompletionTransition, useShownAnalytics} from '#hooks/use-checklist-analytics.js';\nimport {useChecklistDismissal} from '#hooks/use-checklist-dismissal.js';\nimport {SetupChecklistBody} from './setup-checklist-body.js';\nimport {ChecklistDismissAction, checklistCountLabel} from './setup-checklist-host-primitives.js';\nimport type {WorkspaceReference, WorkspaceSetupHostProps} from './setup-checklist-types.js';\n\nexport function WorkspaceSetupIndicator(props: WorkspaceSetupHostProps = {}) {\n if (props.workspace) {\n return (\n <WorkspaceSetupIndicatorForWorkspace key={props.workspace.id} workspace={props.workspace} />\n );\n }\n\n return <WorkspaceSetupIndicatorFromShell />;\n}\n\nfunction WorkspaceSetupIndicatorFromShell() {\n const workspace = useMaybeActiveWorkspace();\n return workspace ? (\n <WorkspaceSetupIndicatorForWorkspace key={workspace.id} workspace={workspace} />\n ) : null;\n}\n\nfunction WorkspaceSetupIndicatorForWorkspace({workspace}: {workspace: WorkspaceReference}) {\n const dismissal = useChecklistDismissal(workspace.id);\n const queryState = useSetupChecklistQueryState(workspace.id, !dismissal.dismissed);\n const analytics = useClientAnalytics();\n const [open, setOpen] = useState(false);\n const [pulseKey, setPulseKey] = useState(0);\n const [burstPending, setBurstPending] = useState(false);\n const handleCompleted = useCallback(\n (completed: boolean) => {\n if (!completed) return;\n setBurstPending(true);\n if (!open) setPulseKey((key) => key + 1);\n },\n [open],\n );\n const showCompletion = useCompletionTransition(queryState, 'popover', handleCompleted);\n const consumeBurst = useCallback(() => setBurstPending(false), []);\n const triggerId = useId();\n\n const dismiss = useCallback(() => {\n dismissal.dismiss();\n setOpen(false);\n analytics.capture('onboarding_checklist_dismissed', {host: 'popover'});\n }, [analytics, dismissal]);\n const handleAction = useCallback(\n (item: SetupChecklistItem) => {\n analytics.capture('onboarding_checklist_row_clicked', {row_id: item.id});\n setOpen(false);\n },\n [analytics],\n );\n\n const indicatorReady = queryState.completionReady || queryState.checklist.openCount > 0;\n const isVisible =\n !dismissal.dismissed &&\n queryState.baseSettled &&\n indicatorReady &&\n (!queryState.checklist.complete || showCompletion);\n useShownAnalytics('popover', isVisible);\n\n if (dismissal.dismissed || !queryState.baseSettled || !indicatorReady) return null;\n if (queryState.checklist.complete && !showCompletion) return null;\n\n const countLabel = checklistCountLabel(queryState.checklist);\n const ariaLabel = `Get started, ${countLabel}`;\n\n return (\n <Popover modal={false} open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"transparent\"\n size=\"sm\"\n id={triggerId}\n aria-label={ariaLabel}\n className=\"gap-inline\"\n >\n <span\n key={pulseKey}\n className={cn(\n 'inline-flex items-center',\n pulseKey > 0 && 'motion-safe:animate-[pulse_1s_ease-in-out_1]',\n )}\n >\n <Icon name=\"circleDottedLine\" size={16} aria-hidden=\"true\" />\n </span>\n <span>Get started · {countLabel}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className=\"w-[360px] max-w-[calc(100vw-32px)] p-0\"\n aria-labelledby={triggerId}\n >\n <Panel asChild>\n <section aria-label=\"Get started\">\n <PanelBody>\n <SetupChecklistBody\n checklist={queryState.checklist}\n workspaceSlug={workspace.slug}\n completion={showCompletion}\n showBurst={burstPending}\n onBurstComplete={consumeBurst}\n onAction={handleAction}\n onDone={dismiss}\n />\n </PanelBody>\n <ChecklistDismissAction onDismiss={dismiss} />\n </section>\n </Panel>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["useClientAnalytics","useMaybeActiveWorkspace","Button","Icon","Panel","PanelBody","Popover","PopoverContent","PopoverTrigger","cn","useCallback","useId","useState","useSetupChecklistQueryState","useCompletionTransition","useShownAnalytics","useChecklistDismissal","SetupChecklistBody","ChecklistDismissAction","checklistCountLabel","WorkspaceSetupIndicator","props","workspace","WorkspaceSetupIndicatorForWorkspace","id","WorkspaceSetupIndicatorFromShell","dismissal","queryState","dismissed","analytics","open","setOpen","pulseKey","setPulseKey","burstPending","setBurstPending","handleCompleted","completed","key","showCompletion","consumeBurst","triggerId","dismiss","capture","host","handleAction","item","row_id","indicatorReady","completionReady","checklist","openCount","isVisible","baseSettled","complete","countLabel","ariaLabel","modal","onOpenChange","asChild","type","variant","size","aria-label","className","span","name","aria-hidden","align","aria-labelledby","section","workspaceSlug","slug","completion","showBurst","onBurstComplete","onAction","onDone","onDismiss"],"mappings":"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"}
@@ -11,6 +11,8 @@ export interface SetupChecklistItem {
11
11
  status: SetupChecklistItemStatus;
12
12
  /** Tracked items count toward completion; pointers never do. */
13
13
  tracked: boolean;
14
+ /** The row's underlying integration has a connection that needs attention. */
15
+ attention?: boolean;
14
16
  /** One line of purpose, rendered for open and pointer rows. */
15
17
  purpose?: string;
16
18
  /** Primary action, a link to the settings page where the work happens. */
@@ -1 +1 @@
1
- {"version":3,"file":"setup-checklist.d.ts","sourceRoot":"","sources":["../../src/core/setup-checklist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,6BAA6B,EAAC,MAAM,4BAA4B,CAAC;AAE9E,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhE,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,SAAS,GACT,OAAO,GACP,QAAQ,GACR,gBAAgB,GAChB,gBAAgB,GAChB,WAAW,CAAC;AAEhB,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,oBAAoB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,wBAAwB,CAAC;IACjC,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,6BAA6B,CAAC;IACzC,mBAAmB,EAAE,SAAS,GAAG,MAAM,CAAC;IACxC,uBAAuB,EAAE,OAAO,CAAC;IACjC,aAAa,EAAE;QAAC,oBAAoB,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAC,CAAC;IACpE,UAAU,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,sBAAsB,EAAE,MAAM,CAAA;KAAC,CAAC;CACnE;AAWD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,UAAU,GACX,EAAE,mBAAmB,GAAG,cAAc,CAiEtC"}
1
+ {"version":3,"file":"setup-checklist.d.ts","sourceRoot":"","sources":["../../src/core/setup-checklist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,6BAA6B,EAAC,MAAM,4BAA4B,CAAC;AAE9E,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhE,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,SAAS,GACT,OAAO,GACP,QAAQ,GACR,gBAAgB,GAChB,gBAAgB,GAChB,WAAW,CAAC;AAEhB,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,oBAAoB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,wBAAwB,CAAC;IACjC,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,6BAA6B,CAAC;IACzC,mBAAmB,EAAE,SAAS,GAAG,MAAM,CAAC;IACxC,uBAAuB,EAAE,OAAO,CAAC;IACjC,aAAa,EAAE;QAAC,oBAAoB,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAC,CAAC;IACpE,UAAU,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,sBAAsB,EAAE,MAAM,CAAA;KAAC,CAAC;CACnE;AAWD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,UAAU,GACX,EAAE,mBAAmB,GAAG,cAAc,CAoEtC"}
@@ -1,5 +1,5 @@
1
1
  const TOOLS_TITLE = 'Connect your tools';
2
- const TOOLS_PURPOSE = 'Connect issue tracking, messaging, observability, or any Shipfox integration so workflows can react to events and agents can use them';
2
+ const TOOLS_PURPOSE = 'Connect issue tracking, messaging, observability, or any Shipfox integration';
3
3
  const RUNNER_PURPOSE = 'Jobs wait in `pending` until a runner is online';
4
4
  const MODEL_PROVIDER_PURPOSE = 'Agent steps need a model provider to run';
5
5
  const FIRST_WORKFLOW_PURPOSE = 'Add a workflow file under `.shipfox/workflows/` and Shipfox picks it up on the next push';
@@ -10,6 +10,7 @@ const TEAMMATES_PURPOSE = 'Everyone in the workspace can edit workflows and see
10
10
  * the runner and model-provider rows exist only when the installation does
11
11
  * not already provide the capability.
12
12
  */ export function deriveSetupChecklist({ readiness, installationRunners, workspaceRunnerCapacity, modelProvider, membership }) {
13
+ const toolsAttention = !readiness.hasToolIntegration && attentionToolProviders(readiness).length > 0;
13
14
  const items = [
14
15
  {
15
16
  id: 'source-control',
@@ -28,6 +29,7 @@ const TEAMMATES_PURPOSE = 'Everyone in the workspace can edit workflows and see
28
29
  title: toolsTitle(readiness),
29
30
  status: readiness.hasToolIntegration ? 'done' : 'open',
30
31
  tracked: true,
32
+ attention: toolsAttention,
31
33
  purpose: TOOLS_PURPOSE,
32
34
  action: {
33
35
  label: 'Connect',
@@ -93,18 +95,21 @@ const TEAMMATES_PURPOSE = 'Everyone in the workspace can edit workflows and see
93
95
  }
94
96
  function toolsTitle(readiness) {
95
97
  if (!readiness.hasToolIntegration) {
96
- const attentionToolProviders = readiness.attentionProviders.map((providerKey)=>readiness.providers.find(({ provider })=>provider === providerKey)).filter((provider)=>provider !== undefined && !provider.capabilities.includes('source_control'));
97
- if (attentionToolProviders.length === 1) {
98
- const provider = attentionToolProviders[0];
98
+ const providers = attentionToolProviders(readiness);
99
+ if (providers.length === 1) {
100
+ const provider = providers[0];
99
101
  if (provider !== undefined) {
100
102
  return `${provider.displayName || provider.provider} needs attention`;
101
103
  }
102
104
  }
103
- if (attentionToolProviders.length > 1) {
104
- return `${attentionToolProviders.length} integrations need attention`;
105
+ if (providers.length > 1) {
106
+ return `${providers.length} integrations need attention`;
105
107
  }
106
108
  }
107
109
  return TOOLS_TITLE;
108
110
  }
111
+ function attentionToolProviders(readiness) {
112
+ return readiness.attentionProviders.map((providerKey)=>readiness.providers.find(({ provider })=>provider === providerKey)).filter((provider)=>provider !== undefined && !provider.capabilities.includes('source_control'));
113
+ }
109
114
 
110
115
  //# sourceMappingURL=setup-checklist.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/setup-checklist.ts"],"sourcesContent":["import type {WorkspaceIntegrationReadiness} from './integration-readiness.js';\n\nexport type SetupChecklistItemStatus = 'done' | 'open' | 'info';\n\nexport type SetupChecklistItemId =\n | 'source-control'\n | 'project'\n | 'tools'\n | 'runner'\n | 'model-provider'\n | 'first-workflow'\n | 'teammates';\n\nexport interface SetupChecklistAction {\n label: string;\n href: string;\n}\n\nexport interface SetupChecklistItem {\n id: SetupChecklistItemId;\n title: string;\n status: SetupChecklistItemStatus;\n /** Tracked items count toward completion; pointers never do. */\n tracked: boolean;\n /** One line of purpose, rendered for open and pointer rows. */\n purpose?: string;\n /** Primary action, a link to the settings page where the work happens. */\n action?: SetupChecklistAction;\n}\n\nexport interface SetupChecklist {\n /** Rows in spec order: done progress, activation asks, then pointers. */\n items: readonly SetupChecklistItem[];\n /** Open tracked rows. */\n openCount: number;\n /** Rows that count toward completion. */\n trackedCount: number;\n /** True when every tracked row is done. */\n complete: boolean;\n}\n\nexport interface SetupChecklistInput {\n readiness: WorkspaceIntegrationReadiness;\n installationRunners: 'managed' | 'none';\n workspaceRunnerCapacity: boolean;\n modelProvider: {installationProvided: boolean; configured: boolean};\n membership: {memberCount: number; pendingInvitationCount: number};\n}\n\nconst TOOLS_TITLE = 'Connect your tools';\nconst TOOLS_PURPOSE =\n 'Connect issue tracking, messaging, observability, or any Shipfox integration so workflows can react to events and agents can use them';\nconst RUNNER_PURPOSE = 'Jobs wait in `pending` until a runner is online';\nconst MODEL_PROVIDER_PURPOSE = 'Agent steps need a model provider to run';\nconst FIRST_WORKFLOW_PURPOSE =\n 'Add a workflow file under `.shipfox/workflows/` and Shipfox picks it up on the next push';\nconst TEAMMATES_PURPOSE = 'Everyone in the workspace can edit workflows and see runs';\n\n/**\n * Derives the workspace setup checklist from integration readiness and the\n * runner, model-provider, and membership facts. Rows follow the spec order;\n * the runner and model-provider rows exist only when the installation does\n * not already provide the capability.\n */\nexport function deriveSetupChecklist({\n readiness,\n installationRunners,\n workspaceRunnerCapacity,\n modelProvider,\n membership,\n}: SetupChecklistInput): SetupChecklist {\n const items: SetupChecklistItem[] = [\n {id: 'source-control', title: 'Connect source control', status: 'done', tracked: true},\n {id: 'project', title: 'Create a project', status: 'done', tracked: true},\n {\n id: 'tools',\n title: toolsTitle(readiness),\n status: readiness.hasToolIntegration ? 'done' : 'open',\n tracked: true,\n purpose: TOOLS_PURPOSE,\n action: {label: 'Connect', href: '/settings/integrations'},\n },\n ];\n\n if (installationRunners === 'none') {\n items.push({\n id: 'runner',\n title: 'Set up runner capacity',\n status: workspaceRunnerCapacity ? 'done' : 'open',\n tracked: true,\n purpose: RUNNER_PURPOSE,\n action: {label: 'Set up', href: '/settings/runners'},\n });\n }\n\n if (!modelProvider.installationProvided) {\n items.push({\n id: 'model-provider',\n title: 'Configure a model provider',\n status: modelProvider.configured ? 'done' : 'open',\n tracked: true,\n purpose: MODEL_PROVIDER_PURPOSE,\n action: {label: 'Configure', href: '/settings/agents'},\n });\n }\n\n items.push(\n {\n id: 'first-workflow',\n title: 'Push your first workflow',\n status: 'info',\n tracked: false,\n purpose: FIRST_WORKFLOW_PURPOSE,\n action: {label: 'Read the quickstart', href: '/docs/getting-started'},\n },\n {\n id: 'teammates',\n title: 'Invite your teammates',\n status:\n membership.memberCount >= 2 || membership.pendingInvitationCount >= 1 ? 'done' : 'info',\n tracked: false,\n purpose: TEAMMATES_PURPOSE,\n action: {label: 'Invite', href: '/settings/members'},\n },\n );\n\n const trackedItems = items.filter((item) => item.tracked);\n const openCount = trackedItems.filter((item) => item.status === 'open').length;\n\n return {\n items,\n openCount,\n trackedCount: trackedItems.length,\n complete: openCount === 0,\n };\n}\n\nfunction toolsTitle(readiness: WorkspaceIntegrationReadiness): string {\n if (!readiness.hasToolIntegration) {\n const attentionToolProviders = readiness.attentionProviders\n .map((providerKey) => readiness.providers.find(({provider}) => provider === providerKey))\n .filter(\n (provider) => provider !== undefined && !provider.capabilities.includes('source_control'),\n );\n if (attentionToolProviders.length === 1) {\n const provider = attentionToolProviders[0];\n if (provider !== undefined) {\n return `${provider.displayName || provider.provider} needs attention`;\n }\n }\n if (attentionToolProviders.length > 1) {\n return `${attentionToolProviders.length} integrations need attention`;\n }\n }\n return TOOLS_TITLE;\n}\n"],"names":["TOOLS_TITLE","TOOLS_PURPOSE","RUNNER_PURPOSE","MODEL_PROVIDER_PURPOSE","FIRST_WORKFLOW_PURPOSE","TEAMMATES_PURPOSE","deriveSetupChecklist","readiness","installationRunners","workspaceRunnerCapacity","modelProvider","membership","items","id","title","status","tracked","toolsTitle","hasToolIntegration","purpose","action","label","href","push","installationProvided","configured","memberCount","pendingInvitationCount","trackedItems","filter","item","openCount","length","trackedCount","complete","attentionToolProviders","attentionProviders","map","providerKey","providers","find","provider","undefined","capabilities","includes","displayName"],"mappings":"AAiDA,MAAMA,cAAc;AACpB,MAAMC,gBACJ;AACF,MAAMC,iBAAiB;AACvB,MAAMC,yBAAyB;AAC/B,MAAMC,yBACJ;AACF,MAAMC,oBAAoB;AAE1B;;;;;CAKC,GACD,OAAO,SAASC,qBAAqB,EACnCC,SAAS,EACTC,mBAAmB,EACnBC,uBAAuB,EACvBC,aAAa,EACbC,UAAU,EACU;IACpB,MAAMC,QAA8B;QAClC;YAACC,IAAI;YAAkBC,OAAO;YAA0BC,QAAQ;YAAQC,SAAS;QAAI;QACrF;YAACH,IAAI;YAAWC,OAAO;YAAoBC,QAAQ;YAAQC,SAAS;QAAI;QACxE;YACEH,IAAI;YACJC,OAAOG,WAAWV;YAClBQ,QAAQR,UAAUW,kBAAkB,GAAG,SAAS;YAChDF,SAAS;YACTG,SAASlB;YACTmB,QAAQ;gBAACC,OAAO;gBAAWC,MAAM;YAAwB;QAC3D;KACD;IAED,IAAId,wBAAwB,QAAQ;QAClCI,MAAMW,IAAI,CAAC;YACTV,IAAI;YACJC,OAAO;YACPC,QAAQN,0BAA0B,SAAS;YAC3CO,SAAS;YACTG,SAASjB;YACTkB,QAAQ;gBAACC,OAAO;gBAAUC,MAAM;YAAmB;QACrD;IACF;IAEA,IAAI,CAACZ,cAAcc,oBAAoB,EAAE;QACvCZ,MAAMW,IAAI,CAAC;YACTV,IAAI;YACJC,OAAO;YACPC,QAAQL,cAAce,UAAU,GAAG,SAAS;YAC5CT,SAAS;YACTG,SAAShB;YACTiB,QAAQ;gBAACC,OAAO;gBAAaC,MAAM;YAAkB;QACvD;IACF;IAEAV,MAAMW,IAAI,CACR;QACEV,IAAI;QACJC,OAAO;QACPC,QAAQ;QACRC,SAAS;QACTG,SAASf;QACTgB,QAAQ;YAACC,OAAO;YAAuBC,MAAM;QAAuB;IACtE,GACA;QACET,IAAI;QACJC,OAAO;QACPC,QACEJ,WAAWe,WAAW,IAAI,KAAKf,WAAWgB,sBAAsB,IAAI,IAAI,SAAS;QACnFX,SAAS;QACTG,SAASd;QACTe,QAAQ;YAACC,OAAO;YAAUC,MAAM;QAAmB;IACrD;IAGF,MAAMM,eAAehB,MAAMiB,MAAM,CAAC,CAACC,OAASA,KAAKd,OAAO;IACxD,MAAMe,YAAYH,aAAaC,MAAM,CAAC,CAACC,OAASA,KAAKf,MAAM,KAAK,QAAQiB,MAAM;IAE9E,OAAO;QACLpB;QACAmB;QACAE,cAAcL,aAAaI,MAAM;QACjCE,UAAUH,cAAc;IAC1B;AACF;AAEA,SAASd,WAAWV,SAAwC;IAC1D,IAAI,CAACA,UAAUW,kBAAkB,EAAE;QACjC,MAAMiB,yBAAyB5B,UAAU6B,kBAAkB,CACxDC,GAAG,CAAC,CAACC,cAAgB/B,UAAUgC,SAAS,CAACC,IAAI,CAAC,CAAC,EAACC,QAAQ,EAAC,GAAKA,aAAaH,cAC3ET,MAAM,CACL,CAACY,WAAaA,aAAaC,aAAa,CAACD,SAASE,YAAY,CAACC,QAAQ,CAAC;QAE5E,IAAIT,uBAAuBH,MAAM,KAAK,GAAG;YACvC,MAAMS,WAAWN,sBAAsB,CAAC,EAAE;YAC1C,IAAIM,aAAaC,WAAW;gBAC1B,OAAO,GAAGD,SAASI,WAAW,IAAIJ,SAASA,QAAQ,CAAC,gBAAgB,CAAC;YACvE;QACF;QACA,IAAIN,uBAAuBH,MAAM,GAAG,GAAG;YACrC,OAAO,GAAGG,uBAAuBH,MAAM,CAAC,4BAA4B,CAAC;QACvE;IACF;IACA,OAAOhC;AACT"}
1
+ {"version":3,"sources":["../../src/core/setup-checklist.ts"],"sourcesContent":["import type {WorkspaceIntegrationReadiness} from './integration-readiness.js';\n\nexport type SetupChecklistItemStatus = 'done' | 'open' | 'info';\n\nexport type SetupChecklistItemId =\n | 'source-control'\n | 'project'\n | 'tools'\n | 'runner'\n | 'model-provider'\n | 'first-workflow'\n | 'teammates';\n\nexport interface SetupChecklistAction {\n label: string;\n href: string;\n}\n\nexport interface SetupChecklistItem {\n id: SetupChecklistItemId;\n title: string;\n status: SetupChecklistItemStatus;\n /** Tracked items count toward completion; pointers never do. */\n tracked: boolean;\n /** The row's underlying integration has a connection that needs attention. */\n attention?: boolean;\n /** One line of purpose, rendered for open and pointer rows. */\n purpose?: string;\n /** Primary action, a link to the settings page where the work happens. */\n action?: SetupChecklistAction;\n}\n\nexport interface SetupChecklist {\n /** Rows in spec order: done progress, activation asks, then pointers. */\n items: readonly SetupChecklistItem[];\n /** Open tracked rows. */\n openCount: number;\n /** Rows that count toward completion. */\n trackedCount: number;\n /** True when every tracked row is done. */\n complete: boolean;\n}\n\nexport interface SetupChecklistInput {\n readiness: WorkspaceIntegrationReadiness;\n installationRunners: 'managed' | 'none';\n workspaceRunnerCapacity: boolean;\n modelProvider: {installationProvided: boolean; configured: boolean};\n membership: {memberCount: number; pendingInvitationCount: number};\n}\n\nconst TOOLS_TITLE = 'Connect your tools';\nconst TOOLS_PURPOSE =\n 'Connect issue tracking, messaging, observability, or any Shipfox integration';\nconst RUNNER_PURPOSE = 'Jobs wait in `pending` until a runner is online';\nconst MODEL_PROVIDER_PURPOSE = 'Agent steps need a model provider to run';\nconst FIRST_WORKFLOW_PURPOSE =\n 'Add a workflow file under `.shipfox/workflows/` and Shipfox picks it up on the next push';\nconst TEAMMATES_PURPOSE = 'Everyone in the workspace can edit workflows and see runs';\n\n/**\n * Derives the workspace setup checklist from integration readiness and the\n * runner, model-provider, and membership facts. Rows follow the spec order;\n * the runner and model-provider rows exist only when the installation does\n * not already provide the capability.\n */\nexport function deriveSetupChecklist({\n readiness,\n installationRunners,\n workspaceRunnerCapacity,\n modelProvider,\n membership,\n}: SetupChecklistInput): SetupChecklist {\n const toolsAttention =\n !readiness.hasToolIntegration && attentionToolProviders(readiness).length > 0;\n const items: SetupChecklistItem[] = [\n {id: 'source-control', title: 'Connect source control', status: 'done', tracked: true},\n {id: 'project', title: 'Create a project', status: 'done', tracked: true},\n {\n id: 'tools',\n title: toolsTitle(readiness),\n status: readiness.hasToolIntegration ? 'done' : 'open',\n tracked: true,\n attention: toolsAttention,\n purpose: TOOLS_PURPOSE,\n action: {label: 'Connect', href: '/settings/integrations'},\n },\n ];\n\n if (installationRunners === 'none') {\n items.push({\n id: 'runner',\n title: 'Set up runner capacity',\n status: workspaceRunnerCapacity ? 'done' : 'open',\n tracked: true,\n purpose: RUNNER_PURPOSE,\n action: {label: 'Set up', href: '/settings/runners'},\n });\n }\n\n if (!modelProvider.installationProvided) {\n items.push({\n id: 'model-provider',\n title: 'Configure a model provider',\n status: modelProvider.configured ? 'done' : 'open',\n tracked: true,\n purpose: MODEL_PROVIDER_PURPOSE,\n action: {label: 'Configure', href: '/settings/agents'},\n });\n }\n\n items.push(\n {\n id: 'first-workflow',\n title: 'Push your first workflow',\n status: 'info',\n tracked: false,\n purpose: FIRST_WORKFLOW_PURPOSE,\n action: {label: 'Read the quickstart', href: '/docs/getting-started'},\n },\n {\n id: 'teammates',\n title: 'Invite your teammates',\n status:\n membership.memberCount >= 2 || membership.pendingInvitationCount >= 1 ? 'done' : 'info',\n tracked: false,\n purpose: TEAMMATES_PURPOSE,\n action: {label: 'Invite', href: '/settings/members'},\n },\n );\n\n const trackedItems = items.filter((item) => item.tracked);\n const openCount = trackedItems.filter((item) => item.status === 'open').length;\n\n return {\n items,\n openCount,\n trackedCount: trackedItems.length,\n complete: openCount === 0,\n };\n}\n\nfunction toolsTitle(readiness: WorkspaceIntegrationReadiness): string {\n if (!readiness.hasToolIntegration) {\n const providers = attentionToolProviders(readiness);\n if (providers.length === 1) {\n const provider = providers[0];\n if (provider !== undefined) {\n return `${provider.displayName || provider.provider} needs attention`;\n }\n }\n if (providers.length > 1) {\n return `${providers.length} integrations need attention`;\n }\n }\n return TOOLS_TITLE;\n}\n\nfunction attentionToolProviders(readiness: WorkspaceIntegrationReadiness) {\n return readiness.attentionProviders\n .map((providerKey) => readiness.providers.find(({provider}) => provider === providerKey))\n .filter(\n (provider) => provider !== undefined && !provider.capabilities.includes('source_control'),\n );\n}\n"],"names":["TOOLS_TITLE","TOOLS_PURPOSE","RUNNER_PURPOSE","MODEL_PROVIDER_PURPOSE","FIRST_WORKFLOW_PURPOSE","TEAMMATES_PURPOSE","deriveSetupChecklist","readiness","installationRunners","workspaceRunnerCapacity","modelProvider","membership","toolsAttention","hasToolIntegration","attentionToolProviders","length","items","id","title","status","tracked","toolsTitle","attention","purpose","action","label","href","push","installationProvided","configured","memberCount","pendingInvitationCount","trackedItems","filter","item","openCount","trackedCount","complete","providers","provider","undefined","displayName","attentionProviders","map","providerKey","find","capabilities","includes"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare const onboardingFeature: {
2
+ readonly id: "shipfox.onboarding";
3
+ };
4
+ export declare const WorkspaceSetupChecklist: import("react").LazyExoticComponent<typeof import("./index.js").WorkspaceSetupChecklist>;
5
+ export declare const WorkspaceSetupIndicator: import("react").LazyExoticComponent<typeof import("./index.js").WorkspaceSetupIndicator>;
6
+ //# sourceMappingURL=feature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../src/feature.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB;;CAE5B,CAAC;AAEH,eAAO,MAAM,uBAAuB,0FAGlC,CAAC;AAEH,eAAO,MAAM,uBAAuB,0FAGlC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { defineClientFeature } from '@shipfox/client-shell';
2
+ import { lazy } from 'react';
3
+ export const onboardingFeature = defineClientFeature({
4
+ id: 'shipfox.onboarding'
5
+ });
6
+ export const WorkspaceSetupChecklist = lazy(async ()=>{
7
+ const module = await import('./components/setup-checklist.js');
8
+ return {
9
+ default: module.WorkspaceSetupChecklist
10
+ };
11
+ });
12
+ export const WorkspaceSetupIndicator = lazy(async ()=>{
13
+ const module = await import('./components/setup-checklist.js');
14
+ return {
15
+ default: module.WorkspaceSetupIndicator
16
+ };
17
+ });
18
+
19
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/feature.ts"],"sourcesContent":["import {defineClientFeature} from '@shipfox/client-shell';\nimport {lazy} from 'react';\n\nexport const onboardingFeature = defineClientFeature({\n id: 'shipfox.onboarding',\n});\n\nexport const WorkspaceSetupChecklist = lazy(async () => {\n const module = await import('./components/setup-checklist.js');\n return {default: module.WorkspaceSetupChecklist};\n});\n\nexport const WorkspaceSetupIndicator = lazy(async () => {\n const module = await import('./components/setup-checklist.js');\n return {default: module.WorkspaceSetupIndicator};\n});\n"],"names":["defineClientFeature","lazy","onboardingFeature","id","WorkspaceSetupChecklist","module","default","WorkspaceSetupIndicator"],"mappings":"AAAA,SAAQA,mBAAmB,QAAO,wBAAwB;AAC1D,SAAQC,IAAI,QAAO,QAAQ;AAE3B,OAAO,MAAMC,oBAAoBF,oBAAoB;IACnDG,IAAI;AACN,GAAG;AAEH,OAAO,MAAMC,0BAA0BH,KAAK;IAC1C,MAAMI,SAAS,MAAM,MAAM,CAAC;IAC5B,OAAO;QAACC,SAASD,OAAOD,uBAAuB;IAAA;AACjD,GAAG;AAEH,OAAO,MAAMG,0BAA0BN,KAAK;IAC1C,MAAMI,SAAS,MAAM,MAAM,CAAC;IAC5B,OAAO;QAACC,SAASD,OAAOE,uBAAuB;IAAA;AACjD,GAAG"}
@@ -0,0 +1,13 @@
1
+ import { type SetupChecklist } from '#core/setup-checklist.js';
2
+ export interface ChecklistQueryState {
3
+ checklist: SetupChecklist;
4
+ baseSettled: boolean;
5
+ completionReady: boolean;
6
+ }
7
+ /**
8
+ * Composes the server state needed by both checklist hosts. Keeping this hook
9
+ * in the API adapter boundary gives each query family one shared cache policy
10
+ * while allowing dismissed hosts to remain unsubscribed.
11
+ */
12
+ export declare function useSetupChecklistQueryState(workspaceId: string, subscribed: boolean): ChecklistQueryState;
13
+ //# sourceMappingURL=setup-checklist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-checklist.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/setup-checklist.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,0BAA0B,CAAC;AAIlC,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,OAAO,GAClB,mBAAmB,CAmGrB"}
@@ -0,0 +1,103 @@
1
+ import { modelProviderCatalogQueryOptions, modelProviderConfigsQueryOptions } from '@shipfox/client-agent';
2
+ import { integrationConnectionsQueryOptions, integrationProvidersQueryOptions } from '@shipfox/client-integrations';
3
+ import { activeProvisionersQueryOptions, installationRunnersStatusQueryOptions } from '@shipfox/client-runners';
4
+ import { listInvitationsQueryOptions, listMembersQueryOptions } from '@shipfox/client-workspace-settings';
5
+ import { useQuery } from '@tanstack/react-query';
6
+ import { deriveIntegrationReadiness } from '#core/integration-readiness.js';
7
+ import { deriveSetupChecklist } from '#core/setup-checklist.js';
8
+ const CHECKLIST_STALE_TIME_MS = 5 * 60 * 1000;
9
+ /**
10
+ * Composes the server state needed by both checklist hosts. Keeping this hook
11
+ * in the API adapter boundary gives each query family one shared cache policy
12
+ * while allowing dismissed hosts to remain unsubscribed.
13
+ */ export function useSetupChecklistQueryState(workspaceId, subscribed) {
14
+ const queryEnabled = subscribed && Boolean(workspaceId);
15
+ const queryPolicy = {
16
+ enabled: queryEnabled,
17
+ subscribed: queryEnabled,
18
+ refetchInterval: false,
19
+ retry: false,
20
+ staleTime: CHECKLIST_STALE_TIME_MS,
21
+ refetchOnWindowFocus: false
22
+ };
23
+ const providersQuery = useQuery({
24
+ ...integrationProvidersQueryOptions(),
25
+ ...queryPolicy
26
+ });
27
+ const connectionsQuery = useQuery({
28
+ ...integrationConnectionsQueryOptions(workspaceId),
29
+ ...queryPolicy
30
+ });
31
+ const activeProvisionersQuery = useQuery({
32
+ ...activeProvisionersQueryOptions(workspaceId),
33
+ ...queryPolicy
34
+ });
35
+ const runnersStatusQuery = useQuery({
36
+ ...installationRunnersStatusQueryOptions(workspaceId),
37
+ ...queryPolicy
38
+ });
39
+ const catalogQuery = useQuery({
40
+ ...modelProviderCatalogQueryOptions(),
41
+ ...queryPolicy
42
+ });
43
+ const configsQuery = useQuery({
44
+ ...modelProviderConfigsQueryOptions(workspaceId),
45
+ ...queryPolicy
46
+ });
47
+ const membersQuery = useQuery({
48
+ ...listMembersQueryOptions(workspaceId),
49
+ ...queryPolicy
50
+ });
51
+ const invitationsQuery = useQuery({
52
+ ...listInvitationsQueryOptions(workspaceId),
53
+ ...queryPolicy
54
+ });
55
+ const baseSettled = isSettled(providersQuery) && isSettled(connectionsQuery);
56
+ const runnerSettled = isSettled(activeProvisionersQuery) && isSettled(runnersStatusQuery);
57
+ const runnerReady = activeProvisionersQuery.isSuccess && runnersStatusQuery.isSuccess;
58
+ const catalogInstallationProvided = catalogQuery.data !== undefined && (catalogQuery.data.managedProviderId !== null || catalogQuery.data.instanceDefaultProviderId !== null);
59
+ const modelReady = catalogQuery.isSuccess && configsQuery.isSuccess;
60
+ const membersReady = membersQuery.isSuccess && invitationsQuery.isSuccess;
61
+ const modelSettled = isSettled(catalogQuery) && isSettled(configsQuery);
62
+ const membersSettled = isSettled(membersQuery) && isSettled(invitationsQuery);
63
+ const completionReady = providersQuery.isSuccess && connectionsQuery.isSuccess && runnerSettled && modelSettled && membersSettled;
64
+ const rawChecklist = deriveSetupChecklist({
65
+ readiness: deriveIntegrationReadiness({
66
+ providers: providersQuery.data ?? [],
67
+ connections: connectionsQuery.data ?? []
68
+ }),
69
+ installationRunners: runnersStatusQuery.data ?? 'managed',
70
+ workspaceRunnerCapacity: (activeProvisionersQuery.data?.length ?? 0) > 0,
71
+ modelProvider: {
72
+ installationProvided: catalogQuery.isSuccess ? catalogInstallationProvided : true,
73
+ configured: (configsQuery.data?.configs.length ?? 0) > 0
74
+ },
75
+ membership: {
76
+ memberCount: membersQuery.data?.length ?? 0,
77
+ pendingInvitationCount: invitationsQuery.data?.length ?? 0
78
+ }
79
+ });
80
+ const hiddenRows = new Set();
81
+ if (!providersQuery.isSuccess || !connectionsQuery.isSuccess) hiddenRows.add('tools');
82
+ if (!runnerReady) hiddenRows.add('runner');
83
+ if (!modelReady) hiddenRows.add('model-provider');
84
+ if (!membersReady) hiddenRows.add('teammates');
85
+ const items = rawChecklist.items.filter((item)=>!hiddenRows.has(item.id));
86
+ const trackedItems = items.filter((item)=>item.tracked);
87
+ const openCount = trackedItems.filter((item)=>item.status === 'open').length;
88
+ return {
89
+ baseSettled,
90
+ completionReady,
91
+ checklist: {
92
+ items,
93
+ openCount,
94
+ trackedCount: trackedItems.length,
95
+ complete: completionReady && openCount === 0
96
+ }
97
+ };
98
+ }
99
+ function isSettled(query) {
100
+ return query.isSuccess || query.isError;
101
+ }
102
+
103
+ //# sourceMappingURL=setup-checklist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/api/setup-checklist.ts"],"sourcesContent":["import {\n modelProviderCatalogQueryOptions,\n modelProviderConfigsQueryOptions,\n} from '@shipfox/client-agent';\nimport {\n integrationConnectionsQueryOptions,\n integrationProvidersQueryOptions,\n} from '@shipfox/client-integrations';\nimport {\n activeProvisionersQueryOptions,\n installationRunnersStatusQueryOptions,\n} from '@shipfox/client-runners';\nimport {\n listInvitationsQueryOptions,\n listMembersQueryOptions,\n} from '@shipfox/client-workspace-settings';\nimport {useQuery} from '@tanstack/react-query';\nimport {deriveIntegrationReadiness} from '#core/integration-readiness.js';\nimport {\n deriveSetupChecklist,\n type SetupChecklist,\n type SetupChecklistItemId,\n} from '#core/setup-checklist.js';\n\nconst CHECKLIST_STALE_TIME_MS = 5 * 60 * 1000;\n\nexport interface ChecklistQueryState {\n checklist: SetupChecklist;\n baseSettled: boolean;\n completionReady: boolean;\n}\n\n/**\n * Composes the server state needed by both checklist hosts. Keeping this hook\n * in the API adapter boundary gives each query family one shared cache policy\n * while allowing dismissed hosts to remain unsubscribed.\n */\nexport function useSetupChecklistQueryState(\n workspaceId: string,\n subscribed: boolean,\n): ChecklistQueryState {\n const queryEnabled = subscribed && Boolean(workspaceId);\n const queryPolicy = {\n enabled: queryEnabled,\n subscribed: queryEnabled,\n refetchInterval: false,\n retry: false,\n staleTime: CHECKLIST_STALE_TIME_MS,\n refetchOnWindowFocus: false,\n } as const;\n\n const providersQuery = useQuery({\n ...integrationProvidersQueryOptions(),\n ...queryPolicy,\n });\n const connectionsQuery = useQuery({\n ...integrationConnectionsQueryOptions(workspaceId),\n ...queryPolicy,\n });\n const activeProvisionersQuery = useQuery({\n ...activeProvisionersQueryOptions(workspaceId),\n ...queryPolicy,\n });\n const runnersStatusQuery = useQuery({\n ...installationRunnersStatusQueryOptions(workspaceId),\n ...queryPolicy,\n });\n const catalogQuery = useQuery({\n ...modelProviderCatalogQueryOptions(),\n ...queryPolicy,\n });\n const configsQuery = useQuery({\n ...modelProviderConfigsQueryOptions(workspaceId),\n ...queryPolicy,\n });\n const membersQuery = useQuery({\n ...listMembersQueryOptions(workspaceId),\n ...queryPolicy,\n });\n const invitationsQuery = useQuery({\n ...listInvitationsQueryOptions(workspaceId),\n ...queryPolicy,\n });\n\n const baseSettled = isSettled(providersQuery) && isSettled(connectionsQuery);\n const runnerSettled = isSettled(activeProvisionersQuery) && isSettled(runnersStatusQuery);\n const runnerReady = activeProvisionersQuery.isSuccess && runnersStatusQuery.isSuccess;\n const catalogInstallationProvided =\n catalogQuery.data !== undefined &&\n (catalogQuery.data.managedProviderId !== null ||\n catalogQuery.data.instanceDefaultProviderId !== null);\n const modelReady = catalogQuery.isSuccess && configsQuery.isSuccess;\n const membersReady = membersQuery.isSuccess && invitationsQuery.isSuccess;\n const modelSettled = isSettled(catalogQuery) && isSettled(configsQuery);\n const membersSettled = isSettled(membersQuery) && isSettled(invitationsQuery);\n const completionReady =\n providersQuery.isSuccess &&\n connectionsQuery.isSuccess &&\n runnerSettled &&\n modelSettled &&\n membersSettled;\n\n const rawChecklist = deriveSetupChecklist({\n readiness: deriveIntegrationReadiness({\n providers: providersQuery.data ?? [],\n connections: connectionsQuery.data ?? [],\n }),\n installationRunners: runnersStatusQuery.data ?? 'managed',\n workspaceRunnerCapacity: (activeProvisionersQuery.data?.length ?? 0) > 0,\n modelProvider: {\n installationProvided: catalogQuery.isSuccess ? catalogInstallationProvided : true,\n configured: (configsQuery.data?.configs.length ?? 0) > 0,\n },\n membership: {\n memberCount: membersQuery.data?.length ?? 0,\n pendingInvitationCount: invitationsQuery.data?.length ?? 0,\n },\n });\n\n const hiddenRows = new Set<SetupChecklistItemId>();\n if (!providersQuery.isSuccess || !connectionsQuery.isSuccess) hiddenRows.add('tools');\n if (!runnerReady) hiddenRows.add('runner');\n if (!modelReady) hiddenRows.add('model-provider');\n if (!membersReady) hiddenRows.add('teammates');\n\n const items = rawChecklist.items.filter((item) => !hiddenRows.has(item.id));\n const trackedItems = items.filter((item) => item.tracked);\n const openCount = trackedItems.filter((item) => item.status === 'open').length;\n\n return {\n baseSettled,\n completionReady,\n checklist: {\n items,\n openCount,\n trackedCount: trackedItems.length,\n complete: completionReady && openCount === 0,\n },\n };\n}\n\nfunction isSettled(query: {isError: boolean; isSuccess: boolean}) {\n return query.isSuccess || query.isError;\n}\n"],"names":["modelProviderCatalogQueryOptions","modelProviderConfigsQueryOptions","integrationConnectionsQueryOptions","integrationProvidersQueryOptions","activeProvisionersQueryOptions","installationRunnersStatusQueryOptions","listInvitationsQueryOptions","listMembersQueryOptions","useQuery","deriveIntegrationReadiness","deriveSetupChecklist","CHECKLIST_STALE_TIME_MS","useSetupChecklistQueryState","workspaceId","subscribed","queryEnabled","Boolean","queryPolicy","enabled","refetchInterval","retry","staleTime","refetchOnWindowFocus","providersQuery","connectionsQuery","activeProvisionersQuery","runnersStatusQuery","catalogQuery","configsQuery","membersQuery","invitationsQuery","baseSettled","isSettled","runnerSettled","runnerReady","isSuccess","catalogInstallationProvided","data","undefined","managedProviderId","instanceDefaultProviderId","modelReady","membersReady","modelSettled","membersSettled","completionReady","rawChecklist","readiness","providers","connections","installationRunners","workspaceRunnerCapacity","length","modelProvider","installationProvided","configured","configs","membership","memberCount","pendingInvitationCount","hiddenRows","Set","add","items","filter","item","has","id","trackedItems","tracked","openCount","status","checklist","trackedCount","complete","query","isError"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ChecklistQueryState } from '#hooks/api/setup-checklist.js';
2
+ type ChecklistHost = 'panel' | 'popover';
3
+ export declare function useCompletionTransition(queryState: ChecklistQueryState, host: ChecklistHost, onCompleted?: (completed: boolean) => void): boolean;
4
+ export declare function useShownAnalytics(host: ChecklistHost, visible: boolean): void;
5
+ export {};
6
+ //# sourceMappingURL=use-checklist-analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-checklist-analytics.d.ts","sourceRoot":"","sources":["../../src/hooks/use-checklist-analytics.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,+BAA+B,CAAC;AAEvE,KAAK,aAAa,GAAG,OAAO,GAAG,SAAS,CAAC;AAEzC,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,mBAAmB,EAC/B,IAAI,EAAE,aAAa,EACnB,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,WAuC3C;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,QAStE"}
@@ -0,0 +1,51 @@
1
+ import { useClientAnalytics } from '@shipfox/client-shell/runtime';
2
+ import { useEffect, useRef, useState } from 'react';
3
+ export function useCompletionTransition(queryState, host, onCompleted) {
4
+ const analytics = useClientAnalytics();
5
+ const observedIncomplete = useRef(false);
6
+ const completionHandled = useRef(false);
7
+ const [showCompletion, setShowCompletion] = useState(false);
8
+ useEffect(()=>{
9
+ if (queryState.baseSettled && !queryState.checklist.complete && queryState.checklist.openCount > 0) {
10
+ observedIncomplete.current = true;
11
+ }
12
+ if (!queryState.completionReady) return;
13
+ if (!queryState.checklist.complete) {
14
+ setShowCompletion(false);
15
+ return;
16
+ }
17
+ if (!observedIncomplete.current || completionHandled.current) return;
18
+ completionHandled.current = true;
19
+ setShowCompletion(true);
20
+ onCompleted?.(true);
21
+ analytics.capture('onboarding_checklist_completed', {
22
+ host
23
+ });
24
+ }, [
25
+ analytics,
26
+ host,
27
+ onCompleted,
28
+ queryState.baseSettled,
29
+ queryState.checklist.complete,
30
+ queryState.checklist.openCount,
31
+ queryState.completionReady
32
+ ]);
33
+ return showCompletion;
34
+ }
35
+ export function useShownAnalytics(host, visible) {
36
+ const analytics = useClientAnalytics();
37
+ const shown = useRef(false);
38
+ useEffect(()=>{
39
+ if (!visible || shown.current) return;
40
+ shown.current = true;
41
+ analytics.capture('onboarding_checklist_shown', {
42
+ host
43
+ });
44
+ }, [
45
+ analytics,
46
+ host,
47
+ visible
48
+ ]);
49
+ }
50
+
51
+ //# sourceMappingURL=use-checklist-analytics.js.map