Skip to content

Commit e2af205

Browse files
committed
add activatable status indicator
1 parent 0a38e96 commit e2af205

6 files changed

Lines changed: 121 additions & 53 deletions

File tree

packages/client/src/clients/guide/client.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,13 @@ const predicate = (
215215
return false;
216216
}
217217

218+
return checkActivatable(guide, location);
219+
};
220+
221+
export const checkActivatable = (
222+
guide: KnockGuide,
223+
location: string | undefined,
224+
) => {
218225
const url = location ? newUrl(location) : undefined;
219226

220227
const urlRules = guide.activation_url_rules || [];

packages/client/src/clients/guide/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
export { KnockGuideClient, DEBUG_QUERY_PARAMS } from "./client";
1+
export {
2+
KnockGuideClient,
3+
DEBUG_QUERY_PARAMS,
4+
checkActivatable,
5+
} from "./client";
26
export type {
37
KnockGuide,
48
KnockGuideStep,

packages/react/src/modules/guide/components/Toolbar/V2/GuideRow.tsx

Lines changed: 72 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { Button } from "@telegraph/button";
2-
import { Stack } from "@telegraph/layout";
2+
import { Box, Stack } from "@telegraph/layout";
33
import { Tag } from "@telegraph/tag";
44
import { Tooltip } from "@telegraph/tooltip";
55
import { Text } from "@telegraph/typography";
6-
import { CheckCircle2, CircleDashed, Eye, UserCircle2 } from "lucide-react";
6+
import {
7+
CheckCircle2,
8+
CircleDashed,
9+
Eye,
10+
LocateFixed,
11+
UserCircle2,
12+
} from "lucide-react";
713
import * as React from "react";
814

915
import { GuideHoverCard } from "./GuideHoverCard";
@@ -38,58 +44,81 @@ export const GuideRow = ({ guide, orderIndex }: Props) => {
3844
</GuideHoverCard>
3945
</Stack>
4046

41-
<Stack gap="1" justify="flex-end">
47+
<Stack justify="flex-end">
4248
{guide.__typename === "Guide" && (
43-
<>
49+
<Stack gap="1">
4450
<Tooltip
45-
label={
46-
!guide.inspection.targetable.status &&
47-
guide.inspection.targetable.message
48-
}
49-
enabled={!guide.inspection.targetable.status}
51+
label="Current location does not match the activation conditions"
52+
enabled={!guide.inspection.activatable.status}
5053
>
5154
<Button
5255
px="1"
5356
size="1"
5457
variant="soft"
55-
color={guide.inspection.targetable.status ? "green" : "red"}
56-
leadingIcon={{ icon: UserCircle2, alt: "Target" }}
58+
color={guide.inspection.activatable.status ? "green" : "red"}
59+
leadingIcon={{ icon: LocateFixed, alt: "Target" }}
5760
/>
5861
</Tooltip>
59-
<Tooltip
60-
label="User has already archived this guide"
61-
enabled={guide.inspection.archived.status}
62-
>
63-
<Button
64-
px="1"
65-
size="1"
66-
variant="soft"
67-
color={guide.inspection.archived.status ? "red" : "green"}
68-
leadingIcon={{ icon: Eye, alt: "Not archived" }}
69-
/>
70-
</Tooltip>
71-
</>
62+
</Stack>
7263
)}
73-
<Tooltip
74-
label={
75-
guide.__typename === "MissingGuide"
76-
? "This guide has never been committed and published yet"
77-
: "This guide is not active"
78-
}
79-
enabled={!guide.active}
80-
>
81-
<Button
82-
px="1"
83-
size="1"
84-
variant="soft"
85-
color={guide.active ? "green" : "red"}
86-
leadingIcon={
87-
guide.active
88-
? { icon: CheckCircle2, alt: "Active" }
89-
: { icon: CircleDashed, alt: "Inactive" }
64+
{guide.__typename === "Guide" && (
65+
<Stack px="1" align="center">
66+
<Box h="3" borderLeft="px" borderColor="gray-6" />
67+
</Stack>
68+
)}
69+
<Stack gap="1">
70+
{guide.__typename === "Guide" && (
71+
<>
72+
<Tooltip
73+
label={
74+
!guide.inspection.targetable.status &&
75+
guide.inspection.targetable.message
76+
}
77+
enabled={!guide.inspection.targetable.status}
78+
>
79+
<Button
80+
px="1"
81+
size="1"
82+
variant="soft"
83+
color={guide.inspection.targetable.status ? "green" : "red"}
84+
leadingIcon={{ icon: UserCircle2, alt: "Target" }}
85+
/>
86+
</Tooltip>
87+
<Tooltip
88+
label="User has already archived this guide"
89+
enabled={guide.inspection.archived.status}
90+
>
91+
<Button
92+
px="1"
93+
size="1"
94+
variant="soft"
95+
color={guide.inspection.archived.status ? "red" : "green"}
96+
leadingIcon={{ icon: Eye, alt: "Not archived" }}
97+
/>
98+
</Tooltip>
99+
</>
100+
)}
101+
<Tooltip
102+
label={
103+
guide.__typename === "MissingGuide"
104+
? "This guide has never been committed and published yet"
105+
: "This guide is not active"
90106
}
91-
/>
92-
</Tooltip>
107+
enabled={!guide.active}
108+
>
109+
<Button
110+
px="1"
111+
size="1"
112+
variant="soft"
113+
color={guide.active ? "green" : "red"}
114+
leadingIcon={
115+
guide.active
116+
? { icon: CheckCircle2, alt: "Active" }
117+
: { icon: CircleDashed, alt: "Inactive" }
118+
}
119+
/>
120+
</Tooltip>
121+
</Stack>
93122
</Stack>
94123
</Row>
95124
);

packages/react/src/modules/guide/components/Toolbar/V2/GuidesListDisplaySelect.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,9 @@ export const GuidesListDisplaySelect = ({ value, onChange }: Props) => {
2222
style: { zIndex: MAX_Z_INDEX },
2323
}}
2424
>
25-
{/*
26-
<Select.Option size="1" value="current-page">
27-
Present and activated on current page
28-
</Select.Option>
29-
*/}
25+
<Select.Option size="1" value="current-page">
26+
Qualified guides on current page
27+
</Select.Option>
3028
<Select.Option size="1" value="all-eligible">
3129
All eligible guides for user
3230
</Select.Option>

packages/react/src/modules/guide/components/Toolbar/V2/V2.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ import {
1616
import { detectToolbarParam } from "./helpers";
1717
import {
1818
checkEligible,
19+
checkUsable,
1920
useInspectGuideClientStore,
2021
} from "./useInspectGuideClientStore";
2122

2223
export const V2 = () => {
2324
const { client } = useGuideContext();
2425

2526
const [guidesListDisplayed, setGuidesListDisplayed] =
26-
React.useState<DisplayOption>("all-eligible");
27+
React.useState<DisplayOption>("current-page");
2728

2829
const [isVisible, setIsVisible] = React.useState(detectToolbarParam());
2930
const [isCollapsed, setIsCollapsed] = React.useState(true);
@@ -100,6 +101,13 @@ export const V2 = () => {
100101
<Box w="full">
101102
{result.error && <Box>{result.error}</Box>}
102103
{result.guides.map((guide, idx) => {
104+
if (
105+
guidesListDisplayed === "current-page" &&
106+
!checkUsable(guide)
107+
) {
108+
return null;
109+
}
110+
103111
if (
104112
guidesListDisplayed === "all-eligible" &&
105113
!checkEligible(guide)

packages/react/src/modules/guide/components/Toolbar/V2/useInspectGuideClientStore.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
KnockGuide,
33
KnockGuideClientStoreState,
44
KnockGuideIneligibilityMarker,
5+
checkActivatable,
56
} from "@knocklabs/client";
67
import { useGuideContext, useStore } from "@knocklabs/react-core";
78

@@ -19,6 +20,10 @@ type TargetableStatusFalse = {
1920
};
2021
type TargetableStatus = TargetableStatusTrue | TargetableStatusFalse;
2122

23+
type ActivatableStatus = {
24+
status: boolean;
25+
};
26+
2227
type ArchivedStatus = {
2328
status: boolean;
2429
};
@@ -28,6 +33,7 @@ export type InspectedGuide = KnockGuide & {
2833
// true status = good
2934
active: ActiveStatus;
3035
targetable: TargetableStatus;
36+
activatable: ActivatableStatus;
3137

3238
// false status = good
3339
archived: ArchivedStatus;
@@ -43,6 +49,14 @@ export const checkEligible = (guide: InspectedGuide | MissingGuide) => {
4349
return true;
4450
};
4551

52+
export const checkUsable = (guide: InspectedGuide | MissingGuide) => {
53+
if (guide.__typename === "MissingGuide") return false;
54+
if (!checkEligible(guide)) return false;
55+
if (!guide.inspection.activatable.status) return false;
56+
57+
return true;
58+
};
59+
4660
// Exists and ordered in control but absent in switchboard (therefore not
4761
// included in the api response), which implies a newly created guide that has
4862
// never been published to switchboard.
@@ -87,13 +101,15 @@ const toArchivedStatus = (
87101

88102
const inspectGuide = (
89103
guide: KnockGuide,
90-
ineligibleGuides: KnockGuideClientStoreState["ineligibleGuides"],
104+
{ ineligibleGuides, location }: StoreStateSnapshot,
105+
// ineligibleGuides: KnockGuideClientStoreState["ineligibleGuides"],
91106
): InspectedGuide => {
92107
const marker = ineligibleGuides[guide.key];
93108

94109
const inspection: InspectedGuide["inspection"] = {
95110
active: { status: guide.active },
96111
targetable: marker ? toTargetableStatus(marker) : { status: true },
112+
activatable: { status: checkActivatable(guide, location) },
97113
archived: marker ? toArchivedStatus(marker) : { status: false },
98114
};
99115

@@ -111,12 +127,18 @@ const newMissingGuide = (key: KnockGuide["key"]) =>
111127
bypass_global_group_limit: false,
112128
}) as MissingGuide;
113129

130+
type StoreStateSnapshot = Pick<
131+
KnockGuideClientStoreState,
132+
"location" | "guides" | "guideGroups" | "ineligibleGuides" | "debug"
133+
>;
134+
114135
export const useInspectGuideClientStore = (): InspectionResult | undefined => {
115136
const { client } = useGuideContext();
116137

117138
// Extract a snapshot of the client store state for debugging.
118-
const snapshot = useStore(client.store, (state) => {
139+
const snapshot: StoreStateSnapshot = useStore(client.store, (state) => {
119140
return {
141+
location: state.location,
120142
guides: state.guides,
121143
guideGroups: state.guideGroups,
122144
ineligibleGuides: state.ineligibleGuides,
@@ -146,7 +168,7 @@ export const useInspectGuideClientStore = (): InspectionResult | undefined => {
146168
return newMissingGuide(guideKey);
147169
}
148170

149-
return inspectGuide(guide, snapshot.ineligibleGuides);
171+
return inspectGuide(guide, snapshot);
150172
});
151173

152174
return {

0 commit comments

Comments
 (0)