@schandlergarcia/sf-web-components 2.3.17 → 2.5.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/.a4drules/skills/command-center-builder/SKILL.md +3 -2
- package/.a4drules/skills/component-library/SKILL.md +50 -4
- package/.a4drules/skills/component-library/card-components.md +88 -0
- package/.a4drules/skills/component-library/when-to-use.md +1 -0
- package/CHANGELOG.md +40 -0
- package/CLAUDE.md +12 -13
- package/README.md +0 -15
- package/dist/components/library/cards/KanbanBoard.js +313 -0
- package/dist/components/library/cards/KanbanBoard.js.map +1 -0
- package/dist/components/library/index.js +60 -57
- package/dist/components/library/index.js.map +1 -1
- package/dist/components/workspace/ComponentRegistry.js +5 -2
- package/dist/components/workspace/ComponentRegistry.js.map +1 -1
- package/dist/index.js +84 -82
- package/dist/index.js.map +1 -1
- package/dist/styles/global.css +44 -57
- package/package.json +7 -2
- package/scripts/apply-brand.mjs +47 -30
- package/scripts/postinstall.mjs +1 -11
- package/src/components/library/cards/KanbanBoard.jsx +507 -0
- package/src/components/library/index.jsx +1 -0
- package/src/styles/global.css +44 -57
- package/brands/engine/PARTNER_HUB_PRD.md +0 -584
- package/brands/engine/agentApiConfig.ts +0 -36
- package/brands/engine/app/api/graphql-operations-types.ts +0 -11260
- package/brands/engine/app/api/graphqlClient.ts +0 -25
- package/brands/engine/app/api/partnerQueries.ts +0 -212
- package/brands/engine/app/appLayout.tsx +0 -5
- package/brands/engine/app/components/AgentPanel.tsx +0 -541
- package/brands/engine/app/components/AgentforceConversationClient.tsx +0 -201
- package/brands/engine/app/components/Data360Widget.tsx +0 -301
- package/brands/engine/app/components/__inherit_AgentforceConversationClient.tsx +0 -3
- package/brands/engine/app/components/alerts/status-alert.tsx +0 -49
- package/brands/engine/app/components/layouts/card-layout.tsx +0 -29
- package/brands/engine/app/components/workspace/CommandCenter.tsx +0 -16
- package/brands/engine/app/config/agentApi.ts +0 -36
- package/brands/engine/app/data/partner-hub-sample-data.js +0 -297
- package/brands/engine/app/features/object-search/__examples__/api/accountSearchService.ts +0 -46
- package/brands/engine/app/features/object-search/__examples__/api/query/distinctAccountIndustries.graphql +0 -19
- package/brands/engine/app/features/object-search/__examples__/api/query/distinctAccountTypes.graphql +0 -19
- package/brands/engine/app/features/object-search/__examples__/api/query/getAccountDetail.graphql +0 -121
- package/brands/engine/app/features/object-search/__examples__/api/query/searchAccounts.graphql +0 -51
- package/brands/engine/app/features/object-search/__examples__/pages/AccountObjectDetailPage.tsx +0 -357
- package/brands/engine/app/features/object-search/__examples__/pages/AccountSearch.tsx +0 -312
- package/brands/engine/app/features/object-search/__examples__/pages/Home.tsx +0 -34
- package/brands/engine/app/features/object-search/api/objectSearchService.ts +0 -84
- package/brands/engine/app/features/object-search/components/ActiveFilters.tsx +0 -89
- package/brands/engine/app/features/object-search/components/FilterContext.tsx +0 -83
- package/brands/engine/app/features/object-search/components/ObjectBreadcrumb.tsx +0 -66
- package/brands/engine/app/features/object-search/components/PaginationControls.tsx +0 -109
- package/brands/engine/app/features/object-search/components/SearchBar.tsx +0 -41
- package/brands/engine/app/features/object-search/components/SortControl.tsx +0 -143
- package/brands/engine/app/features/object-search/components/filters/BooleanFilter.tsx +0 -78
- package/brands/engine/app/features/object-search/components/filters/DateFilter.tsx +0 -128
- package/brands/engine/app/features/object-search/components/filters/DateRangeFilter.tsx +0 -70
- package/brands/engine/app/features/object-search/components/filters/FilterFieldWrapper.tsx +0 -33
- package/brands/engine/app/features/object-search/components/filters/MultiSelectFilter.tsx +0 -97
- package/brands/engine/app/features/object-search/components/filters/NumericRangeFilter.tsx +0 -163
- package/brands/engine/app/features/object-search/components/filters/SearchFilter.tsx +0 -50
- package/brands/engine/app/features/object-search/components/filters/SelectFilter.tsx +0 -97
- package/brands/engine/app/features/object-search/components/filters/TextFilter.tsx +0 -91
- package/brands/engine/app/features/object-search/hooks/useAsyncData.ts +0 -54
- package/brands/engine/app/features/object-search/hooks/useCachedAsyncData.ts +0 -184
- package/brands/engine/app/features/object-search/hooks/useDebouncedCallback.ts +0 -34
- package/brands/engine/app/features/object-search/hooks/useObjectSearchParams.ts +0 -252
- package/brands/engine/app/features/object-search/utils/debounce.ts +0 -25
- package/brands/engine/app/features/object-search/utils/fieldUtils.ts +0 -29
- package/brands/engine/app/features/object-search/utils/filterUtils.ts +0 -404
- package/brands/engine/app/features/object-search/utils/sortUtils.ts +0 -38
- package/brands/engine/app/hooks/useEngineLiveData.ts +0 -49
- package/brands/engine/app/hooks/useEvaAgent.ts +0 -288
- package/brands/engine/app/hooks/usePartnerDashboardData.ts +0 -141
- package/brands/engine/app/navigationMenu.tsx +0 -80
- package/brands/engine/app/pages/AccountObjectDetailPage.tsx +0 -361
- package/brands/engine/app/pages/AccountSearch.tsx +0 -305
- package/brands/engine/app/pages/BlankDashboard.tsx +0 -15
- package/brands/engine/app/pages/DataTest.tsx +0 -78
- package/brands/engine/app/pages/Home.tsx +0 -5
- package/brands/engine/app/pages/NotFound.tsx +0 -19
- package/brands/engine/app/pages/PartnerHubDashboard.tsx +0 -2760
- package/brands/engine/app/pages/Search.tsx +0 -13
- package/brands/engine/app/router-utils.tsx +0 -35
- package/brands/engine/app/routes.tsx +0 -39
- package/brands/engine/app/styles/global.css +0 -269
- package/brands/engine/brand.css +0 -40
- package/brands/engine/engine-command-center-prd.md +0 -575
- package/brands/engine/engine-live-data.js +0 -135
- package/brands/engine/engine-sample-data.js +0 -378
- package/brands/engine/engine_logo.png +0 -0
- package/brands/engine/global.css +0 -269
- package/brands/engine/partner-hub-sample-data.js +0 -281
- package/brands/engine/schema.graphql +0 -292
- package/brands/engine/useEngineLiveData.ts +0 -49
- package/brands/engine/useEvaAgent.ts +0 -288
|
@@ -3,7 +3,7 @@ name: command-center-builder
|
|
|
3
3
|
description: >-
|
|
4
4
|
Conventions and patterns for building Command Center dashboards.
|
|
5
5
|
Covers layout patterns (visualization-hero, grid), component selection
|
|
6
|
-
(MetricCard, ChartCard, GeoMap, ListCard, ActivityCard), dark mode,
|
|
6
|
+
(MetricCard, ChartCard, GeoMap, ListCard, KanbanBoard, ActivityCard), dark mode,
|
|
7
7
|
brand colors, styling, and wiring dashboards into the app. Use when
|
|
8
8
|
scaffolding, building, or editing any dashboard page in this project.
|
|
9
9
|
---
|
|
@@ -92,7 +92,7 @@ Use these exact imports. Do NOT read component source files to discover imports
|
|
|
92
92
|
```tsx
|
|
93
93
|
// Library components — named imports from barrel
|
|
94
94
|
import {
|
|
95
|
-
BaseCard, MetricCard, ChartCard, TableCard, ListCard, ActivityCard,
|
|
95
|
+
BaseCard, MetricCard, ChartCard, TableCard, ListCard, KanbanBoard, ActivityCard,
|
|
96
96
|
StatusCard, WidgetCard, SectionCard, FeedPanel, CalloutCard, ActionList,
|
|
97
97
|
MetricsStrip, D3Chart, D3ChartTemplates, GeoMap, Avatar, UIButton,
|
|
98
98
|
UIChip, UIText, EmptyState, Spinner, ChatBar, FilterBar,
|
|
@@ -164,6 +164,7 @@ The component library (`@/components/library`) provides pre-built, themed, dark-
|
|
|
164
164
|
| Activity feed (display only) | `ActivityCard` or `FeedPanel` | `<div>` with hand-rolled interaction rows |
|
|
165
165
|
| **Activity feed (with per-item actions like "Assign to Agent")** | **Custom markup inside `BaseCard`** | **`ActivityCard` (doesn't support per-item buttons)** |
|
|
166
166
|
| Data table | `TableCard` | `<table>` or custom grid of rows |
|
|
167
|
+
| Board / swim-lanes (cards across columns, optional DnD) | `KanbanBoard` | Stacked `ListCard`s or hand-rolled grid of columns |
|
|
167
168
|
| Stats panel | `WidgetCard` or `SectionCard` | `<div className="bg-white ... p-6">` with label/value pairs |
|
|
168
169
|
| Custom interactive content (expand/collapse, inline details) | `WidgetCard` (sections accept arbitrary JSX) or `ListCard` (with `onItemClick` + `itemActions`) | Hand-rolled `<div>` card with custom expand/collapse logic |
|
|
169
170
|
| Status items | `StatusCard` | `<div>` with custom status badges |
|
|
@@ -3,10 +3,11 @@ name: component-library
|
|
|
3
3
|
description: >-
|
|
4
4
|
API reference for the command center component library at
|
|
5
5
|
@/components/library. Documents every component (MetricCard, ChartCard,
|
|
6
|
-
D3Chart, D3ChartTemplates, GeoMap, ListCard,
|
|
7
|
-
StatusCard, WidgetCard, BaseCard, ChatBar, Avatar, UIChip,
|
|
8
|
-
their props, data shapes, and import patterns. Use when
|
|
9
|
-
to use a specific component or choosing which component
|
|
6
|
+
D3Chart, D3ChartTemplates, GeoMap, ListCard, KanbanBoard, ActivityCard,
|
|
7
|
+
TableCard, StatusCard, WidgetCard, BaseCard, ChatBar, Avatar, UIChip,
|
|
8
|
+
FilterBar), their props, data shapes, and import patterns. Use when
|
|
9
|
+
looking up how to use a specific component or choosing which component
|
|
10
|
+
fits a need.
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# Component Library
|
|
@@ -64,6 +65,9 @@ import type { MetricCardProps } from "@/components/library";
|
|
|
64
65
|
- Pass initials strings as `avatar` in ListCard items — `avatar: "MR"` is treated as an image URL and breaks. Instead, omit `avatar` to auto-generate initials from `title`/`name`, or pass a ReactNode like `<Avatar initials="MR" />`
|
|
65
66
|
- Hand-roll a card container (`<div className="bg-white border rounded ...">`) when `WidgetCard` with a single section can wrap your custom content
|
|
66
67
|
- Use Recharts, Chart.js, or any third-party chart library — only `D3Chart` and `GeoMap` are allowed
|
|
68
|
+
- Use `react-beautiful-dnd`, `react-dnd`, or any other DnD library — `KanbanBoard` is built on `@dnd-kit/*` (peer deps); do not introduce a second DnD system
|
|
69
|
+
- Forget `columnId` on a `KanbanBoard` card — every card MUST have `columnId` matching one of `columns[].id`, otherwise it won't render
|
|
70
|
+
- Wrap `KanbanBoard` in another card (`WidgetCard`, `BaseCard`) — it already renders inside its own `BaseCard`
|
|
67
71
|
- Dismiss `validate:dashboard` errors as "justified" — every error has a library component fix
|
|
68
72
|
|
|
69
73
|
---
|
|
@@ -76,6 +80,7 @@ import type { MetricCardProps } from "@/components/library";
|
|
|
76
80
|
| A row of 2–4 KPIs | Grid of `MetricCard` | `MetricsStrip` (for compact inline) |
|
|
77
81
|
| A data table | `TableCard` | `<table>` HTML |
|
|
78
82
|
| A scrollable item list | `ListCard` | Custom divs with `.map()` |
|
|
83
|
+
| A board / swim-lanes (cards in columns, optional drag-and-drop) | `KanbanBoard` | Stacked `ListCard`s or hand-rolled grid |
|
|
79
84
|
| An activity log with status icons | `ActivityCard` | ListCard (no status icons) |
|
|
80
85
|
| A chart (line, bar) | `ChartCard` + `D3Chart` | Raw `<svg>` |
|
|
81
86
|
| A donut/pie chart | `ChartCard` + `D3Chart` + custom renderChart | Any chart library |
|
|
@@ -358,6 +363,47 @@ Status aliases: `"ok"/"healthy"/"up"` → `"operational"`, `"warn"` → `"degrad
|
|
|
358
363
|
/>
|
|
359
364
|
```
|
|
360
365
|
|
|
366
|
+
### KanbanBoard
|
|
367
|
+
**When:** Display items as cards across labeled swim-lanes (statuses, pipeline stages, sprint board). Includes drag-and-drop powered by `@dnd-kit/*`.
|
|
368
|
+
|
|
369
|
+
**Peer deps required:** `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities`. Install in your project if missing:
|
|
370
|
+
```bash
|
|
371
|
+
npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
```jsx
|
|
375
|
+
<KanbanBoard
|
|
376
|
+
title="Sprint Board"
|
|
377
|
+
subtitle="Q2 · Sprint 4"
|
|
378
|
+
columns={[
|
|
379
|
+
{ id: "todo", title: "To Do", limit: 5, accent: "default" },
|
|
380
|
+
{ id: "doing", title: "In Progress", limit: 3, accent: "warning" },
|
|
381
|
+
{ id: "done", title: "Done", accent: "success" },
|
|
382
|
+
]}
|
|
383
|
+
cards={[
|
|
384
|
+
{ id: "C-1", columnId: "todo", title: "Onboard Acme", subtitle: "Owner: Aria", badges: ["Priority"], status: "warning" },
|
|
385
|
+
{ id: "C-2", columnId: "doing", title: "Renew SLA", status: "danger", badges: [{ label: "Risk", color: "danger" }] },
|
|
386
|
+
{ id: "C-3", columnId: "done", title: "Q2 review prep", status: "success" },
|
|
387
|
+
]}
|
|
388
|
+
isDraggable // default true
|
|
389
|
+
showColumnCounts // default true
|
|
390
|
+
showWipLimits // default true; renders "count / limit"
|
|
391
|
+
dense={false}
|
|
392
|
+
accent="default" // fallback accent
|
|
393
|
+
onCardClick={(card) => {/* row click */}}
|
|
394
|
+
onCardMove={({ cardId, fromColumnId, toColumnId, fromIndex, toIndex }) => {
|
|
395
|
+
// Optional. If provided, you own the state. If omitted, the board manages reorder/move internally.
|
|
396
|
+
}}
|
|
397
|
+
emptyMessage="No items"
|
|
398
|
+
loading={false}
|
|
399
|
+
error={null}
|
|
400
|
+
/>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Card shape:** `{ id, columnId, title, subtitle?, description?, status?, accent?, avatar?, badges?, meta?, actions? }`
|
|
404
|
+
**Column shape:** `{ id, title, accent?, limit?, emptyMessage?, footer?, actions? }`
|
|
405
|
+
**Mistakes:** ❌ Missing `columnId` on a card → it won't render. ❌ Wrapping `KanbanBoard` in another card. ❌ Using a non-`@dnd-kit` DnD library alongside it.
|
|
406
|
+
|
|
361
407
|
---
|
|
362
408
|
|
|
363
409
|
## Chart Components
|
|
@@ -249,5 +249,93 @@ Status aliases: `"ok"/"healthy"/"up"` → `"operational"`, `"warn"` → `"degrad
|
|
|
249
249
|
/>
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
+
### KanbanBoard
|
|
253
|
+
**When:** Display items as cards across labeled swim-lanes (To Do → In Progress → Done, pipeline stages, status boards). Supports drag-and-drop between columns out of the box.
|
|
254
|
+
|
|
255
|
+
**Peer deps:** `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities` are required peer dependencies. Install in your project if they're not already there:
|
|
256
|
+
```bash
|
|
257
|
+
npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```jsx
|
|
261
|
+
import { KanbanBoard } from "@/components/library";
|
|
262
|
+
import useDataSource from "@/components/library/data/useDataSource";
|
|
263
|
+
|
|
264
|
+
const SAMPLE_CARDS = [
|
|
265
|
+
{ id: "C-1", columnId: "todo", title: "Onboard Acme", subtitle: "Owner: Aria", badges: ["Priority"], status: "warning" },
|
|
266
|
+
{ id: "C-2", columnId: "doing", title: "Renew SLA", subtitle: "Owner: Devin", badges: [{ label: "Risk", color: "danger" }], status: "danger" },
|
|
267
|
+
{ id: "C-3", columnId: "done", title: "Q2 review prep", subtitle: "Owner: Priya", status: "success" },
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
const COLUMNS = [
|
|
271
|
+
{ id: "todo", title: "To Do", limit: 5, accent: "default" },
|
|
272
|
+
{ id: "doing", title: "In Progress", limit: 3, accent: "warning" }, // count > limit shows red ring on chip
|
|
273
|
+
{ id: "done", title: "Done", accent: "success" },
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
export default function PipelineBoard() {
|
|
277
|
+
const cards = useDataSource({ sample: SAMPLE_CARDS, live: [] });
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<KanbanBoard
|
|
281
|
+
title="Sprint Board"
|
|
282
|
+
subtitle="Q2 · Sprint 4"
|
|
283
|
+
columns={COLUMNS}
|
|
284
|
+
cards={cards}
|
|
285
|
+
isDraggable // enable @dnd-kit DnD (default true)
|
|
286
|
+
showColumnCounts // chip on each column header (default true)
|
|
287
|
+
showWipLimits // render "count / limit" when limit set (default true)
|
|
288
|
+
dense={false} // tighter card padding when true
|
|
289
|
+
accent="default" // fallback accent: default|primary|success|warning|danger|info
|
|
290
|
+
onCardClick={(card) => console.log(card.id)} // optional row click
|
|
291
|
+
onCardMove={({ cardId, fromColumnId, toColumnId, fromIndex, toIndex }) => {
|
|
292
|
+
// Optional. If provided, you own the state and must update `cards`.
|
|
293
|
+
// If omitted, KanbanBoard manages reorder/move internally.
|
|
294
|
+
}}
|
|
295
|
+
emptyMessage="No items" // shown in empty columns
|
|
296
|
+
loading={false}
|
|
297
|
+
error={null}
|
|
298
|
+
/>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Card shape:**
|
|
304
|
+
```js
|
|
305
|
+
{
|
|
306
|
+
id: "C-1", // required, unique
|
|
307
|
+
columnId: "todo", // required, must match one of columns[].id
|
|
308
|
+
title: "Onboard Acme", // required
|
|
309
|
+
subtitle: "Owner: Aria",
|
|
310
|
+
description: "Long-form text (clamped to 3 lines)",
|
|
311
|
+
status: "warning", // dot color: default|primary|success|warning|danger|info
|
|
312
|
+
accent: "primary", // overrides board accent for the card's left border
|
|
313
|
+
avatar: "/url" | <Icon /> | "AR" (auto-initials when omitted),
|
|
314
|
+
badges: ["Priority", { label: "Risk", color: "danger", size: "sm" }],
|
|
315
|
+
meta: ["Due Mon", { icon: <ClockIcon />, label: "2d" }],
|
|
316
|
+
actions: <UIButton size="sm">Open</UIButton>
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Column shape:**
|
|
321
|
+
```js
|
|
322
|
+
{
|
|
323
|
+
id: "todo", // required, unique
|
|
324
|
+
title: "To Do", // required
|
|
325
|
+
accent: "default" | "primary" | "success" | "warning" | "danger" | "info",
|
|
326
|
+
limit: 5, // WIP limit; over-limit columns get a red ring on the count chip
|
|
327
|
+
emptyMessage: "Nothing here yet", // overrides board-level emptyMessage
|
|
328
|
+
footer: <span>Capacity: 80%</span>,
|
|
329
|
+
actions: <UIButton size="xs">+</UIButton>
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Mistakes:**
|
|
334
|
+
- ❌ Forgetting `columnId` on a card → card won't render. Every card MUST have `columnId` matching a column.
|
|
335
|
+
- ❌ Passing the full cards list back into a `setCards` from `onCardMove` (the callback fires on intent only — you decide if/how to update). If you want default reorder behavior, just omit `onCardMove`.
|
|
336
|
+
- ❌ Using `KanbanBoard` for a simple sortable list — use `ListCard` instead.
|
|
337
|
+
- ❌ Wrapping `KanbanBoard` in another card (`WidgetCard`, `BaseCard`) — it already renders inside a `BaseCard`.
|
|
338
|
+
- ❌ Installing `react-beautiful-dnd` or `react-dnd` — only `@dnd-kit/*` is supported.
|
|
339
|
+
|
|
252
340
|
---
|
|
253
341
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
| A row of 2–4 KPIs | Grid of `MetricCard` | `MetricsStrip` (for compact inline) |
|
|
7
7
|
| A data table | `TableCard` | `<table>` HTML |
|
|
8
8
|
| A scrollable item list | `ListCard` | Custom divs with `.map()` |
|
|
9
|
+
| A board / swim-lanes (cards in columns) | `KanbanBoard` | Multiple stacked `ListCard`s |
|
|
9
10
|
| An activity log with status icons | `ActivityCard` | ListCard (no status icons) |
|
|
10
11
|
| A chart (line, bar) | `ChartCard` + `D3Chart` | Raw `<svg>` |
|
|
11
12
|
| A donut/pie chart | `ChartCard` + `D3Chart` + custom renderChart | Any chart library |
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,46 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.5.0] - 2026-05-07
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **`KanbanBoard` component** — board / swim-lane view for items in columns with drag-and-drop. Lives at `src/components/library/cards/KanbanBoard.jsx`, exported from the library barrel as `KanbanBoard`.
|
|
12
|
+
- Structured card shape: `{ id, columnId, title, subtitle?, description?, status?, accent?, avatar?, badges?, meta?, actions? }`.
|
|
13
|
+
- Structured column shape: `{ id, title, accent?, limit?, emptyMessage?, footer?, actions? }`.
|
|
14
|
+
- Drag-and-drop powered by `@dnd-kit/core` + `@dnd-kit/sortable` + `@dnd-kit/utilities` (new peer dependencies).
|
|
15
|
+
- Pointer + keyboard sensors for full a11y; drag overlay shows the active card; over-limit columns show a red ring on the count chip.
|
|
16
|
+
- `onCardMove({ cardId, fromColumnId, toColumnId, fromIndex, toIndex })` callback for state-owning parents; if omitted, the board manages reorder/move internally.
|
|
17
|
+
- `onCardClick(card)`, `loading`, `error`, `dense`, `accent`, `showColumnCounts`, `showWipLimits`, `emptyMessage` props.
|
|
18
|
+
- Renders inside its own `BaseCard` (do not wrap in another card).
|
|
19
|
+
- **Peer dependencies:** `@dnd-kit/core ^6.0.0`, `@dnd-kit/sortable ^8.0.0 || ^9.0.0 || ^10.0.0`, `@dnd-kit/utilities ^3.0.0`. Consuming projects must install these for `KanbanBoard` to work.
|
|
20
|
+
- **`vite.config.ts` externals updated** so `@dnd-kit/*` is not bundled into the library output.
|
|
21
|
+
|
|
22
|
+
### Documentation
|
|
23
|
+
- Added `KanbanBoard` row to `.a4drules/skills/component-library/when-to-use.md` and the corresponding "When to Use" table in `SKILL.md`.
|
|
24
|
+
- Added full `KanbanBoard` API reference to `.a4drules/skills/component-library/card-components.md` (card shape, column shape, mistakes, peer-dep install hint).
|
|
25
|
+
- Added `KanbanBoard` API summary to `.a4drules/skills/component-library/SKILL.md` Card Components section.
|
|
26
|
+
- Updated `.a4drules/skills/command-center-builder/SKILL.md`: added `KanbanBoard` to the frontmatter description, the canonical import block, and the "Need / MUST use" table.
|
|
27
|
+
- Added Do-Not entries forbidding alternative DnD libraries (`react-beautiful-dnd`, `react-dnd`), forgetting `columnId` on a card, and double-wrapping `KanbanBoard` in another card.
|
|
28
|
+
|
|
29
|
+
### Context
|
|
30
|
+
- Many command-center use cases (sprint boards, opportunity pipelines, ticket triage, approval queues) need a board layout. Without a library component, agents would either stack `ListCard`s (no DnD, no column counts/limits) or hand-roll a grid of columns (violates "every section is a library component"). `KanbanBoard` gives agents a single structured component that follows the same data-driven, sample/live-aware patterns as `ListCard` and `TableCard`.
|
|
31
|
+
|
|
32
|
+
## [2.4.0] - 2026-05-07
|
|
33
|
+
|
|
34
|
+
### Removed
|
|
35
|
+
- **Engine brand assets** — deleted `brands/engine/` (global.css, app/, sample data, PRD, GraphQL schema, Engine logo, Engine-specific hooks like `useEvaAgent` and `useEngineLiveData`).
|
|
36
|
+
- **Top-level `data/` directory** — removed all Engine Travel Command Center data, schema, agent API config, and the README/USAGE docs that documented Engine-only assets.
|
|
37
|
+
- **`brand:engine` and `demo:engine` npm scripts** — removed from `package.json` and from the postinstall script that adds scripts to consuming projects.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
- **`src/styles/global.css` is now truly neutral** — replaced the `--color-engine-*` palette and the Cyan/Engine-Black brand ramp with a slate-based neutral `--color-brand-*` ramp; replaced the Engine-coded `.heroui-scope` (Engine Black `#0D1117`, Cyan `#7DCBD9`, Orange-Red `#FD4B23`, Amber `#FFB200`, Engine Blue `#157DE5`) with neutral `oklch()` defaults. The `:root` `--dash-*` tokens (which previously used Engine amber/cream) now use neutral slate/standard semantic colors.
|
|
41
|
+
- **`scripts/apply-brand.mjs` is now brand-agnostic** — sample-data file copying discovers `*-sample-data.js` / `*-live-data.js` / `*-data.{js,ts}` files dynamically instead of hardcoding `engine-sample-data.js`, `engine-live-data.js`, `partner-hub-sample-data.js`. Logo copying discovers any `*.png|jpg|svg|webp|gif` file at the brand root instead of hardcoding `engine_logo.png`. Updated header docs to remove Engine examples.
|
|
42
|
+
- **`CLAUDE.md` Brand System section rewritten** — removed the "Engine Brand" callout and the `brand:engine` example. Now explains the brand system as optional infrastructure (no brands ship by default) and documents the conventions a `brands/<name>/` folder can use.
|
|
43
|
+
- **`README.md` Sample Data section removed** — the package no longer ships Engine Travel sample data.
|
|
44
|
+
|
|
45
|
+
### Context
|
|
46
|
+
- The package and its source files no longer carry any Engine Travel branding so it can be used as a neutral baseline for building a new app on top of the component library and Command Center builder. The brand system itself remains available; teams can drop their own brand under `brands/<name>/`.
|
|
47
|
+
|
|
8
48
|
## [2.3.17] - 2026-04-15
|
|
9
49
|
|
|
10
50
|
### Changed
|
package/CLAUDE.md
CHANGED
|
@@ -67,8 +67,7 @@ sf-web-components/
|
|
|
67
67
|
│ │ └── workspace/ # CommandCenter.tsx.template
|
|
68
68
|
│ ├── lib/ # Utilities (utils.ts)
|
|
69
69
|
│ └── styles/ # global.css with neutral brand tokens
|
|
70
|
-
├── brands/
|
|
71
|
-
│ └── engine/ # Engine brand (global.css, sample data, PRD, hooks)
|
|
70
|
+
├── brands/ # Optional brand bundles (none ship by default)
|
|
72
71
|
├── scripts/
|
|
73
72
|
│ ├── postinstall.mjs # Runs after npm install in consuming projects
|
|
74
73
|
│ ├── apply-brand.mjs # Apply/reset brand themes
|
|
@@ -86,23 +85,23 @@ sf-web-components/
|
|
|
86
85
|
|
|
87
86
|
## Brand System
|
|
88
87
|
|
|
89
|
-
The package ships with a **neutral theme** by default.
|
|
88
|
+
The package ships with a **neutral theme** by default. No brands ship out of the box; the brand system is optional infrastructure for layering custom themes/demo apps on top of the neutral baseline.
|
|
90
89
|
|
|
91
90
|
```bash
|
|
92
|
-
npm run brand:
|
|
93
|
-
npm run brand:reset #
|
|
94
|
-
npm run brand:list # Show available brands
|
|
91
|
+
npm run brand:list # Show available brands (empty by default)
|
|
92
|
+
npm run brand:reset # Restore neutral theme (clears any active brand)
|
|
95
93
|
```
|
|
96
94
|
|
|
97
|
-
|
|
95
|
+
To add a brand, drop a folder under `brands/<name>/` containing any of:
|
|
98
96
|
|
|
99
|
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
97
|
+
- `global.css` — overrides applied to `src/styles/global.css`
|
|
98
|
+
- `app/` — full demo app (pages, hooks, api, features) installed via `--demo`
|
|
99
|
+
- `*.png` / `*.svg` — logo assets installed into `src/assets/images/`
|
|
100
|
+
- `*-sample-data.js` / `*-live-data.js` — installed into `src/data/`
|
|
101
|
+
- `*_PRD.md` / `*-prd.md` — product requirements docs copied to project root
|
|
102
|
+
- `schema.graphql` — data schema copied to project root
|
|
104
103
|
|
|
105
|
-
|
|
104
|
+
When a brand is active, the reset script (`npm run reset:command-center`) preserves the brand's `global.css` instead of restoring neutral. The `.brand` marker file tracks which brand is active.
|
|
106
105
|
|
|
107
106
|
## Key Conventions
|
|
108
107
|
|
package/README.md
CHANGED
|
@@ -75,21 +75,6 @@ This library includes:
|
|
|
75
75
|
- Filter components
|
|
76
76
|
- Layout components
|
|
77
77
|
|
|
78
|
-
## Sample Data
|
|
79
|
-
|
|
80
|
-
The package includes pre-seeded sample data in the `data/` directory:
|
|
81
|
-
- **Engine Travel Command Center** sample data with real Salesforce field names
|
|
82
|
-
- Dashboard-ready data (map markers, chart data, metrics, etc.)
|
|
83
|
-
- Easy to swap for live data later
|
|
84
|
-
|
|
85
|
-
**Copy to your project:**
|
|
86
|
-
```bash
|
|
87
|
-
# From your webapp directory
|
|
88
|
-
cp node_modules/@schandlergarcia/sf-web-components/data/engine-sample-data.js src/data/
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
See `data/README.md` and `data/USAGE.md` in the package for full documentation.
|
|
92
|
-
|
|
93
78
|
## Utilities
|
|
94
79
|
|
|
95
80
|
```tsx
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { jsx as t, jsxs as c } from "react/jsx-runtime";
|
|
2
|
+
import K, { useState as F, useMemo as re, useCallback as U } from "react";
|
|
3
|
+
import { useSensors as ne, useSensor as V, PointerSensor as se, KeyboardSensor as le, DndContext as ae, closestCenter as ie, DragOverlay as de, useDroppable as oe } from "@dnd-kit/core";
|
|
4
|
+
import { sortableKeyboardCoordinates as ce, arrayMove as ue, SortableContext as me, verticalListSortingStrategy as fe, useSortable as he } from "@dnd-kit/sortable";
|
|
5
|
+
import { CSS as pe } from "@dnd-kit/utilities";
|
|
6
|
+
import ge from "./BaseCard.js";
|
|
7
|
+
import N from "../ui/Text.js";
|
|
8
|
+
import T from "../ui/Chip.js";
|
|
9
|
+
const B = {
|
|
10
|
+
default: "bg-slate-400",
|
|
11
|
+
primary: "bg-brand-500",
|
|
12
|
+
success: "bg-emerald-500",
|
|
13
|
+
warning: "bg-amber-500",
|
|
14
|
+
danger: "bg-rose-500",
|
|
15
|
+
info: "bg-sky-500"
|
|
16
|
+
}, _ = {
|
|
17
|
+
default: "border-l-slate-300 dark:border-l-slate-700",
|
|
18
|
+
primary: "border-l-brand-500",
|
|
19
|
+
success: "border-l-emerald-500",
|
|
20
|
+
warning: "border-l-amber-500",
|
|
21
|
+
danger: "border-l-rose-500",
|
|
22
|
+
info: "border-l-sky-500"
|
|
23
|
+
};
|
|
24
|
+
function be({ badges: e }) {
|
|
25
|
+
return !e || e.length === 0 ? null : /* @__PURE__ */ t("div", { className: "mt-2 flex flex-wrap gap-1.5", children: e.map((r, a) => K.isValidElement(r) ? /* @__PURE__ */ t("span", { children: r }, a) : typeof r == "string" ? /* @__PURE__ */ t(T, { size: "sm", variant: "default", children: r }, a) : /* @__PURE__ */ t(
|
|
26
|
+
T,
|
|
27
|
+
{
|
|
28
|
+
size: r.size ?? "sm",
|
|
29
|
+
variant: r.variant ?? "default",
|
|
30
|
+
color: r.color,
|
|
31
|
+
children: r.label ?? r.text
|
|
32
|
+
},
|
|
33
|
+
r.id ?? a
|
|
34
|
+
)) });
|
|
35
|
+
}
|
|
36
|
+
function xe({ avatar: e, title: r }) {
|
|
37
|
+
return e ? typeof e == "string" ? /* @__PURE__ */ t(
|
|
38
|
+
"img",
|
|
39
|
+
{
|
|
40
|
+
src: e,
|
|
41
|
+
alt: "",
|
|
42
|
+
className: "h-7 w-7 shrink-0 rounded-full border border-slate-200 object-cover dark:border-slate-800"
|
|
43
|
+
}
|
|
44
|
+
) : K.isValidElement(e) ? /* @__PURE__ */ t("div", { className: "grid h-7 w-7 shrink-0 place-items-center rounded-full border border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-800", children: e }) : /* @__PURE__ */ t("div", { className: "grid h-7 w-7 shrink-0 place-items-center rounded-full border border-slate-200 bg-slate-100 text-[10px] font-semibold text-slate-700 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-200", children: String(r ?? "??").slice(0, 2).toUpperCase() }) : null;
|
|
45
|
+
}
|
|
46
|
+
function M({ card: e, accent: r, dragging: a, onClick: s, dense: f }) {
|
|
47
|
+
const d = _[e?.accent ?? r ?? "default"] ?? _.default;
|
|
48
|
+
return /* @__PURE__ */ c(
|
|
49
|
+
s ? "button" : "div",
|
|
50
|
+
{
|
|
51
|
+
type: s ? "button" : void 0,
|
|
52
|
+
onClick: s ? () => s(e) : void 0,
|
|
53
|
+
className: [
|
|
54
|
+
"group block w-full rounded-xl border bg-white text-left shadow-sm transition",
|
|
55
|
+
"border-slate-200 dark:border-slate-800 dark:bg-slate-900",
|
|
56
|
+
"border-l-4",
|
|
57
|
+
d,
|
|
58
|
+
f ? "p-2.5" : "p-3",
|
|
59
|
+
a ? "opacity-60 ring-2 ring-brand-500" : "hover:-translate-y-[1px] hover:shadow-md"
|
|
60
|
+
].filter(Boolean).join(" "),
|
|
61
|
+
children: [
|
|
62
|
+
/* @__PURE__ */ c("div", { className: "flex items-start gap-2", children: [
|
|
63
|
+
/* @__PURE__ */ t(xe, { avatar: e.avatar, title: e.title }),
|
|
64
|
+
/* @__PURE__ */ c("div", { className: "min-w-0 flex-1", children: [
|
|
65
|
+
/* @__PURE__ */ t(N, { as: "div", size: "sm", weight: "medium", className: "truncate", children: e.title }),
|
|
66
|
+
e.subtitle ? /* @__PURE__ */ t(N, { as: "div", size: "xs", muted: !0, className: "mt-0.5 truncate", children: e.subtitle }) : null
|
|
67
|
+
] }),
|
|
68
|
+
e.status ? /* @__PURE__ */ t(
|
|
69
|
+
"span",
|
|
70
|
+
{
|
|
71
|
+
"aria-label": String(e.status),
|
|
72
|
+
className: [
|
|
73
|
+
"mt-1.5 h-2 w-2 shrink-0 rounded-full",
|
|
74
|
+
B[e.status] ?? B.default
|
|
75
|
+
].join(" ")
|
|
76
|
+
}
|
|
77
|
+
) : null
|
|
78
|
+
] }),
|
|
79
|
+
e.description ? /* @__PURE__ */ t(N, { as: "div", size: "xs", muted: !0, className: "mt-2 line-clamp-3 text-left", children: e.description }) : null,
|
|
80
|
+
/* @__PURE__ */ t(be, { badges: e.badges }),
|
|
81
|
+
e.meta && e.meta.length > 0 ? /* @__PURE__ */ t("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-slate-500 dark:text-slate-400", children: e.meta.map((l, x) => K.isValidElement(l) ? /* @__PURE__ */ t("span", { children: l }, x) : typeof l == "string" ? /* @__PURE__ */ t("span", { children: l }, x) : /* @__PURE__ */ c("span", { className: "inline-flex items-center gap-1", children: [
|
|
82
|
+
l.icon ? /* @__PURE__ */ t("span", { className: "opacity-70", children: l.icon }) : null,
|
|
83
|
+
/* @__PURE__ */ t("span", { children: l.label ?? l.text })
|
|
84
|
+
] }, l.id ?? x)) }) : null,
|
|
85
|
+
e.actions ? /* @__PURE__ */ t(
|
|
86
|
+
"div",
|
|
87
|
+
{
|
|
88
|
+
className: "mt-2 flex justify-end gap-1.5",
|
|
89
|
+
onClick: (l) => l.stopPropagation(),
|
|
90
|
+
children: e.actions
|
|
91
|
+
}
|
|
92
|
+
) : null
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
function ve({ card: e, accent: r, onClick: a, isDraggable: s, dense: f }) {
|
|
98
|
+
const d = he({
|
|
99
|
+
id: e.id,
|
|
100
|
+
data: { columnId: e.columnId, type: "card" },
|
|
101
|
+
disabled: !s
|
|
102
|
+
}), b = {
|
|
103
|
+
transform: pe.Translate.toString(d.transform),
|
|
104
|
+
transition: d.transition
|
|
105
|
+
};
|
|
106
|
+
return /* @__PURE__ */ t(
|
|
107
|
+
"div",
|
|
108
|
+
{
|
|
109
|
+
ref: d.setNodeRef,
|
|
110
|
+
style: b,
|
|
111
|
+
...s ? d.attributes : {},
|
|
112
|
+
...s ? d.listeners : {},
|
|
113
|
+
className: s ? "touch-none" : void 0,
|
|
114
|
+
children: /* @__PURE__ */ t(
|
|
115
|
+
M,
|
|
116
|
+
{
|
|
117
|
+
card: e,
|
|
118
|
+
accent: r,
|
|
119
|
+
dragging: d.isDragging,
|
|
120
|
+
onClick: a,
|
|
121
|
+
dense: f
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
function Ne({
|
|
128
|
+
column: e,
|
|
129
|
+
cards: r,
|
|
130
|
+
accent: a,
|
|
131
|
+
onCardClick: s,
|
|
132
|
+
isDraggable: f,
|
|
133
|
+
dense: d,
|
|
134
|
+
showCounts: b,
|
|
135
|
+
showLimits: l,
|
|
136
|
+
emptyMessage: x
|
|
137
|
+
}) {
|
|
138
|
+
const k = oe({
|
|
139
|
+
id: `column:${e.id}`,
|
|
140
|
+
data: { columnId: e.id, type: "column" }
|
|
141
|
+
}), y = r.length, w = e.limit != null && y > e.limit;
|
|
142
|
+
return /* @__PURE__ */ c("div", { className: "flex w-72 shrink-0 flex-col rounded-2xl border border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-900/60", children: [
|
|
143
|
+
/* @__PURE__ */ c("div", { className: "flex items-center justify-between gap-2 border-b border-slate-200 px-3 py-2 dark:border-slate-800", children: [
|
|
144
|
+
/* @__PURE__ */ c("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
145
|
+
/* @__PURE__ */ t(
|
|
146
|
+
"span",
|
|
147
|
+
{
|
|
148
|
+
"aria-hidden": !0,
|
|
149
|
+
className: [
|
|
150
|
+
"h-2 w-2 shrink-0 rounded-full",
|
|
151
|
+
B[e.accent ?? a ?? "default"] ?? B.default
|
|
152
|
+
].join(" ")
|
|
153
|
+
}
|
|
154
|
+
),
|
|
155
|
+
/* @__PURE__ */ t(N, { as: "div", size: "sm", weight: "medium", className: "truncate", children: e.title }),
|
|
156
|
+
b ? /* @__PURE__ */ t(
|
|
157
|
+
T,
|
|
158
|
+
{
|
|
159
|
+
size: "sm",
|
|
160
|
+
variant: "default",
|
|
161
|
+
className: w ? "ring-1 ring-rose-500" : void 0,
|
|
162
|
+
children: l && e.limit != null ? `${y} / ${e.limit}` : y
|
|
163
|
+
}
|
|
164
|
+
) : null
|
|
165
|
+
] }),
|
|
166
|
+
e.actions ? /* @__PURE__ */ t("div", { className: "shrink-0", children: e.actions }) : null
|
|
167
|
+
] }),
|
|
168
|
+
/* @__PURE__ */ t(
|
|
169
|
+
"div",
|
|
170
|
+
{
|
|
171
|
+
ref: k.setNodeRef,
|
|
172
|
+
className: [
|
|
173
|
+
"flex flex-1 flex-col gap-2 overflow-y-auto p-2",
|
|
174
|
+
k.isOver ? "bg-brand-50/50 dark:bg-brand-950/30" : ""
|
|
175
|
+
].filter(Boolean).join(" "),
|
|
176
|
+
style: { minHeight: 80 },
|
|
177
|
+
children: /* @__PURE__ */ t(
|
|
178
|
+
me,
|
|
179
|
+
{
|
|
180
|
+
items: r.map((h) => h.id),
|
|
181
|
+
strategy: fe,
|
|
182
|
+
children: r.length === 0 ? /* @__PURE__ */ t("div", { className: "grid flex-1 place-items-center px-2 py-6", children: /* @__PURE__ */ t(N, { as: "div", size: "xs", muted: !0, className: "text-center", children: e.emptyMessage ?? x ?? "No items" }) }) : r.map((h) => /* @__PURE__ */ t(
|
|
183
|
+
ve,
|
|
184
|
+
{
|
|
185
|
+
card: h,
|
|
186
|
+
accent: a,
|
|
187
|
+
onClick: s,
|
|
188
|
+
isDraggable: f,
|
|
189
|
+
dense: d
|
|
190
|
+
},
|
|
191
|
+
h.id
|
|
192
|
+
))
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
),
|
|
197
|
+
e.footer ? /* @__PURE__ */ t("div", { className: "border-t border-slate-200 px-3 py-2 text-xs text-slate-500 dark:border-slate-800 dark:text-slate-400", children: e.footer }) : null
|
|
198
|
+
] });
|
|
199
|
+
}
|
|
200
|
+
function De({
|
|
201
|
+
columns: e = [],
|
|
202
|
+
cards: r = [],
|
|
203
|
+
title: a,
|
|
204
|
+
subtitle: s,
|
|
205
|
+
accent: f = "default",
|
|
206
|
+
isDraggable: d = !0,
|
|
207
|
+
dense: b = !1,
|
|
208
|
+
showColumnCounts: l = !0,
|
|
209
|
+
showWipLimits: x = !0,
|
|
210
|
+
emptyMessage: k = "No items",
|
|
211
|
+
loading: y = !1,
|
|
212
|
+
error: w,
|
|
213
|
+
actions: h,
|
|
214
|
+
onCardClick: P,
|
|
215
|
+
onCardMove: R,
|
|
216
|
+
className: G = "",
|
|
217
|
+
...H
|
|
218
|
+
}) {
|
|
219
|
+
const [A, D] = F(null), [W, q] = F(null), u = W ?? r, J = ne(
|
|
220
|
+
V(se, { activationConstraint: { distance: 4 } }),
|
|
221
|
+
V(le, { coordinateGetter: ce })
|
|
222
|
+
), E = re(() => {
|
|
223
|
+
const n = {};
|
|
224
|
+
for (const o of e) n[o.id] = [];
|
|
225
|
+
for (const o of u) {
|
|
226
|
+
const i = o.columnId;
|
|
227
|
+
n[i] || (n[i] = []), n[i].push(o);
|
|
228
|
+
}
|
|
229
|
+
return n;
|
|
230
|
+
}, [e, u]), Q = U(
|
|
231
|
+
(n) => u.find((i) => i.id === n)?.columnId ?? null,
|
|
232
|
+
[u]
|
|
233
|
+
), X = U(
|
|
234
|
+
(n) => n ? typeof n == "string" && n.startsWith("column:") ? n.slice(7) : u.find((i) => i.id === n)?.columnId ?? null : null,
|
|
235
|
+
[u]
|
|
236
|
+
), Y = (n) => D(n.active.id), Z = (n) => {
|
|
237
|
+
D(null);
|
|
238
|
+
const { active: o, over: i } = n;
|
|
239
|
+
if (!i) return;
|
|
240
|
+
const p = Q(o.id), m = X(i.id);
|
|
241
|
+
if (!p || !m) return;
|
|
242
|
+
const C = (E[p] ?? []).slice(), I = p === m ? C : (E[m] ?? []).slice(), S = C.findIndex((v) => v.id === o.id);
|
|
243
|
+
let g;
|
|
244
|
+
if (i.data?.current?.type === "column" || i.id === `column:${m}` ? g = I.length : (g = I.findIndex((v) => v.id === i.id), g === -1 && (g = I.length)), p === m && S === g) return;
|
|
245
|
+
let L;
|
|
246
|
+
if (p === m) {
|
|
247
|
+
const v = ue(C, S, g);
|
|
248
|
+
L = [...u.filter((z) => z.columnId !== p), ...v];
|
|
249
|
+
} else {
|
|
250
|
+
const v = { ...C[S], columnId: m }, $ = C.filter((j) => j.id !== o.id), z = I.slice();
|
|
251
|
+
z.splice(g, 0, v), L = [...u.filter(
|
|
252
|
+
(j) => j.columnId !== p && j.columnId !== m
|
|
253
|
+
), ...$, ...z];
|
|
254
|
+
}
|
|
255
|
+
R ? R({
|
|
256
|
+
cardId: o.id,
|
|
257
|
+
fromColumnId: p,
|
|
258
|
+
toColumnId: m,
|
|
259
|
+
fromIndex: S,
|
|
260
|
+
toIndex: g
|
|
261
|
+
}) : q(L);
|
|
262
|
+
}, O = A ? u.find((n) => n.id === A) : null, ee = a || s || h ? /* @__PURE__ */ c("div", { className: "mb-3 flex items-start justify-between gap-3", children: [
|
|
263
|
+
/* @__PURE__ */ c("div", { className: "min-w-0", children: [
|
|
264
|
+
a ? /* @__PURE__ */ t(N, { as: "div", size: "sm", weight: "medium", children: a }) : null,
|
|
265
|
+
s ? /* @__PURE__ */ t(N, { as: "div", size: "xs", muted: !0, className: "mt-1", children: s }) : null
|
|
266
|
+
] }),
|
|
267
|
+
h ? /* @__PURE__ */ t("div", { className: "shrink-0", children: h }) : null
|
|
268
|
+
] }) : null, te = /* @__PURE__ */ t("div", { className: "flex gap-3 overflow-x-auto pb-1", children: e.map((n) => /* @__PURE__ */ t(
|
|
269
|
+
Ne,
|
|
270
|
+
{
|
|
271
|
+
column: n,
|
|
272
|
+
cards: E[n.id] ?? [],
|
|
273
|
+
accent: f,
|
|
274
|
+
onCardClick: P,
|
|
275
|
+
isDraggable: d,
|
|
276
|
+
dense: b,
|
|
277
|
+
showCounts: l,
|
|
278
|
+
showLimits: x,
|
|
279
|
+
emptyMessage: k
|
|
280
|
+
},
|
|
281
|
+
n.id
|
|
282
|
+
)) });
|
|
283
|
+
return /* @__PURE__ */ c(
|
|
284
|
+
ge,
|
|
285
|
+
{
|
|
286
|
+
padding: "default",
|
|
287
|
+
isLoading: y,
|
|
288
|
+
className: G,
|
|
289
|
+
...H,
|
|
290
|
+
children: [
|
|
291
|
+
ee,
|
|
292
|
+
w ? /* @__PURE__ */ t("div", { className: "rounded-md border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700 dark:border-rose-900/60 dark:bg-rose-950/40 dark:text-rose-300", children: String(w) }) : /* @__PURE__ */ c(
|
|
293
|
+
ae,
|
|
294
|
+
{
|
|
295
|
+
sensors: J,
|
|
296
|
+
collisionDetection: ie,
|
|
297
|
+
onDragStart: Y,
|
|
298
|
+
onDragEnd: Z,
|
|
299
|
+
onDragCancel: () => D(null),
|
|
300
|
+
children: [
|
|
301
|
+
te,
|
|
302
|
+
/* @__PURE__ */ t(de, { children: O ? /* @__PURE__ */ t(M, { card: O, accent: f, dragging: !0, dense: b }) : null })
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
)
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
export {
|
|
311
|
+
De as default
|
|
312
|
+
};
|
|
313
|
+
//# sourceMappingURL=KanbanBoard.js.map
|