@aircall/blocks 0.17.0 → 0.18.1
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/dist/globals.css +1 -1
- package/dist/index.d.ts +862 -18
- package/dist/index.js +1044 -82
- package/package.json +2 -2
- package/skills/aircall-blocks/migrate-dashboard/SKILL.md +20 -3
- package/skills/aircall-blocks/migrate-dashboard/card/SKILL.md +43 -71
- package/skills/aircall-blocks/migrate-dashboard/catch-up-checklist/SKILL.md +109 -0
- package/skills/aircall-blocks/migrate-dashboard/combobox/SKILL.md +0 -1
- package/skills/aircall-blocks/migrate-dashboard/copy-button/SKILL.md +0 -1
- package/skills/aircall-blocks/migrate-dashboard/dashboard-page/SKILL.md +39 -1
- package/skills/aircall-blocks/migrate-dashboard/data-table/SKILL.md +10 -1
- package/skills/aircall-blocks/migrate-dashboard/empty-states/SKILL.md +0 -1
- package/skills/aircall-blocks/migrate-dashboard/form-wizard/SKILL.md +40 -1
- package/skills/aircall-blocks/migrate-dashboard/info-popup/SKILL.md +0 -1
- package/skills/aircall-blocks/migrate-dashboard/layout/SKILL.md +0 -1
- package/skills/aircall-blocks/migrate-dashboard/list/SKILL.md +0 -1
- package/skills/aircall-blocks/migrate-dashboard/loading/SKILL.md +0 -1
- package/skills/aircall-blocks/migrate-dashboard/page-header/SKILL.md +0 -1
- package/skills/aircall-blocks/migrate-dashboard/setting-card/SKILL.md +258 -0
- package/skills/aircall-blocks/migrate-dashboard/tile/SKILL.md +211 -161
- package/skills/aircall-blocks/migrate-dashboard/toggle-row/SKILL.md +0 -1
- package/skills/aircall-blocks/setup/SKILL.md +22 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aircall-blocks/migrate-dashboard/setting-card
|
|
3
|
+
description: >
|
|
4
|
+
Migrate @dashboard/library Paper and PaperForm (settings-page surfaces) to the
|
|
5
|
+
@aircall/blocks SettingCard family (SettingCard, SettingCardHeader, SettingCardTitle,
|
|
6
|
+
SettingCardDescription, SettingCardAction, SettingCardContent) plus useForm + CardSaveBar
|
|
7
|
+
for the save bar. Load when a file imports Paper or PaperForm from @dashboard/library and
|
|
8
|
+
the surface is a settings section (title / description / optional toggle or action /
|
|
9
|
+
optional save bar). The errorBoundary is handled OUTSIDE the card — it does not migrate.
|
|
10
|
+
type: sub-skill
|
|
11
|
+
library: aircall-blocks
|
|
12
|
+
requires:
|
|
13
|
+
- aircall-blocks/setup
|
|
14
|
+
- aircall-blocks/migrate-dashboard
|
|
15
|
+
sources:
|
|
16
|
+
- "aircall/hydra:packages/blocks/src/components/setting-card.tsx"
|
|
17
|
+
- "aircall/hydra:packages/blocks/src/components/card-save-bar.tsx"
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
This skill builds on aircall-blocks/migrate-dashboard.
|
|
21
|
+
|
|
22
|
+
`Paper` / `PaperForm` are the `@dashboard/library` settings-page surfaces (title, subtitle,
|
|
23
|
+
optional header action, content, optional save-bar footer). Their target is **`SettingCard`**
|
|
24
|
+
(`@aircall/blocks`) — a thin composition over the DS `Card` that adds the settings level
|
|
25
|
+
backgrounds and the level-aware title. For a **non-settings** card (a tile or generic
|
|
26
|
+
container with no title/toggle semantics) use the raw `Card` primitives instead — load
|
|
27
|
+
`@aircall/blocks#aircall-blocks/migrate-dashboard/card`.
|
|
28
|
+
|
|
29
|
+
## 1. Component mapping
|
|
30
|
+
|
|
31
|
+
| @dashboard/library | @aircall/blocks / @aircall/ds |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `Paper` (root surface) | `SettingCard` |
|
|
34
|
+
| `Paper` `title` prop | `SettingCardTitle` inside `SettingCardHeader` |
|
|
35
|
+
| `Paper` `subtitle` prop | `SettingCardDescription` inside `SettingCardHeader` |
|
|
36
|
+
| `Paper` `titleSide` prop | `SettingCardAction` inside `SettingCardHeader` |
|
|
37
|
+
| `Paper` `children` (body) | `SettingCardContent` |
|
|
38
|
+
| `Paper` `footer` (save/discard bar) | `useForm` + `CardSaveBar` (form on the outer level) |
|
|
39
|
+
| `Paper` `footer` (generic) | plain children after `SettingCardContent`, or a DS `CardFooter` |
|
|
40
|
+
| `Paper` `banner` prop | inline DS `Banner` before `SettingCardHeader` |
|
|
41
|
+
| `Paper` `disabled` / `disabledText` | app-level — dim the card + render a lock label; `SettingCard` has no `disabled` prop |
|
|
42
|
+
| `Paper` `fluid` prop | content is already full-width — drop it |
|
|
43
|
+
| `Paper` `errorBoundary` prop | **stays in the app, OUTSIDE `SettingCard`** — see §3. Does NOT migrate. |
|
|
44
|
+
| `Paper` `BoxProps` spread | drop — `className` for genuine one-offs only |
|
|
45
|
+
| `PaperForm` (form + save bar) | `<form>` (outer) + `useForm` + `SettingCard` + `CardSaveBar` |
|
|
46
|
+
| `PaperForm` `formProps` (`react-final-form`) | `useForm` (TanStack Form) — `defaultValues`, `onSubmit: async ({ value })` |
|
|
47
|
+
| `PaperForm` `getErrorMessage` / `submitError` | catch in `onSubmit`, store in state, render an `Alert` above the content |
|
|
48
|
+
| `PaperForm` `shouldBlockNavigation` | app-level (navigation blocker) — not part of blocks |
|
|
49
|
+
| `PaperForm` `submitButtonText` / `undoButtonText` | `CardSaveBar` `submitLabel` / `resetLabel` |
|
|
50
|
+
|
|
51
|
+
## 2. Imports
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// blocks — SettingCard family + TanStack Form + save bar
|
|
55
|
+
import {
|
|
56
|
+
SettingCard,
|
|
57
|
+
SettingCardHeader,
|
|
58
|
+
SettingCardTitle,
|
|
59
|
+
SettingCardDescription,
|
|
60
|
+
SettingCardAction,
|
|
61
|
+
SettingCardContent,
|
|
62
|
+
useForm,
|
|
63
|
+
CardSaveBar
|
|
64
|
+
} from '@aircall/blocks';
|
|
65
|
+
|
|
66
|
+
// DS primitives used inside (Button, Switch, Alert, Banner, fields …)
|
|
67
|
+
import { Button, Switch, Alert } from '@aircall/ds';
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 3. The errorBoundary stays OUTSIDE SettingCard
|
|
71
|
+
|
|
72
|
+
`Paper` wrapped **its children** in `ScopedErrorBoundary` (plus an optional
|
|
73
|
+
`ErrorContextProvider`) via the `errorBoundary` prop. Those are **app-level**
|
|
74
|
+
`@dashboard/library` components — `ScopedErrorBoundary` is react-router-aware and reads
|
|
75
|
+
`ErrorContext`; neither migrates to blocks/ds. `SettingCard` is a presentational layout
|
|
76
|
+
primitive and deliberately has **no** `errorBoundary` prop.
|
|
77
|
+
|
|
78
|
+
So keep the boundary in the app and wrap the **content** (not the whole card), matching
|
|
79
|
+
`Paper`'s scope — a content crash still leaves the card title/description visible:
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { ScopedErrorBoundary, ErrorContextProvider } from '@dashboard/library';
|
|
83
|
+
|
|
84
|
+
<SettingCard>
|
|
85
|
+
<SettingCardHeader>
|
|
86
|
+
<SettingCardTitle>Redaction</SettingCardTitle>
|
|
87
|
+
<SettingCardDescription>Mask sensitive data in transcripts.</SettingCardDescription>
|
|
88
|
+
</SettingCardHeader>
|
|
89
|
+
<SettingCardContent>
|
|
90
|
+
{/* errorBoundary lives here, around the fallible content — NOT on SettingCard */}
|
|
91
|
+
<ScopedErrorBoundary message="Couldn't load redaction settings" showRetryButton>
|
|
92
|
+
<RedactionCategories />
|
|
93
|
+
</ScopedErrorBoundary>
|
|
94
|
+
</SettingCardContent>
|
|
95
|
+
</SettingCard>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
If `Paper`'s `errorBoundary` config carried a `context`, keep the
|
|
99
|
+
`ErrorContextProvider` wrapper too (same nesting as `Paper` did — provider outside the
|
|
100
|
+
boundary):
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<ErrorContextProvider value={ctx}>
|
|
104
|
+
<ScopedErrorBoundary {...cfg}>{content}</ScopedErrorBoundary>
|
|
105
|
+
</ErrorContextProvider>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
> If the whole section (header included) should be replaced on error, wrap the entire
|
|
109
|
+
> `<SettingCard>` instead. Default to wrapping only the content — that is what `Paper` did.
|
|
110
|
+
|
|
111
|
+
## 4. Before / After
|
|
112
|
+
|
|
113
|
+
### 4a. Basic Paper — settings surface
|
|
114
|
+
|
|
115
|
+
**Before:**
|
|
116
|
+
```tsx
|
|
117
|
+
import { Paper } from '@dashboard/library';
|
|
118
|
+
|
|
119
|
+
<Paper title="General settings" subtitle="Manage your workspace preferences.">
|
|
120
|
+
<WorkspacePreferences />
|
|
121
|
+
</Paper>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**After:**
|
|
125
|
+
```tsx
|
|
126
|
+
import { SettingCard, SettingCardHeader, SettingCardTitle, SettingCardDescription, SettingCardContent } from '@aircall/blocks';
|
|
127
|
+
|
|
128
|
+
<SettingCard>
|
|
129
|
+
<SettingCardHeader>
|
|
130
|
+
<SettingCardTitle>General settings</SettingCardTitle>
|
|
131
|
+
<SettingCardDescription>Manage your workspace preferences.</SettingCardDescription>
|
|
132
|
+
</SettingCardHeader>
|
|
133
|
+
<SettingCardContent>
|
|
134
|
+
<WorkspacePreferences />
|
|
135
|
+
</SettingCardContent>
|
|
136
|
+
</SettingCard>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- `title` → `SettingCardTitle`, `subtitle` → `SettingCardDescription`, both inside `SettingCardHeader`.
|
|
140
|
+
- Body → `SettingCardContent`. Drop the `BoxProps` spread — `SettingCard` owns the surface.
|
|
141
|
+
|
|
142
|
+
### 4b. Paper with a header action / toggle (titleSide)
|
|
143
|
+
|
|
144
|
+
**Before:**
|
|
145
|
+
```tsx
|
|
146
|
+
<Paper
|
|
147
|
+
title="Call recording"
|
|
148
|
+
subtitle="Record inbound and outbound calls."
|
|
149
|
+
titleSide={<Switch checked={enabled} onChange={setEnabled} />}
|
|
150
|
+
>
|
|
151
|
+
{enabled && <RecordingOptions />}
|
|
152
|
+
</Paper>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**After:**
|
|
156
|
+
```tsx
|
|
157
|
+
<SettingCard>
|
|
158
|
+
<SettingCardHeader>
|
|
159
|
+
<SettingCardTitle>Call recording</SettingCardTitle>
|
|
160
|
+
<SettingCardDescription>Record inbound and outbound calls.</SettingCardDescription>
|
|
161
|
+
<SettingCardAction>
|
|
162
|
+
<Switch checked={enabled} onCheckedChange={setEnabled} />
|
|
163
|
+
</SettingCardAction>
|
|
164
|
+
</SettingCardHeader>
|
|
165
|
+
{enabled && (
|
|
166
|
+
<SettingCardContent>
|
|
167
|
+
<RecordingOptions />
|
|
168
|
+
</SettingCardContent>
|
|
169
|
+
)}
|
|
170
|
+
</SettingCard>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
- `titleSide` → `SettingCardAction` (inside `SettingCardHeader`; the header grid pins it top-right).
|
|
174
|
+
- DS `Switch` uses `onCheckedChange`, not tractor's `onChange`.
|
|
175
|
+
- **Progressive disclosure is consumer-controlled**: render `SettingCardContent` only when the toggle is on (`{enabled && …}`). `SettingCard` has no internal toggle→content coupling, so gated data/fields never mount while off.
|
|
176
|
+
|
|
177
|
+
### 4c. Nested settings (levels are automatic)
|
|
178
|
+
|
|
179
|
+
A `SettingCard` rendered inside another card's `SettingCardContent` auto-detects **level 2**
|
|
180
|
+
(white background + border, 16px medium title); the top card is **level 1** (muted,
|
|
181
|
+
borderless, 18px bold). No prop needed. For a rare 3rd level, force `level={1}` on the
|
|
182
|
+
deepest card to reuse the level-1 background (its title stays 16px medium automatically).
|
|
183
|
+
|
|
184
|
+
### 4d. PaperForm — form with save/discard bar
|
|
185
|
+
|
|
186
|
+
**Before:**
|
|
187
|
+
```tsx
|
|
188
|
+
import { PaperForm } from '@dashboard/library';
|
|
189
|
+
|
|
190
|
+
<PaperForm
|
|
191
|
+
title="Profile"
|
|
192
|
+
subtitle="Update your display name."
|
|
193
|
+
formProps={{ initialValues: { name: '' }, onSubmit: async (values) => save(values) }}
|
|
194
|
+
submitButtonText="Save"
|
|
195
|
+
undoButtonText="Discard"
|
|
196
|
+
>
|
|
197
|
+
{({ values }) => <input name="name" defaultValue={values.name} />}
|
|
198
|
+
</PaperForm>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**After:**
|
|
202
|
+
```tsx
|
|
203
|
+
import { SettingCard, SettingCardHeader, SettingCardTitle, SettingCardDescription, SettingCardContent, useForm, FormInputField } from '@aircall/blocks';
|
|
204
|
+
import { FieldGroup, Input } from '@aircall/ds';
|
|
205
|
+
|
|
206
|
+
function ProfileForm() {
|
|
207
|
+
const form = useForm({
|
|
208
|
+
defaultValues: { name: '' },
|
|
209
|
+
onSubmit: async ({ value }) => { await save(value); }
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
// Form on the OUTER level — SettingCard stays presentational.
|
|
214
|
+
<form onSubmit={e => { e.preventDefault(); void form.handleSubmit(); }}>
|
|
215
|
+
<SettingCard>
|
|
216
|
+
<SettingCardHeader>
|
|
217
|
+
<SettingCardTitle>Profile</SettingCardTitle>
|
|
218
|
+
<SettingCardDescription>Update your display name.</SettingCardDescription>
|
|
219
|
+
</SettingCardHeader>
|
|
220
|
+
<SettingCardContent>
|
|
221
|
+
<FieldGroup>
|
|
222
|
+
<FormInputField form={form} name="name" label="Display name">
|
|
223
|
+
{(_field, { inputProps }) => <Input {...inputProps} />}
|
|
224
|
+
</FormInputField>
|
|
225
|
+
</FieldGroup>
|
|
226
|
+
</SettingCardContent>
|
|
227
|
+
<form.AppForm>
|
|
228
|
+
<form.CardSaveBar submitLabel="Save" resetLabel="Discard" />
|
|
229
|
+
</form.AppForm>
|
|
230
|
+
</SettingCard>
|
|
231
|
+
</form>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Key changes:
|
|
237
|
+
- `react-final-form` `formProps` + render-prop → `useForm` from `@aircall/blocks` (TanStack Form): `defaultValues` (not `initialValues`), `onSubmit` receives `{ value }`.
|
|
238
|
+
- Hand-wired inputs → `Form*Field` wrappers driven by the form (never per-field `useState`).
|
|
239
|
+
- Put the `<form>` on the **outer level** (wrapping `SettingCard`), and drop **`form.CardSaveBar`** in as the last child of `SettingCard`. It reads `isDirty`/`canSubmit`/`isSubmitting` from context, slides in when dirty, and carries `data-slot="card-footer"` so the card auto-collapses its bottom padding and the bar sits flush. **No `className` is needed on any part** - the card's own layout (row gap + auto `pb-0`) handles both the clean and dirty states.
|
|
240
|
+
- `submitButtonText`/`undoButtonText` → `CardSaveBar` `submitLabel`/`resetLabel`.
|
|
241
|
+
|
|
242
|
+
**Submit error (`getErrorMessage` / `submitError`).** No built-in equivalent — catch in
|
|
243
|
+
`onSubmit`, store it, and render an `Alert` above the content:
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
const [submitError, setSubmitError] = useState<string | null>(null);
|
|
247
|
+
const form = useForm({
|
|
248
|
+
defaultValues: { name: '' },
|
|
249
|
+
onSubmit: async ({ value }) => {
|
|
250
|
+
try { await save(value); setSubmitError(null); }
|
|
251
|
+
catch (e) { setSubmitError(getErrorMessage(e)); }
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
// In JSX, above the fields inside SettingCardContent:
|
|
255
|
+
{submitError && <Alert variant="error">{submitError}</Alert>}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
> DS `Alert`'s error variant is `error` (not tractor's `critical`). Full set: `default` / `info` / `success` / `warning` / `error`.
|