@asteby/metacore-runtime-react 23.5.0 → 23.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 23.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 88214d1: DynamicKanban: automatizaciones de etapa (estilo Bitrix). Cada lane suma un botón ⚡ que abre un diálogo para configurar reglas "al entrar a esta etapa → agregar tag / quitar tag / setear campo = valor", genéricas para cualquier modelo. Las reglas viven en el backend vía el cliente HTTP existente (`/stage-automations`), con toggle de activación, eliminación e indicador de conteo activo en el header del lane. La feature es no-intrusiva: si el endpoint no existe (404/error) el afford ⚡ simplemente no se muestra y el tablero sigue funcionando. Todo el texto pasa por `t('dynamic.automations.*')` con `defaultValue` en español.
8
+
9
+ ## 23.5.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 36e7cb8: DynamicKanban: las lanes ahora aprovechan el ancho del viewport. Crecen (flex-1) para llenar el contenedor cuando todas caben, con un ancho mínimo legible (280px) y un máximo razonable (420px); el scroll horizontal solo aparece cuando ya no caben. Elimina el espacio muerto a la derecha y las columnas angostas al reducir la ventana.
14
+
3
15
  ## 23.5.0
4
16
 
5
17
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { Badge, Button, Card, CardContent, DropdownMenu, DropdownMenuContent, Dr
8
8
  import { ColumnFilterControl, FilterValueCombobox } from '@asteby/metacore-ui/data-table';
9
9
  import { generateBadgeStyles, optionColor } from '@asteby/metacore-ui/lib';
10
10
  import { useApi } from './api-context';
11
+ import { useStageAutomations, StageAutomationsButton, } from './stage-automations';
11
12
  import { useDynamicFilters } from './use-dynamic-filters';
12
13
  import { FilterChipsRow, summarizeFilterValues, translateOptionLabels, } from './filter-chips';
13
14
  import { dedupeById, useInfiniteScrollSentinel } from './use-infinite-scroll';
@@ -236,6 +237,9 @@ export function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, on
236
237
  const { t, i18n } = useTranslation();
237
238
  const api = useApi();
238
239
  const isDark = useIsDarkTheme();
240
+ // Stage automations (Bitrix-style per-lane rules). Degrades to no-op when
241
+ // the host has no `/stage-automations` endpoint — the ⚡ affordance hides.
242
+ const automations = useStageAutomations(model);
239
243
  const { getMetadata, setMetadata: cacheMetadata } = useMetadataCache();
240
244
  const cachedMeta = getMetadata(model);
241
245
  const [metadata, setMetadata] = useState(cachedMeta || null);
@@ -588,7 +592,7 @@ export function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, on
588
592
  }
589
593
  }, [api, endpoint, groupByKey, lanePagination, model, records, stageOfCard, t, transitions]);
590
594
  if (loading) {
591
- return (_jsx("div", { className: "flex gap-4 overflow-x-auto p-1", children: [0, 1, 2, 3].map((i) => (_jsxs("div", { className: "w-[300px] shrink-0 space-y-3", children: [_jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-24 w-full" }), _jsx(Skeleton, { className: "h-24 w-full" })] }, i))) }));
595
+ return (_jsx("div", { className: "flex w-full gap-4 overflow-x-auto p-1", children: [0, 1, 2, 3].map((i) => (_jsxs("div", { className: "min-w-[280px] max-w-[420px] flex-1 shrink-0 space-y-3", children: [_jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-24 w-full" }), _jsx(Skeleton, { className: "h-24 w-full" })] }, i))) }));
592
596
  }
593
597
  if (!metadata || !groupByKey || stages.length === 0) {
594
598
  return (_jsx("div", { className: "rounded-md border border-dashed p-8 text-center text-sm text-muted-foreground", children: t('kanban.noStages', {
@@ -615,7 +619,7 @@ export function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, on
615
619
  })
616
620
  : t('kanban.noActiveFilters', {
617
621
  defaultValue: 'Sin filtros',
618
- }) }), _jsxs(Button, { variant: "ghost", size: "sm", className: "h-7 gap-1 text-xs", onClick: clearAll, disabled: activeFilterCount === 0, children: [_jsx(X, { className: "h-3.5 w-3.5" }), t('kanban.clearAll', { defaultValue: 'Limpiar todo' }), activeFilterCount > 0 ? ` (${activeFilterCount})` : ''] })] })] })] }))] }), _jsx(FilterChipsRow, { fields: activeFields, onClearAll: clearAll, "data-testid": "kanban-filter-chips" }), _jsxs(DndContext, { sensors: sensors, onDragStart: onDragStart, onDragEnd: onDragEnd, children: [_jsx("div", { className: "flex min-w-0 gap-4 overflow-x-auto p-1", "data-testid": "kanban-board", children: lanes.map((stage) => {
622
+ }) }), _jsxs(Button, { variant: "ghost", size: "sm", className: "h-7 gap-1 text-xs", onClick: clearAll, disabled: activeFilterCount === 0, children: [_jsx(X, { className: "h-3.5 w-3.5" }), t('kanban.clearAll', { defaultValue: 'Limpiar todo' }), activeFilterCount > 0 ? ` (${activeFilterCount})` : ''] })] })] })] }))] }), _jsx(FilterChipsRow, { fields: activeFields, onClearAll: clearAll, "data-testid": "kanban-filter-chips" }), _jsxs(DndContext, { sensors: sensors, onDragStart: onDragStart, onDragEnd: onDragEnd, children: [_jsx("div", { className: "flex w-full min-w-0 gap-4 overflow-x-auto p-1", "data-testid": "kanban-board", children: lanes.map((stage) => {
619
623
  const allCards = grouped.get(stage.key) ?? [];
620
624
  // Per-lane client-side narrowing (instant, scoped to this
621
625
  // stage). The funnel (field/value) and the lane search
@@ -640,7 +644,7 @@ export function DynamicKanban({ model, endpoint, refreshTrigger, onCardClick, on
640
644
  field: f?.field,
641
645
  values: f?.values,
642
646
  text: f?.text,
643
- }), onQueryChange: (q) => updateLaneFilter(stage.key, { query: q }), isDark: isDark, dimmed: !!activeId && !droppableAllowed, disabled: !!activeId && !droppableAllowed, children: loadingData && cards.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20 w-full" }), _jsx(Skeleton, { className: "h-20 w-full" })] })) : cards.length === 0 ? (_jsx("p", { className: "px-1 py-6 text-center text-xs text-muted-foreground", children: t('kanban.emptyLane', { defaultValue: 'Sin tarjetas' }) })) : (cards.map((card) => (_jsx(KanbanCard, { card: card, titleCol: titleCol, fieldCols: fieldCols, actions: rowActions, locale: i18n.language, timeZone: timeZone, currency: currency, onClick: onCardClick, onAction: handleInternalAction }, String(card.id))))) }, stage.key));
647
+ }), onQueryChange: (q) => updateLaneFilter(stage.key, { query: q }), isDark: isDark, dimmed: !!activeId && !droppableAllowed, disabled: !!activeId && !droppableAllowed, model: model, columns: metadata?.columns ?? [], automationsAvailable: automations.available && stage.key !== UNASSIGNED_LANE, automationRules: automations.byStage.get(stage.key) ?? [], onAutomationCreate: automations.create, onAutomationUpdate: automations.update, onAutomationRemove: automations.remove, children: loadingData && cards.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-20 w-full" }), _jsx(Skeleton, { className: "h-20 w-full" })] })) : cards.length === 0 ? (_jsx("p", { className: "px-1 py-6 text-center text-xs text-muted-foreground", children: t('kanban.emptyLane', { defaultValue: 'Sin tarjetas' }) })) : (cards.map((card) => (_jsx(KanbanCard, { card: card, titleCol: titleCol, fieldCols: fieldCols, actions: rowActions, locale: i18n.language, timeZone: timeZone, currency: currency, onClick: onCardClick, onAction: handleInternalAction }, String(card.id))))) }, stage.key));
644
648
  }) }), _jsx(DragOverlay, { children: activeCard ? (_jsx(CardPreview, { card: activeCard, titleCol: titleCol, fieldCols: fieldCols, locale: i18n.language, timeZone: timeZone, currency: currency })) : null }), rowActionDialogs] })] }));
645
649
  }
646
650
  /**
@@ -670,7 +674,7 @@ function SheetFilterRow({ field, isStage, }) {
670
674
  const summary = summarizeFilterValues(field.config.selectedValues, field.config.options);
671
675
  return (_jsx(ColumnFilterControl, { variant: "row", align: "end", icon: filterTypeIcon(field.config.filterType, isStage), label: field.label, valueSummary: summary, filterKey: field.config.filterKey, filterType: field.config.filterType, filterOptions: field.config.options, filterLoading: field.config.loading, filterSearchEndpoint: field.config.searchEndpoint, selectedValues: field.config.selectedValues, onFilterChange: field.config.onFilterChange, loadOptions: field.config.loadOptions }));
672
676
  }
673
- function KanbanLane({ stage, count, totalCount, serverTotal, hasMore, loadingMore, onLoadMore, filterFields, laneFilter, onFunnelChange, onQueryChange, isDark, dimmed, disabled, children, }) {
677
+ function KanbanLane({ stage, count, totalCount, serverTotal, hasMore, loadingMore, onLoadMore, filterFields, laneFilter, onFunnelChange, onQueryChange, isDark, dimmed, disabled, model, columns, automationsAvailable, automationRules, onAutomationCreate, onAutomationUpdate, onAutomationRemove, children, }) {
674
678
  const { t } = useTranslation();
675
679
  const { setNodeRef, isOver } = useDroppable({ id: stage.key, disabled });
676
680
  // Infinite scroll: the sentinel lives at the bottom of the lane's own scroll
@@ -710,13 +714,18 @@ function KanbanLane({ stage, count, totalCount, serverTotal, hasMore, loadingMor
710
714
  text: laneFilter.text,
711
715
  }
712
716
  : undefined;
713
- return (_jsxs("div", { ref: setNodeRef, className: "group/lane flex w-[300px] shrink-0 flex-col rounded-xl border bg-muted/30 transition-opacity", style: {
717
+ return (_jsxs("div", { ref: setNodeRef,
718
+ // Fluid width: lanes grow (flex-1) to fill the board when they all
719
+ // fit, capped at max-w so a couple of lanes don't stretch absurdly
720
+ // wide. Below min-w the board's overflow-x-auto takes over and the
721
+ // lanes scroll horizontally at their minimum width.
722
+ className: "group/lane flex min-w-[280px] max-w-[420px] flex-1 shrink-0 flex-col rounded-xl border bg-muted/30 transition-opacity", style: {
714
723
  opacity: dimmed ? 0.45 : 1,
715
724
  outline: isOver && !disabled ? '2px solid var(--ring, #3b82f6)' : 'none',
716
725
  outlineOffset: 2,
717
726
  }, "data-stage": stage.key, "data-disabled": disabled || undefined, children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(Badge, { variant: "outline", className: "border-0 text-xs font-semibold", style: headerStyle, children: t(stage.label, { defaultValue: stage.label }) }), _jsx("span", { className: "text-xs font-medium tabular-nums text-muted-foreground", children: formatLaneCount(count, totalCount, serverTotal, laneActive) })] }), _jsxs("div", { className: "flex items-center gap-0.5", children: [_jsxs("button", { type: "button", onClick: () => setSearchOpen((o) => !o), className: `relative flex size-6 items-center justify-center rounded-md transition-colors hover:bg-accent hover:text-foreground ${queryActive ? 'text-primary' : 'text-muted-foreground'}`, "aria-label": t('kanban.searchLane', {
718
727
  defaultValue: 'Buscar en la columna',
719
- }), children: [_jsx(Search, { className: "h-3.5 w-3.5" }), queryActive && (_jsx("span", { className: "absolute -right-0.5 -top-0.5 size-1.5 rounded-full bg-primary" }))] }), _jsx(LaneFilterButton, { fields: filterFields, value: funnelValue, onChange: onFunnelChange })] })] }), searchOpen && (_jsx("div", { className: "px-3 pb-1.5", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { ref: searchRef, value: laneFilter?.query ?? '', onChange: (e) => onQueryChange(e.target.value), onKeyDown: (e) => {
728
+ }), children: [_jsx(Search, { className: "h-3.5 w-3.5" }), queryActive && (_jsx("span", { className: "absolute -right-0.5 -top-0.5 size-1.5 rounded-full bg-primary" }))] }), _jsx(LaneFilterButton, { fields: filterFields, value: funnelValue, onChange: onFunnelChange }), automationsAvailable && (_jsx(StageAutomationsButton, { model: model, stageKey: stage.key, stageLabel: stage.label, columns: columns, rules: automationRules, onCreate: onAutomationCreate, onUpdate: onAutomationUpdate, onRemove: onAutomationRemove }))] })] }), searchOpen && (_jsx("div", { className: "px-3 pb-1.5", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { ref: searchRef, value: laneFilter?.query ?? '', onChange: (e) => onQueryChange(e.target.value), onKeyDown: (e) => {
720
729
  if (e.key === 'Escape') {
721
730
  onQueryChange('');
722
731
  setSearchOpen(false);
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './types';
2
2
  export * from './options-context';
3
3
  export * from './dynamic-table';
4
4
  export { DynamicKanban, type DynamicKanbanProps, deriveStages, groupByStage, isTransitionAllowed, applyOptimisticMove, selectCardColumns, UNASSIGNED_LANE, } from './dynamic-kanban';
5
+ export { useStageAutomations, StageAutomationsButton, isTagColumn, automationFieldOptions, groupAutomationsByStage, activeAutomationCount, type StageAutomation, type StageAutomationAction, type StageAutomationActionType, type NewStageAutomation, type UseStageAutomationsResult, type StageAutomationsButtonProps, } from './stage-automations';
5
6
  export { DynamicView, resolveViewRenderer, readViewFromSearch, resolveActiveView, type DynamicViewProps, } from './dynamic-view';
6
7
  export { useDynamicFilters, type UseDynamicFiltersOptions, type UseDynamicFiltersResult, } from './use-dynamic-filters';
7
8
  export * from './dynamic-form';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,SAAS,CAAA;AACvB,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,OAAO,EACH,aAAa,EACb,KAAK,kBAAkB,EACvB,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACH,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,gBAAgB,GACxB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACH,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC/B,MAAM,uBAAuB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACH,qBAAqB,EACrB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,kBAAkB,EAClB,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACvB,MAAM,wBAAwB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACH,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,WAAW,EAChB,KAAK,wBAAwB,GAChC,MAAM,wBAAwB,CAAA;AAC/B,cAAc,QAAQ,CAAA;AACtB,cAAc,mBAAmB,CAAA;AACjC,OAAO,EACH,mBAAmB,EACnB,MAAM,EACN,oBAAoB,EACpB,OAAO,EACP,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,KAAK,EACV,KAAK,wBAAwB,GAChC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,KAAK,iBAAiB,GACzB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,OAAO,EACZ,KAAK,SAAS,GACjB,MAAM,uBAAuB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,OAAO,EACH,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,8BAA8B,GACtC,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACH,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,WAAW,EACX,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,GAC9B,MAAM,yBAAyB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,YAAY,EACR,kBAAkB,EAClB,YAAY,IAAI,yBAAyB,EACzC,iBAAiB,EACjB,oBAAoB,GACvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,wBAAwB,EACxB,4BAA4B,EAC5B,cAAc,EACd,oBAAoB,EACpB,KAAK,qBAAqB,GAC7B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EACH,OAAO,EACP,QAAQ,EACR,cAAc,EACd,UAAU,EACV,QAAQ,EACR,WAAW,EACX,WAAW,EACX,UAAU,EACV,SAAS,EACT,UAAU,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,KAAK,OAAO,EACZ,KAAK,cAAc,GACtB,MAAM,YAAY,CAAA;AACnB,OAAO,EACH,cAAc,EACd,YAAY,EACZ,WAAW,EACX,UAAU,EACV,KAAK,mBAAmB,EACxB,KAAK,SAAS,IAAI,uBAAuB,GAC5C,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzE,YAAY,EAAE,wBAAwB,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC5G,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAC/D,YAAY,EACR,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,GACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC9B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,GACjB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACH,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,KAAK,qBAAqB,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GAC3B,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACH,sBAAsB,EACtB,uBAAuB,GAC1B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,kBAAkB,EAClB,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,GAChC,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,eAAe,GACvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,qBAAqB,EACrB,KAAK,0BAA0B,GAClC,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACzB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACH,aAAa,EACb,KAAK,kBAAkB,GAC1B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACH,gBAAgB,EAChB,KAAK,qBAAqB,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,aAAa,EACb,eAAe,GAClB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACR,UAAU,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACH,UAAU,EACV,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,EACd,KAAK,iBAAiB,GACzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,cAAc,EACd,cAAc,EACd,SAAS,EACT,UAAU,EACV,KAAK,mBAAmB,GAC3B,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,UAAU,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,KAAK,eAAe,GACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,eAAe,GACvB,MAAM,yBAAyB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ export * from './types';
7
7
  export * from './options-context';
8
8
  export * from './dynamic-table';
9
9
  export { DynamicKanban, deriveStages, groupByStage, isTransitionAllowed, applyOptimisticMove, selectCardColumns, UNASSIGNED_LANE, } from './dynamic-kanban';
10
+ export { useStageAutomations, StageAutomationsButton, isTagColumn, automationFieldOptions, groupAutomationsByStage, activeAutomationCount, } from './stage-automations';
10
11
  export { DynamicView, resolveViewRenderer, readViewFromSearch, resolveActiveView, } from './dynamic-view';
11
12
  export { useDynamicFilters, } from './use-dynamic-filters';
12
13
  export * from './dynamic-form';
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ import type { ApiClient } from './api-context';
3
+ import type { ColumnDefinition } from './types';
4
+ export type StageAutomationActionType = 'add_tag' | 'remove_tag' | 'set_field';
5
+ export interface StageAutomationAction {
6
+ type: StageAutomationActionType;
7
+ field: string;
8
+ value: string;
9
+ }
10
+ export interface StageAutomation {
11
+ id: string | number;
12
+ model: string;
13
+ /** Source stage the card is leaving. `'*'` = any stage. */
14
+ from_stage: string;
15
+ /** Destination stage that triggers the rule (the lane's stage key). */
16
+ to_stage: string;
17
+ actions: StageAutomationAction[];
18
+ enabled: boolean;
19
+ }
20
+ /** Draft for a new rule — a single action, keyed to a destination stage. */
21
+ export interface NewStageAutomation {
22
+ model: string;
23
+ from_stage: string;
24
+ to_stage: string;
25
+ actions: StageAutomationAction[];
26
+ enabled: boolean;
27
+ }
28
+ /** Whether a column holds a tag/json array (valid target for add/remove_tag). */
29
+ export declare function isTagColumn(col: ColumnDefinition): boolean;
30
+ /**
31
+ * Columns a given action type may target:
32
+ * - add_tag / remove_tag → only tag/json columns
33
+ * - set_field → any editable (non-readonly, non-hidden) column
34
+ * Hidden and readonly columns are never offered.
35
+ */
36
+ export declare function automationFieldOptions(columns: ColumnDefinition[], actionType: StageAutomationActionType): ColumnDefinition[];
37
+ /** Rules grouped by their destination stage key (only what the lane needs). */
38
+ export declare function groupAutomationsByStage(rules: StageAutomation[]): Map<string, StageAutomation[]>;
39
+ /** Count of ENABLED rules for a stage — drives the lane's ⚡ indicator. */
40
+ export declare function activeAutomationCount(rules: StageAutomation[] | undefined): number;
41
+ export interface UseStageAutomationsResult {
42
+ /** False when the endpoint is missing/errored — hide the ⚡ affordance. */
43
+ available: boolean;
44
+ loading: boolean;
45
+ /** Destination-stage-keyed rules. */
46
+ byStage: Map<string, StageAutomation[]>;
47
+ create: (draft: NewStageAutomation) => Promise<void>;
48
+ update: (id: StageAutomation['id'], patch: Partial<StageAutomation>) => Promise<void>;
49
+ remove: (id: StageAutomation['id']) => Promise<void>;
50
+ }
51
+ /**
52
+ * Loads a model's stage automations and exposes CRUD. Swallows a missing
53
+ * endpoint (404 / network error) into `available: false` so the kanban never
54
+ * breaks; real mutation failures surface a toast and re-throw so the dialog can
55
+ * keep its draft.
56
+ */
57
+ export declare function useStageAutomations(model: string): UseStageAutomationsResult;
58
+ export interface StageAutomationsButtonProps {
59
+ model: string;
60
+ /** The lane's stage key + human label (destination that triggers rules). */
61
+ stageKey: string;
62
+ stageLabel: string;
63
+ columns: ColumnDefinition[];
64
+ rules: StageAutomation[];
65
+ onCreate: (draft: NewStageAutomation) => Promise<void>;
66
+ onUpdate: (id: StageAutomation['id'], patch: Partial<StageAutomation>) => Promise<void>;
67
+ onRemove: (id: StageAutomation['id']) => Promise<void>;
68
+ }
69
+ /**
70
+ * The per-lane ⚡ button: shows an active-rule count badge and opens the rule
71
+ * editor for THIS stage. Rendered only when the endpoint is available.
72
+ */
73
+ export declare function StageAutomationsButton({ model, stageKey, stageLabel, columns, rules, onCreate, onUpdate, onRemove, }: StageAutomationsButtonProps): React.JSX.Element;
74
+ export type { ApiClient };
75
+ //# sourceMappingURL=stage-automations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-automations.d.ts","sourceRoot":"","sources":["../src/stage-automations.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,264 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // Stage automations — a Bitrix-style "when a card enters this stage, run these
3
+ // actions" rule editor, surfaced per-lane in DynamicKanban. The feature is
4
+ // generic over any model: rules live server-side (the host wires the
5
+ // `/stage-automations` REST endpoints against the same api client the rest of
6
+ // the dynamic runtime uses) and reference the model's own columns.
7
+ //
8
+ // Non-intrusive by design: if the endpoint 404s or errors, the hook reports
9
+ // `available: false` and the lane simply omits the ⚡ affordance — the board
10
+ // keeps working. All UI text goes through t() with a Spanish defaultValue so a
11
+ // host that ships no `dynamic.automations.*` keys still renders in Spanish.
12
+ import { useCallback, useEffect, useMemo, useState } from 'react';
13
+ import { useTranslation } from 'react-i18next';
14
+ import { Zap, Trash2, Plus } from 'lucide-react';
15
+ import { toast } from 'sonner';
16
+ import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, } from '@asteby/metacore-ui/primitives';
17
+ import { useApi } from './api-context';
18
+ // ---------------------------------------------------------------------------
19
+ // Pure helpers (exported for unit tests — no React, no transport)
20
+ // ---------------------------------------------------------------------------
21
+ const ACTION_TYPES = [
22
+ 'add_tag',
23
+ 'remove_tag',
24
+ 'set_field',
25
+ ];
26
+ /** Whether a column holds a tag/json array (valid target for add/remove_tag). */
27
+ export function isTagColumn(col) {
28
+ const t = col.type;
29
+ const style = col.cellStyle;
30
+ return t === 'tags' || t === 'json' || style === 'tags';
31
+ }
32
+ /**
33
+ * Columns a given action type may target:
34
+ * - add_tag / remove_tag → only tag/json columns
35
+ * - set_field → any editable (non-readonly, non-hidden) column
36
+ * Hidden and readonly columns are never offered.
37
+ */
38
+ export function automationFieldOptions(columns, actionType) {
39
+ return columns.filter((c) => {
40
+ if (c.hidden)
41
+ return false;
42
+ const ro = !!c.readonly ||
43
+ !!c.read_only;
44
+ if (ro)
45
+ return false;
46
+ if (actionType === 'add_tag' || actionType === 'remove_tag') {
47
+ return isTagColumn(c);
48
+ }
49
+ return true;
50
+ });
51
+ }
52
+ /** Rules grouped by their destination stage key (only what the lane needs). */
53
+ export function groupAutomationsByStage(rules) {
54
+ const map = new Map();
55
+ for (const r of rules) {
56
+ const arr = map.get(r.to_stage) ?? [];
57
+ arr.push(r);
58
+ map.set(r.to_stage, arr);
59
+ }
60
+ return map;
61
+ }
62
+ /** Count of ENABLED rules for a stage — drives the lane's ⚡ indicator. */
63
+ export function activeAutomationCount(rules) {
64
+ if (!rules)
65
+ return 0;
66
+ return rules.filter((r) => r.enabled).length;
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // Data hook
70
+ // ---------------------------------------------------------------------------
71
+ function unwrap(res) {
72
+ const body = res?.data;
73
+ // Envelope {success, data}; tolerate a bare array/object too.
74
+ if (body && typeof body === 'object' && 'data' in body)
75
+ return body.data;
76
+ return body;
77
+ }
78
+ /**
79
+ * Loads a model's stage automations and exposes CRUD. Swallows a missing
80
+ * endpoint (404 / network error) into `available: false` so the kanban never
81
+ * breaks; real mutation failures surface a toast and re-throw so the dialog can
82
+ * keep its draft.
83
+ */
84
+ export function useStageAutomations(model) {
85
+ const api = useApi();
86
+ const { t } = useTranslation();
87
+ const [available, setAvailable] = useState(true);
88
+ const [loading, setLoading] = useState(true);
89
+ const [rules, setRules] = useState([]);
90
+ const load = useCallback(async () => {
91
+ setLoading(true);
92
+ try {
93
+ const res = await api.get(`/stage-automations?model=${encodeURIComponent(model)}`);
94
+ const data = unwrap(res);
95
+ setRules(Array.isArray(data) ? data : []);
96
+ setAvailable(true);
97
+ }
98
+ catch {
99
+ // Endpoint absent or errored — degrade silently.
100
+ setRules([]);
101
+ setAvailable(false);
102
+ }
103
+ finally {
104
+ setLoading(false);
105
+ }
106
+ }, [api, model]);
107
+ useEffect(() => {
108
+ void load();
109
+ }, [load]);
110
+ const create = useCallback(async (draft) => {
111
+ try {
112
+ const res = await api.post('/stage-automations', draft);
113
+ const created = unwrap(res);
114
+ await load();
115
+ if (!created)
116
+ return;
117
+ }
118
+ catch (e) {
119
+ toast.error(t('dynamic.automations.saveError', {
120
+ defaultValue: 'No se pudo guardar la automatización',
121
+ }));
122
+ throw e;
123
+ }
124
+ }, [api, load, t]);
125
+ const update = useCallback(async (id, patch) => {
126
+ try {
127
+ await api.put(`/stage-automations/${id}`, patch);
128
+ await load();
129
+ }
130
+ catch (e) {
131
+ toast.error(t('dynamic.automations.saveError', {
132
+ defaultValue: 'No se pudo guardar la automatización',
133
+ }));
134
+ throw e;
135
+ }
136
+ }, [api, load, t]);
137
+ const remove = useCallback(async (id) => {
138
+ try {
139
+ await api.delete(`/stage-automations/${id}`);
140
+ await load();
141
+ }
142
+ catch (e) {
143
+ toast.error(t('dynamic.automations.deleteError', {
144
+ defaultValue: 'No se pudo eliminar la automatización',
145
+ }));
146
+ throw e;
147
+ }
148
+ }, [api, load, t]);
149
+ const byStage = useMemo(() => groupAutomationsByStage(rules), [rules]);
150
+ return { available, loading, byStage, create, update, remove };
151
+ }
152
+ /**
153
+ * The per-lane ⚡ button: shows an active-rule count badge and opens the rule
154
+ * editor for THIS stage. Rendered only when the endpoint is available.
155
+ */
156
+ export function StageAutomationsButton({ model, stageKey, stageLabel, columns, rules, onCreate, onUpdate, onRemove, }) {
157
+ const { t } = useTranslation();
158
+ const [open, setOpen] = useState(false);
159
+ const activeCount = activeAutomationCount(rules);
160
+ return (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", onClick: () => setOpen(true), className: `relative flex size-6 items-center justify-center rounded-md transition-colors hover:bg-accent hover:text-foreground ${activeCount > 0 ? 'text-primary' : 'text-muted-foreground'}`, "aria-label": t('dynamic.automations.open', {
161
+ defaultValue: 'Automatizaciones de la etapa',
162
+ }), "data-testid": `automations-trigger-${stageKey}`, children: [_jsx(Zap, { className: "h-3.5 w-3.5" }), activeCount > 0 && (_jsx("span", { className: "absolute -right-1 -top-1 flex min-w-3.5 items-center justify-center rounded-full bg-primary px-1 text-[9px] font-semibold leading-none text-primary-foreground", children: activeCount }))] }), open && (_jsx(StageAutomationsDialog, { open: open, onOpenChange: setOpen, model: model, stageKey: stageKey, stageLabel: stageLabel, columns: columns, rules: rules, onCreate: onCreate, onUpdate: onUpdate, onRemove: onRemove }))] }));
163
+ }
164
+ function describeAction(action, columns, t) {
165
+ const col = columns.find((c) => c.key === action.field);
166
+ const fieldLabel = col ? t(col.label, { defaultValue: col.label }) : action.field;
167
+ switch (action.type) {
168
+ case 'add_tag':
169
+ return t('dynamic.automations.summary.addTag', {
170
+ defaultValue: 'Agregar tag "{{value}}" a {{field}}',
171
+ value: action.value,
172
+ field: fieldLabel,
173
+ });
174
+ case 'remove_tag':
175
+ return t('dynamic.automations.summary.removeTag', {
176
+ defaultValue: 'Quitar tag "{{value}}" de {{field}}',
177
+ value: action.value,
178
+ field: fieldLabel,
179
+ });
180
+ default:
181
+ return t('dynamic.automations.summary.setField', {
182
+ defaultValue: 'Setear {{field}} = "{{value}}"',
183
+ value: action.value,
184
+ field: fieldLabel,
185
+ });
186
+ }
187
+ }
188
+ function StageAutomationsDialog({ open, onOpenChange, model, stageKey, stageLabel, columns, rules, onCreate, onUpdate, onRemove, }) {
189
+ const { t } = useTranslation();
190
+ const [actionType, setActionType] = useState('add_tag');
191
+ const [field, setField] = useState('');
192
+ const [value, setValue] = useState('');
193
+ const [saving, setSaving] = useState(false);
194
+ const fieldChoices = useMemo(() => automationFieldOptions(columns, actionType), [columns, actionType]);
195
+ // Keep the selected field valid for the current action type.
196
+ useEffect(() => {
197
+ if (!fieldChoices.some((c) => c.key === field)) {
198
+ setField(fieldChoices[0]?.key ?? '');
199
+ }
200
+ }, [fieldChoices, field]);
201
+ const label = t(stageLabel, { defaultValue: stageLabel });
202
+ const canSubmit = !!field && !!value.trim() && !saving;
203
+ const submit = async () => {
204
+ if (!canSubmit)
205
+ return;
206
+ setSaving(true);
207
+ try {
208
+ await onCreate({
209
+ model,
210
+ from_stage: '*',
211
+ to_stage: stageKey,
212
+ actions: [{ type: actionType, field, value: value.trim() }],
213
+ enabled: true,
214
+ });
215
+ setValue('');
216
+ toast.success(t('dynamic.automations.saved', {
217
+ defaultValue: 'Automatización guardada',
218
+ }));
219
+ }
220
+ catch {
221
+ // toast already surfaced by the hook; keep the draft.
222
+ }
223
+ finally {
224
+ setSaving(false);
225
+ }
226
+ };
227
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t('dynamic.automations.title', {
228
+ defaultValue: 'Automatizaciones · {{stage}}',
229
+ stage: label,
230
+ }) }), _jsx(DialogDescription, { children: t('dynamic.automations.description', {
231
+ defaultValue: 'Al entrar una tarjeta a esta etapa se ejecutan estas acciones.',
232
+ }) })] }), _jsx("div", { className: "flex flex-col gap-2", children: rules.length === 0 ? (_jsx("p", { className: "rounded-md border border-dashed px-3 py-6 text-center text-sm text-muted-foreground", children: t('dynamic.automations.empty', {
233
+ defaultValue: 'Sin automatizaciones para esta etapa.',
234
+ }) })) : (rules.map((rule) => (_jsxs("div", { className: "flex items-center gap-2 rounded-md border px-3 py-2", "data-testid": `automation-rule-${rule.id}`, children: [_jsx("div", { className: "min-w-0 flex-1", children: rule.actions.map((a, i) => (_jsx("p", { className: `truncate text-sm ${rule.enabled ? '' : 'text-muted-foreground line-through'}`, children: describeAction(a, columns, t) }, i))) }), _jsx(Switch, { checked: rule.enabled, onCheckedChange: (checked) => void onUpdate(rule.id, { enabled: checked }), "aria-label": t('dynamic.automations.toggle', {
235
+ defaultValue: 'Activar automatización',
236
+ }), "data-testid": `automation-toggle-${rule.id}` }), _jsx(Button, { variant: "ghost", size: "icon", className: "size-7 shrink-0 text-muted-foreground hover:text-destructive", onClick: () => {
237
+ void onRemove(rule.id).then(() => toast.success(t('dynamic.automations.deleted', {
238
+ defaultValue: 'Automatización eliminada',
239
+ })));
240
+ }, "aria-label": t('dynamic.automations.delete', {
241
+ defaultValue: 'Eliminar automatización',
242
+ }), "data-testid": `automation-delete-${rule.id}`, children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, String(rule.id))))) }), _jsxs("div", { className: "grid grid-cols-1 gap-3 rounded-md border bg-muted/30 p-3 sm:grid-cols-3", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-xs", children: t('dynamic.automations.actionLabel', {
243
+ defaultValue: 'Acción',
244
+ }) }), _jsxs(Select, { value: actionType, onValueChange: (v) => setActionType(v), children: [_jsx(SelectTrigger, { "data-testid": "automation-action-select", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: ACTION_TYPES.map((at) => (_jsx(SelectItem, { value: at, children: t(`dynamic.automations.action.${at}`, {
245
+ defaultValue: at === 'add_tag'
246
+ ? 'Agregar tag'
247
+ : at === 'remove_tag'
248
+ ? 'Quitar tag'
249
+ : 'Setear campo',
250
+ }) }, at))) })] })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-xs", children: t('dynamic.automations.fieldLabel', {
251
+ defaultValue: 'Campo',
252
+ }) }), _jsxs(Select, { value: field, onValueChange: setField, disabled: fieldChoices.length === 0, children: [_jsx(SelectTrigger, { "data-testid": "automation-field-select", children: _jsx(SelectValue, { placeholder: t('dynamic.automations.noFields', {
253
+ defaultValue: 'Sin campos',
254
+ }) }) }), _jsx(SelectContent, { children: fieldChoices.map((c) => (_jsx(SelectItem, { value: c.key, children: t(c.label, { defaultValue: c.label }) }, c.key))) })] })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-xs", children: t('dynamic.automations.valueLabel', {
255
+ defaultValue: 'Valor',
256
+ }) }), _jsx(Input, { value: value, onChange: (e) => setValue(e.target.value), onKeyDown: (e) => {
257
+ if (e.key === 'Enter')
258
+ void submit();
259
+ }, placeholder: t('dynamic.automations.valuePlaceholder', {
260
+ defaultValue: 'Valor...',
261
+ }), "data-testid": "automation-value-input" })] })] }), _jsx(DialogFooter, { children: _jsxs(Button, { onClick: () => void submit(), disabled: !canSubmit, className: "gap-1", "data-testid": "automation-add", children: [_jsx(Plus, { className: "h-4 w-4" }), t('dynamic.automations.add', {
262
+ defaultValue: 'Agregar regla',
263
+ })] }) })] }) }));
264
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "23.5.0",
3
+ "version": "23.6.0",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -383,6 +383,29 @@ describe('DynamicKanban render', () => {
383
383
  // a secondary field label is present
384
384
  expect(screen.getAllByText('Assignee:').length).toBeGreaterThan(0)
385
385
  })
386
+
387
+ it('lays out fluid lanes: the board fills the width and lanes grow (flex-1) with a min/max width', async () => {
388
+ useMetadataCache.getState().setMetadata('issue', meta())
389
+ const { container } = render(
390
+ <ApiProvider client={fakeApi()}>
391
+ <DynamicKanban model="issue" />
392
+ </ApiProvider>,
393
+ )
394
+ await screen.findByText('Backlog')
395
+ // The board fills the container and scrolls horizontally only on overflow.
396
+ const board = container.querySelector('[data-testid="kanban-board"]')!
397
+ expect(board.className).toContain('w-full')
398
+ expect(board.className).toContain('overflow-x-auto')
399
+ // Each lane grows to fill the available width but never shrinks past a
400
+ // readable min-width nor stretches beyond a sane max-width.
401
+ const lanes = container.querySelectorAll('[data-stage]')
402
+ expect(lanes.length).toBe(4)
403
+ lanes.forEach((lane) => {
404
+ expect(lane.className).toContain('flex-1')
405
+ expect(lane.className).toContain('min-w-[280px]')
406
+ expect(lane.className).toContain('max-w-[420px]')
407
+ })
408
+ })
386
409
  })
387
410
 
388
411
  // The drag itself is wired by dnd-kit (pointer + layout), which happy-dom can't