@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +28 -0
- package/README.md +26 -7
- package/dist/components/setup-checklist-body.d.ts +3 -0
- package/dist/components/setup-checklist-body.d.ts.map +1 -0
- package/dist/components/setup-checklist-body.js +25 -0
- package/dist/components/setup-checklist-body.js.map +1 -0
- package/dist/components/setup-checklist-completion.d.ts +6 -0
- package/dist/components/setup-checklist-completion.d.ts.map +1 -0
- package/dist/components/setup-checklist-completion.js +156 -0
- package/dist/components/setup-checklist-completion.js.map +1 -0
- package/dist/components/setup-checklist-confetti.d.ts +13 -0
- package/dist/components/setup-checklist-confetti.d.ts.map +1 -0
- package/dist/components/setup-checklist-confetti.js +24 -0
- package/dist/components/setup-checklist-confetti.js.map +1 -0
- package/dist/components/setup-checklist-host-primitives.d.ts +11 -0
- package/dist/components/setup-checklist-host-primitives.d.ts.map +1 -0
- package/dist/components/setup-checklist-host-primitives.js +71 -0
- package/dist/components/setup-checklist-host-primitives.js.map +1 -0
- package/dist/components/setup-checklist-row.d.ts +7 -0
- package/dist/components/setup-checklist-row.d.ts.map +1 -0
- package/dist/components/setup-checklist-row.js +177 -0
- package/dist/components/setup-checklist-row.js.map +1 -0
- package/dist/components/setup-checklist-types.d.ts +19 -0
- package/dist/components/setup-checklist-types.d.ts.map +1 -0
- package/dist/components/setup-checklist-types.js +3 -0
- package/dist/components/setup-checklist-types.js.map +1 -0
- package/dist/components/setup-checklist.d.ts +5 -0
- package/dist/components/setup-checklist.d.ts.map +1 -0
- package/dist/components/setup-checklist.js +5 -0
- package/dist/components/setup-checklist.js.map +1 -0
- package/dist/components/setup-checklist.stories.d.ts +19 -0
- package/dist/components/setup-checklist.stories.d.ts.map +1 -0
- package/dist/components/setup-checklist.stories.js +305 -0
- package/dist/components/setup-checklist.stories.js.map +1 -0
- package/dist/components/workspace-setup-checklist.d.ts +3 -0
- package/dist/components/workspace-setup-checklist.d.ts.map +1 -0
- package/dist/components/workspace-setup-checklist.js +80 -0
- package/dist/components/workspace-setup-checklist.js.map +1 -0
- package/dist/components/workspace-setup-indicator.d.ts +3 -0
- package/dist/components/workspace-setup-indicator.d.ts.map +1 -0
- package/dist/components/workspace-setup-indicator.js +133 -0
- package/dist/components/workspace-setup-indicator.js.map +1 -0
- package/dist/core/setup-checklist.d.ts +2 -0
- package/dist/core/setup-checklist.d.ts.map +1 -1
- package/dist/core/setup-checklist.js +11 -6
- package/dist/core/setup-checklist.js.map +1 -1
- package/dist/feature.d.ts +6 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/feature.js +19 -0
- package/dist/feature.js.map +1 -0
- package/dist/hooks/api/setup-checklist.d.ts +13 -0
- package/dist/hooks/api/setup-checklist.d.ts.map +1 -0
- package/dist/hooks/api/setup-checklist.js +103 -0
- package/dist/hooks/api/setup-checklist.js.map +1 -0
- package/dist/hooks/use-checklist-analytics.d.ts +6 -0
- package/dist/hooks/use-checklist-analytics.d.ts.map +1 -0
- package/dist/hooks/use-checklist-analytics.js +51 -0
- package/dist/hooks/use-checklist-analytics.js.map +1 -0
- package/dist/hooks/use-checklist-dismissal.d.ts +5 -0
- package/dist/hooks/use-checklist-dismissal.d.ts.map +1 -0
- package/dist/hooks/use-checklist-dismissal.js +31 -0
- package/dist/hooks/use-checklist-dismissal.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +12 -5
- package/src/components/setup-checklist-body.tsx +35 -0
- package/src/components/setup-checklist-completion.test.tsx +124 -0
- package/src/components/setup-checklist-completion.tsx +165 -0
- package/src/components/setup-checklist-confetti.test.ts +19 -0
- package/src/components/setup-checklist-confetti.ts +39 -0
- package/src/components/setup-checklist-host-primitives.tsx +65 -0
- package/src/components/setup-checklist-row.tsx +189 -0
- package/src/components/setup-checklist-types.ts +21 -0
- package/src/components/setup-checklist.stories.tsx +281 -0
- package/src/components/setup-checklist.test.tsx +564 -0
- package/src/components/setup-checklist.tsx +8 -0
- package/src/components/workspace-setup-checklist.tsx +91 -0
- package/src/components/workspace-setup-indicator.tsx +126 -0
- package/src/core/setup-checklist.test.ts +2 -2
- package/src/core/setup-checklist.ts +19 -10
- package/src/feature.ts +16 -0
- package/src/hooks/api/setup-checklist.ts +144 -0
- package/src/hooks/use-checklist-analytics.ts +60 -0
- package/src/hooks/use-checklist-dismissal.ts +30 -0
- package/src/index.ts +8 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- 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 @@
|
|
|
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,
|
|
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
|
|
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
|
|
97
|
-
if (
|
|
98
|
-
const provider =
|
|
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 (
|
|
104
|
-
return `${
|
|
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
|
|
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"}
|
package/dist/feature.js
ADDED
|
@@ -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
|