@actioneer/ads 1.0.8 → 1.0.9

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.
@@ -19,7 +19,12 @@ export declare const TabList: React.ForwardRefExoticComponent<TabListProps & Rea
19
19
  export interface TabProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
20
20
  value: string;
21
21
  isDisabled?: boolean;
22
- /** Optional trailing count, rendered as `· N` (e.g. a filter's match count). */
22
+ /**
23
+ * Optional trailing count (e.g. a filter's match count), rendered as the bare
24
+ * number 6px after the label. It stays quieter than the label: Regular
25
+ * weight and tabular figures in every state, tertiary grey when inactive and
26
+ * the primary-action teal when active.
27
+ */
23
28
  count?: number;
24
29
  }
25
30
  export declare const Tab: React.ForwardRefExoticComponent<TabProps & React.RefAttributes<HTMLButtonElement>>;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as m, jsxs as E } from "react/jsx-runtime";
2
+ import { jsx as p, jsxs as E } from "react/jsx-runtime";
3
3
  import * as r from "react";
4
4
  import { MotionConfig as j, motion as $ } from "motion/react";
5
5
  import { cva as L } from "class-variance-authority";
@@ -18,26 +18,26 @@ const U = r.forwardRef(
18
18
  value: e,
19
19
  defaultValue: t,
20
20
  onValueChange: a,
21
- variant: w,
21
+ variant: g,
22
22
  orientation: c = "horizontal",
23
- size: p,
23
+ size: m,
24
24
  children: R,
25
25
  ...A
26
26
  }, k) => {
27
- const x = e !== void 0, [y, v] = r.useState(t), T = r.useId().replaceAll(":", ""), d = r.useId(), g = r.useRef([]), h = r.useRef(/* @__PURE__ */ new Set()), f = x ? e : y, I = r.useCallback(
27
+ const x = e !== void 0, [y, v] = r.useState(t), T = r.useId().replaceAll(":", ""), d = r.useId(), w = r.useRef([]), h = r.useRef(/* @__PURE__ */ new Set()), f = x ? e : y, I = r.useCallback(
28
28
  (l) => {
29
29
  x || v(l), a == null || a(l);
30
30
  },
31
31
  [x, a]
32
32
  ), i = r.useCallback((l) => {
33
- g.current.includes(l) || g.current.push(l);
33
+ w.current.includes(l) || w.current.push(l);
34
34
  }, []), s = r.useMemo(
35
35
  () => ({
36
36
  value: f,
37
37
  setValue: I,
38
38
  baseId: T,
39
39
  registerTab: i,
40
- tabs: g,
40
+ tabs: w,
41
41
  disabled: h,
42
42
  thumbId: d,
43
43
  orientation: c
@@ -51,7 +51,7 @@ const U = r.forwardRef(
51
51
  c
52
52
  ]
53
53
  );
54
- return /* @__PURE__ */ m(P.Provider, { value: s, children: /* @__PURE__ */ m(
54
+ return /* @__PURE__ */ p(P.Provider, { value: s, children: /* @__PURE__ */ p(
55
55
  "div",
56
56
  {
57
57
  ref: k,
@@ -63,13 +63,13 @@ const U = r.forwardRef(
63
63
  o
64
64
  ),
65
65
  ...A,
66
- children: /* @__PURE__ */ m(j, { reducedMotion: "user", children: R })
66
+ children: /* @__PURE__ */ p(j, { reducedMotion: "user", children: R })
67
67
  }
68
68
  ) });
69
69
  }
70
70
  );
71
71
  U.displayName = "Tabs";
72
- const C = L("inline-flex text-l", {
72
+ const C = L("inline-flex text-m", {
73
73
  variants: {
74
74
  orientation: {
75
75
  horizontal: b(
@@ -88,7 +88,7 @@ const C = L("inline-flex text-l", {
88
88
  }), B = r.forwardRef(
89
89
  ({ className: o, ...e }, t) => {
90
90
  const { orientation: a } = z("TabList");
91
- return /* @__PURE__ */ m(
91
+ return /* @__PURE__ */ p(
92
92
  "div",
93
93
  {
94
94
  ref: t,
@@ -127,7 +127,7 @@ const H = L(
127
127
  }
128
128
  }
129
129
  ), O = r.forwardRef(
130
- ({ className: o, value: e, isDisabled: t, count: a, children: w, onClick: c, onKeyDown: p, ...R }, A) => {
130
+ ({ className: o, value: e, isDisabled: t, count: a, children: g, onClick: c, onKeyDown: m, ...R }, A) => {
131
131
  const {
132
132
  value: k,
133
133
  setValue: x,
@@ -135,7 +135,7 @@ const H = L(
135
135
  registerTab: v,
136
136
  tabs: T,
137
137
  disabled: d,
138
- thumbId: g,
138
+ thumbId: w,
139
139
  orientation: h
140
140
  } = z("Tab"), f = k === e;
141
141
  r.useEffect(() => {
@@ -144,7 +144,7 @@ const H = L(
144
144
  t ? d.current.add(e) : d.current.delete(e);
145
145
  }, [d, t, e]);
146
146
  const I = (i) => {
147
- if (p == null || p(i), i.defaultPrevented) return;
147
+ if (m == null || m(i), i.defaultPrevented) return;
148
148
  const s = T.current, l = s.indexOf(e);
149
149
  if (l === -1) return;
150
150
  const V = h === "vertical" ? ["ArrowDown"] : ["ArrowRight", "ArrowDown"], _ = h === "vertical" ? ["ArrowUp"] : ["ArrowLeft", "ArrowUp"];
@@ -199,10 +199,10 @@ const H = L(
199
199
  onKeyDown: I,
200
200
  ...R,
201
201
  children: [
202
- f && /* @__PURE__ */ m(
202
+ f && /* @__PURE__ */ p(
203
203
  $.span,
204
204
  {
205
- layoutId: g,
205
+ layoutId: w,
206
206
  "aria-hidden": !0,
207
207
  transition: M,
208
208
  className: b(
@@ -211,19 +211,16 @@ const H = L(
211
211
  )
212
212
  }
213
213
  ),
214
- /* @__PURE__ */ E("span", { className: "relative inline-flex items-center gap-1", children: [
215
- w,
216
- a != null && /* @__PURE__ */ E(
214
+ /* @__PURE__ */ E("span", { className: "relative inline-flex items-center gap-1.5", children: [
215
+ g,
216
+ a != null && /* @__PURE__ */ p(
217
217
  "span",
218
218
  {
219
219
  className: b(
220
- "tabular-nums",
221
- f ? "text-action-primary-text" : "text-secondary"
220
+ "font-normal tabular-nums",
221
+ f ? "text-action-primary-text" : "text-on-white-tertiary"
222
222
  ),
223
- children: [
224
- "· ",
225
- a
226
- ]
223
+ children: a
227
224
  }
228
225
  )
229
226
  ] })
@@ -236,7 +233,7 @@ O.displayName = "Tab";
236
233
  const q = r.forwardRef(
237
234
  ({ className: o, ...e }, t) => {
238
235
  const { orientation: a } = z("TabSeparator");
239
- return /* @__PURE__ */ m(
236
+ return /* @__PURE__ */ p(
240
237
  "span",
241
238
  {
242
239
  ref: t,
@@ -254,15 +251,15 @@ const q = r.forwardRef(
254
251
  );
255
252
  q.displayName = "TabSeparator";
256
253
  const F = r.forwardRef(
257
- ({ className: o, value: e, children: t, ...a }, w) => {
258
- const { value: c, baseId: p } = z("TabPanel");
259
- return c !== e ? null : /* @__PURE__ */ m(
254
+ ({ className: o, value: e, children: t, ...a }, g) => {
255
+ const { value: c, baseId: m } = z("TabPanel");
256
+ return c !== e ? null : /* @__PURE__ */ p(
260
257
  "div",
261
258
  {
262
- ref: w,
259
+ ref: g,
263
260
  role: "tabpanel",
264
- id: N(p, "panel", e),
265
- "aria-labelledby": N(p, "tab", e),
261
+ id: N(m, "panel", e),
262
+ "aria-labelledby": N(m, "tab", e),
266
263
  tabIndex: 0,
267
264
  className: b(
268
265
  "flex-1 outline-none focus-visible:ring-3 focus-visible:ring-ring rounded-sm",
@@ -35,6 +35,7 @@ import "react-aria-components";
35
35
  import "../../components/autocomplete.js";
36
36
  import "../../components/slider.js";
37
37
  import "../../components/segmented.js";
38
+ import "../../components/filters.js";
38
39
  import { Modal as q, ModalHeader as J, ModalTitle as Q, ModalBody as x, ModalFooter as g } from "../../components/modal.js";
39
40
  import "../../components/drawer.js";
40
41
  import "../../components/popover.js";
@@ -106,7 +107,7 @@ import "../../components/assistant-ui/quote.js";
106
107
  import "../../components/assistant-ui/sources.js";
107
108
  import "../../components/assistant-ui/tool-fallback.js";
108
109
  import "../../components/assistant-ui/tool-group.js";
109
- function ei({
110
+ function ii({
110
111
  block: i,
111
112
  trigger: v = "button",
112
113
  className: A,
@@ -358,5 +359,5 @@ function ei({
358
359
  ] });
359
360
  }
360
361
  export {
361
- ei as AddToArtifact
362
+ ii as AddToArtifact
362
363
  };
@@ -13,6 +13,8 @@ root in `MotionConfig`, `ToastProvider` and `TooltipProvider`.
13
13
  | `agents-screen.tsx` | `AgentsScreen` | A full list page: `PageHeader` with a primary action, a summary strip, `Tabs` with counts leading a `SearchDisclosure`, a filter `Dropdown`, a sortable `Table` with clickable rows and a pinned action column, per-row actions with `Tooltip`s, and filtered `EmptyState`s. |
14
14
  | `settings-screen.tsx` | `SettingsScreen` | A settings card: `Card` header with a status `Badge`, an `InputField`, a `SettingList` of `SettingRow`s with `Switch` and `Button` controls, and one primary action. |
15
15
  | `workspace-header.tsx` | `WorkspaceHeaderScreen` | A page header with a status `Badge` and trailing actions, then `Tabs` with counts whose panels hold `Card`s. |
16
+ | `campaigns-screen.tsx` | `CampaignsScreen` | The collection-screen recipe: `BodyPageHeader` with one primary action, counted `Tabs` leading `SearchDisclosure` + `Filters`, a multi-select `Table` (hover-revealed checkboxes, cohort child rows, `isActive` open row) whose page size fills the panel with `Pagination` at the bottom, a shell-level `Drawer` with status under the title and Performance / Details sections, and the fixed-height New campaign `Modal`. |
17
+ | `campaign-analysis-screen.tsx` | `CampaignAnalysisScreen` | The record-analytics recipe: a 16px `Breadcrumbs` title with the primary action on the same row, record `Tabs`, outlined stat cards with `Progress`, and outlined `BarChart` / `LineChart` cards with header legends and printed values. |
16
18
 
17
19
  ```tsx
18
20
  // app/agents/page.tsx
@@ -25,6 +27,5 @@ export default function Page() {
25
27
  }
26
28
  ```
27
29
 
28
- These are the same compositions the ADS Storybook renders under
29
- **Recipes**, so what you see there is what these files produce. Icons come from
30
+ Icons come from
30
31
  `@actioneer/ads/lib/icons`; never install a second icon package.
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Campaign analysis screen — the ADS record/analytics-page recipe, built only
3
+ * from ADS exports.
4
+ *
5
+ * Follows principles `leading-back-navigation`, `analytics-surfaces` and
6
+ * `status-language` (see `get_principles`):
7
+ *
8
+ * - The 16px Breadcrumbs trail (parent / record) IS the page title; the one
9
+ * primary action sits at the right end of the same row. No back arrow and
10
+ * no repeated H1.
11
+ * - Every stat and every chart sits in its own outlined card (1px WG3 border,
12
+ * radius-l, 20px padding, flat), 16px apart.
13
+ * - A stat card stacks label → number + scope 4px apart, then a Progress bar
14
+ * 12px below that fills the card.
15
+ * - A chart card puts its title + scope subtitle on the left and the legend at
16
+ * the right of the same row (`legendPlacement="header"`), and prints its
17
+ * values on the chart — never hover-only.
18
+ *
19
+ * Copy it and replace the sample STATS / SEGMENT / REACH data with your own.
20
+ */
21
+ import * as React from "react";
22
+ import {
23
+ BarChart,
24
+ BreadcrumbItem,
25
+ Breadcrumbs,
26
+ Button,
27
+ LineChart,
28
+ Progress,
29
+ Tab,
30
+ TabList,
31
+ Tabs,
32
+ } from "@actioneer/ads";
33
+
34
+ export const STATS = [
35
+ { label: "Calls analysed", value: 3930, of: "of 4,800 calls", percent: 82, color: "primary" as const },
36
+ { label: "Recipients reached", value: 864, of: "of 4,800 people", percent: 18, color: "primary" as const },
37
+ // The one number that needs attention is the only amber one.
38
+ { label: "Asked for a callback", value: 144, of: "of 864 calls", percent: 17, color: "warning" as const },
39
+ ];
40
+
41
+ export const SEGMENT = [
42
+ { day: "Oct 19", netNew: 3200, already: 1900 },
43
+ { day: "Oct 20", netNew: 420, already: 3080 },
44
+ { day: "Oct 21", netNew: 360, already: 3240 },
45
+ { day: "Oct 22", netNew: 440, already: 3460 },
46
+ { day: "Oct 23", netNew: 380, already: 3520 },
47
+ ];
48
+
49
+ export const REACH = [
50
+ { day: "Oct 19", reached: 12, booked: 2 },
51
+ { day: "Oct 20", reached: 14, booked: 3 },
52
+ { day: "Oct 21", reached: 15, booked: 4 },
53
+ { day: "Oct 22", reached: 17, booked: 4 },
54
+ { day: "Oct 23", reached: 18, booked: 5 },
55
+ ];
56
+
57
+ const TABS = ["Script", "Workflow", "Analysis", "Call logs", "Guardrails"];
58
+
59
+ /** Outlined container for one stat or one chart (principle `analytics-surfaces`). */
60
+ function DataCard({ children, label }: { children: React.ReactNode; label: string }) {
61
+ return (
62
+ <section aria-label={label} className="flex min-w-0 flex-col rounded-l border border-tertiary bg-card p-5">
63
+ {children}
64
+ </section>
65
+ );
66
+ }
67
+
68
+ export function CampaignAnalysisScreen() {
69
+ const [tab, setTab] = React.useState("Analysis");
70
+
71
+ return (
72
+ <div className="flex h-screen bg-background p-3 text-foreground">
73
+ <main aria-label="Q4 renewal reminders" className="flex min-w-0 flex-1 flex-col overflow-hidden rounded-l bg-card">
74
+ <div className="px-6 pt-6">
75
+ <Breadcrumbs actions={<Button>Pause campaign</Button>}>
76
+ <BreadcrumbItem href="#campaigns">Voice campaigns</BreadcrumbItem>
77
+ <BreadcrumbItem isCurrent>Q4 renewal reminders</BreadcrumbItem>
78
+ </Breadcrumbs>
79
+ </div>
80
+
81
+ <div className="px-6 pt-5">
82
+ <Tabs value={tab} onValueChange={setTab}>
83
+ <TabList aria-label="Campaign sections">
84
+ {TABS.map((item) => (
85
+ <Tab key={item} value={item}>{item}</Tab>
86
+ ))}
87
+ </TabList>
88
+ </Tabs>
89
+ </div>
90
+
91
+ <div className="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-6 pt-6 pb-6">
92
+ <div role="list" aria-label="Campaign progress" className="grid grid-cols-[repeat(auto-fill,minmax(16rem,1fr))] gap-4">
93
+ {STATS.map((stat) => (
94
+ <div role="listitem" key={stat.label} className="contents">
95
+ <DataCard label={stat.label}>
96
+ <p className="text-m text-secondary">{stat.label}</p>
97
+ <p className="mt-1 flex items-baseline gap-2">
98
+ <span className="text-3xl tabular-nums text-foreground">{stat.value.toLocaleString("en-IN")}</span>
99
+ <span className="text-m text-secondary">{stat.of}</span>
100
+ </p>
101
+ <Progress className="mt-3" value={stat.percent} color={stat.color} label={stat.label} />
102
+ </DataCard>
103
+ </div>
104
+ ))}
105
+ </div>
106
+
107
+ <div className="grid grid-cols-[repeat(auto-fill,minmax(24rem,1fr))] gap-4">
108
+ <DataCard label="Who was in the segment">
109
+ <BarChart
110
+ title="Who was in the segment"
111
+ subtitle="4,800 net new numbers · Oct 19 – 23"
112
+ legendPlacement="header"
113
+ data={SEGMENT}
114
+ categoryKey="day"
115
+ series={["netNew", "already"]}
116
+ seriesLabels={{ netNew: "Net new", already: "Already in campaign" }}
117
+ stacked
118
+ height={360}
119
+ />
120
+ </DataCard>
121
+ <DataCard label="Did we reach them?">
122
+ <LineChart
123
+ title="Did we reach them?"
124
+ subtitle="Share of 4,800 people · by day"
125
+ legendPlacement="header"
126
+ data={REACH}
127
+ categoryKey="day"
128
+ series={["reached", "booked"]}
129
+ seriesLabels={{ reached: "Reached", booked: "Booked" }}
130
+ height={360}
131
+ />
132
+ </DataCard>
133
+ </div>
134
+ </div>
135
+ </main>
136
+ </div>
137
+ );
138
+ }