@universe-platform/ui 0.1.0-beta.4 → 0.1.0-beta.40

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/index.d.ts CHANGED
@@ -1,9 +1,81 @@
1
1
  import { ClassValue } from 'clsx';
2
+ import { ColumnDef } from './columns';
3
+ export { BaseColumnDef, COLUMN_CAPABILITIES, COLUMN_DEFAULT_INPUT_CONFIG, COLUMN_TYPES, COLUMN_TYPE_LABELS, ColumnCapabilities, ColumnConfig, ColumnConfigMap, ColumnDef, ColumnDefaultInputConfig, ColumnInfo, ColumnType, CurrencyConfig, ExtendedColumnInfo, GoogleContactConfig, GoogleContactNamePart, PhoneConfig, RelationConfig, SubTableConfig, TEXT_REPRESENTABLE_TYPES, TextRepresentableColumnType, TypedColumnDef, isTextRepresentable } from './columns';
4
+ import { RecordData, CellValue } from './records';
5
+ export { CellValue, ColumnValueMap, DriveFileValue, GoogleContactValue, LinkValue, RecordData, SubTableEntryValue, SubTableResolvedEntryValue } from './records';
6
+ import { WhatsAppConfig } from './pages';
7
+ export { BaseComponent, ComponentType, EditorApp, EditorPage, PageComponent, PageInput, TableComponent, WhatsAppConfig } from './pages';
8
+ import { SchemaDef } from './schema';
9
+ export { SchemaDef, TableMeta } from './schema';
10
+ import { TemplateIdentity } from './whatsappValidation';
11
+ export * from './whatsappValidation';
12
+ export { StatusConfig, TemplateIdentity, TemplateValidationParams, TemplateValidationResult, extractUniqueVariableIndices, extractVariableIndices, findVariableAtPosition, formatFileSize, getNextVariableIndex, getStatusConfig, hasBracketSpaces, hasConsecutiveVariables, hasFloatingVariables, hasInsufficientContextBetweenVariables, isDuplicateTemplate, isDuplicateTemplateName, isValidTemplateName, isWordToken, reindexVariables, sanitizeTemplateNameInput, seamlessVariableEngine, validateSequentialVariables, validateTemplateBody, validateWhatsAppTemplate, validateWordToVariableRatio } from './whatsappValidation';
13
+ export * from './formula';
14
+ export * from './relationCopy';
15
+ export * from './relationButtons';
16
+ export * from './defaultFilters';
2
17
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import React, { ReactNode } from 'react';
18
+ import * as react from 'react';
19
+ import react__default, { ReactNode } from 'react';
4
20
 
5
21
  declare function cn(...inputs: ClassValue[]): string;
6
22
 
23
+ /**
24
+ * WhatsApp opener and phone formatting utilities.
25
+ */
26
+ /**
27
+ * Robust WhatsApp Opener (Desktop App with Web Fallback).
28
+ * Tries the direct protocol first (whatsapp://) and falls back to WhatsApp Web
29
+ * if the app is not installed (window didn't lose focus).
30
+ */
31
+ declare function openWhatsAppChat(phone: string, defaultCountryCode?: string): void;
32
+ /**
33
+ * Formats a phone number for local UI display (e.g. Israeli 05X-XXX-XXXX format).
34
+ */
35
+ declare function formatPhoneDisplay(phone: string): string;
36
+
37
+ /**
38
+ * Phone number sanitization, validation, and WhatsApp link generation utilities.
39
+ * Pure functions with zero React or DOM dependencies for universal (server & client) usage.
40
+ */
41
+ /**
42
+ * Sanitizes phone numbers by stripping whitespace, dashes, and non-numeric characters,
43
+ * preserving a leading '+' if present.
44
+ */
45
+ declare function sanitizePhoneNumber(phone: string): string;
46
+ /**
47
+ * Validates whether a phone number is valid (after sanitization).
48
+ * Supports Israeli formats (e.g. 0501234567, 031234567) and international formats (+972..., 9-15 digits).
49
+ */
50
+ declare function isValidPhoneNumber(phone: string): boolean;
51
+ /**
52
+ * Generates an official wa.me WhatsApp direct message link.
53
+ *
54
+ * @param phone Raw or sanitized phone number
55
+ * @param defaultCountryCode Defaults to Israeli code '972'
56
+ * @returns WhatsApp URL or null if phone is empty
57
+ */
58
+ declare function generateWhatsAppLink(phone: string, defaultCountryCode?: string): string | null;
59
+ /**
60
+ * Normalizes a phone number for WhatsApp sending (Meta international format without leading '+').
61
+ * - Returns empty string if input is falsy.
62
+ * - Trims input and removes leading '+' if it exists.
63
+ * - Strips all non-digit characters.
64
+ * - If starting with '972', returns it as is.
65
+ * - If starting with '0' (Israeli mobile/landline), strips '0' and prepends '972'.
66
+ * - If exactly 9 digits long (omitted leading zero), prepends '972'.
67
+ * - Returns the processed digits string.
68
+ */
69
+ declare function normalizePhoneForWhatsApp(phone: string): string;
70
+
71
+ interface PastelPalette {
72
+ bg: string;
73
+ text: string;
74
+ border: string;
75
+ }
76
+ declare const PASTEL_PALETTES: PastelPalette[];
77
+ declare function getDeterministicPastelBadge(key: string): PastelPalette;
78
+
7
79
  /**
8
80
  * Core definitions for Universe UI components.
9
81
  * These interfaces define the standard contract that all generic visual components
@@ -43,37 +115,32 @@ interface BaseComponentProps<TData = any, TConfig = Record<string, any>> extends
43
115
  "data-testid"?: string;
44
116
  }
45
117
 
46
- /**
47
- * Table data schemas and record definitions for Universe UI components.
48
- */
49
- interface ColumnDef {
50
- name: string;
51
- type?: string;
52
- defaultValue?: any;
53
- isRequired?: boolean;
54
- isUnique?: boolean;
55
- referencedTableId?: string;
56
- displayField?: string;
57
- currencySymbol?: string;
58
- }
59
- interface SchemaDef {
60
- columns?: ColumnDef[];
61
- [key: string]: any;
62
- }
63
- interface RecordData {
64
- id: string;
65
- data: Record<string, any>;
66
- }
67
- interface DriveFileValue {
68
- fileId: string;
69
- fileUrl: string;
70
- fileName: string;
118
+ type OrgRole = "OWNER" | "EDITOR" | "VIEWER";
119
+ interface RolePermissions {
120
+ permissions: ComponentPermissions;
121
+ readOnly: boolean;
71
122
  }
123
+ declare function getRolePermissions(role?: OrgRole | null): RolePermissions;
72
124
 
73
125
  interface TableConfig extends SchemaDef {
74
126
  }
75
127
  type TableData = RecordData[];
76
- type TableBlockProps = BaseComponentProps<TableData, TableConfig>;
128
+ type RowStatusType = "success" | "failed" | "skipped" | "partial";
129
+ interface TableBlockProps extends Partial<BaseComponentProps<TableData, TableConfig>> {
130
+ exportAction?: (payload: any) => Promise<any>;
131
+ role?: OrgRole | null;
132
+ permissions?: ComponentPermissions;
133
+ readOnly?: boolean;
134
+ selectedRowIds?: string[];
135
+ onSelectionChange?: (selectedIds: string[]) => void;
136
+ onRowToggle?: (rowId: string, checked: boolean) => void;
137
+ rowStatusMap?: Record<string, {
138
+ status: RowStatusType;
139
+ tooltip?: string;
140
+ } | RowStatusType>;
141
+ editModelUrl?: string;
142
+ hideRelationButtons?: boolean;
143
+ }
77
144
  interface CreateRecordPayload {
78
145
  records: RecordData[];
79
146
  }
@@ -84,15 +151,658 @@ interface DeleteRecordPayload {
84
151
  ids: string[];
85
152
  }
86
153
 
154
+ declare function TableBlockClient({ data: initialRecords, settings: schema, executeAction, exportAction, emitEvent, pageState, isLoading, isUpdating, error, isEmpty: isStateEmpty, componentId, orgId, className, 'data-testid': dataTestId, role, permissions, readOnly, selectedRowIds, onSelectionChange, onRowToggle, rowStatusMap, editModelUrl, hideRelationButtons, }: TableBlockProps): react_jsx_runtime.JSX.Element;
155
+
87
156
  declare function TableBlock(props: TableBlockProps): react_jsx_runtime.JSX.Element;
88
157
 
158
+ interface UseColumnResizeOptions {
159
+ storageKey: string;
160
+ defaultWidth?: number;
161
+ minWidth?: number;
162
+ maxWidth?: number;
163
+ getMinColWidth?: (colName: string) => number;
164
+ }
165
+ declare function useColumnResize({ storageKey, defaultWidth, minWidth, maxWidth, getMinColWidth, }: UseColumnResizeOptions): {
166
+ columnWidths: Record<string, number>;
167
+ setColumnWidths: react.Dispatch<react.SetStateAction<Record<string, number>>>;
168
+ resizingCol: {
169
+ name: string;
170
+ startWidth: number;
171
+ startX: number;
172
+ minWidth: number;
173
+ } | null;
174
+ startResize: (colName: string, currentWidth: number, clientX: number, customMinWidth?: number) => void;
175
+ getWidth: (colName: string, fallback?: number) => number;
176
+ minWidth: number;
177
+ };
178
+
179
+ interface UseColumnReorderOptions {
180
+ onReorder: (draggedName: string, dropTargetName: string) => void | Promise<void>;
181
+ disabled?: boolean;
182
+ }
183
+ declare function useColumnReorder({ onReorder, disabled, }: UseColumnReorderOptions): {
184
+ draggedColName: string | null;
185
+ dragOverColName: string | null;
186
+ isDragging: (colName: string) => boolean;
187
+ isDragOver: (colName: string) => boolean;
188
+ getHeaderDragProps: (colName: string) => {
189
+ draggable: boolean;
190
+ onDragStart?: undefined;
191
+ onDragOver?: undefined;
192
+ onDragLeave?: undefined;
193
+ onDrop?: undefined;
194
+ onDragEnd?: undefined;
195
+ } | {
196
+ draggable: boolean;
197
+ onDragStart: (e: React.DragEvent) => void;
198
+ onDragOver: (e: React.DragEvent) => void;
199
+ onDragLeave: () => void;
200
+ onDrop: (e: React.DragEvent) => void;
201
+ onDragEnd: () => void;
202
+ };
203
+ };
204
+
205
+ /**
206
+ * Measure the width of a given text string in canvas 2D context using the specified font.
207
+ */
208
+ declare function measureTextWidth(text: string, font?: string): number;
209
+ interface SplitTitleResult {
210
+ isSingleWord: boolean;
211
+ line1: string;
212
+ line2?: string;
213
+ }
214
+ /**
215
+ * Splits a header title:
216
+ * - 1 word: stays on 1 line.
217
+ * - 2 or more words: divided as equally as possible between 2 lines without cutting any word.
218
+ */
219
+ declare function splitHeaderTitle(name: string): SplitTitleResult;
220
+ /**
221
+ * Calculate the minimum column width needed to display the header title cleanly:
222
+ * - If 1 word: minimum width is that word + padding + icon.
223
+ * - If 2+ words: minimum width is the longest line + padding + icon.
224
+ * - For specialized field types like GOOGLE_CONTACT: ensures room for button.
225
+ * Words are NEVER cut or truncated with ellipsis.
226
+ */
227
+ declare function calculateMinColumnWidth(name: string, isSubTable?: boolean, colType?: string): number;
228
+ interface ColumnHeaderTitleProps {
229
+ name: string;
230
+ width?: number;
231
+ isSubTable?: boolean;
232
+ }
233
+ /**
234
+ * Header title component:
235
+ * - Default / when enough width: takes up entire line on 1 single row.
236
+ * - When column is shrunk below full-line width: divides evenly into 2 rows without cutting any word.
237
+ * - Single words always stay on 1 row.
238
+ */
239
+ declare function ColumnHeaderTitle({ name, width, isSubTable }: ColumnHeaderTitleProps): react_jsx_runtime.JSX.Element;
240
+
241
+ declare function formatConflictCellValue(rawVal: any, colDef?: ColumnDef | {
242
+ name: string;
243
+ type?: string;
244
+ config?: any;
245
+ }, relationOptionsMap?: Record<string, any[]>): react__default.ReactNode;
246
+
247
+ interface DatePickerCellProps {
248
+ value: CellValue<'DATE'> | Date | number;
249
+ onChange: (value: CellValue<'DATE'>) => void;
250
+ column?: ColumnDef;
251
+ readOnly?: boolean;
252
+ }
253
+ declare function DatePickerCell({ value, onChange, readOnly }: DatePickerCellProps): react_jsx_runtime.JSX.Element;
254
+
255
+ interface TimePickerCellProps {
256
+ value: CellValue<'TIME'> | Date;
257
+ onChange: (value: CellValue<'TIME'>) => void;
258
+ column?: ColumnDef;
259
+ readOnly?: boolean;
260
+ }
261
+ declare function parseTimeParts(val: any): {
262
+ hour: number;
263
+ minute: number;
264
+ } | null;
265
+ declare function formatTimeIso(hour: number, minute: number): string;
266
+ declare function formatTimeInput(input: string | null | undefined): string;
267
+ declare function TimePickerCell({ value, onChange, readOnly }: TimePickerCellProps): react_jsx_runtime.JSX.Element;
268
+
269
+ interface LinkPickerCellProps {
270
+ value: CellValue<'LINK'> | string;
271
+ onChange: (value: CellValue<'LINK'>) => void;
272
+ column?: ColumnDef;
273
+ readOnly?: boolean;
274
+ }
275
+ declare function parseLinkValue(val: CellValue<'LINK'> | string): {
276
+ url: string;
277
+ label: string;
278
+ };
279
+ declare function resolveProtocol(url: string): string;
280
+ declare function LinkPickerCell({ value, onChange, readOnly }: LinkPickerCellProps): react_jsx_runtime.JSX.Element;
281
+
282
+ interface RelationPickerCellProps {
283
+ value: CellValue<'RELATION'> | string;
284
+ onChange: (value: CellValue<'RELATION'>) => void;
285
+ column: ColumnDef<'RELATION'> | ColumnDef;
286
+ context?: any;
287
+ rowId?: string;
288
+ readOnly?: boolean;
289
+ }
290
+ /**
291
+ * Normalizes any relation value into a canonical string[] of record IDs.
292
+ * Handles legacy single-string records, empty strings, null/undefined, and object shapes.
293
+ */
294
+ declare function normalizeRelationValue(val: any): string[];
295
+ declare function RelationPickerCell({ value, onChange, column, context, rowId, readOnly, }: RelationPickerCellProps): react_jsx_runtime.JSX.Element;
296
+
297
+ interface RelationFilterOption {
298
+ id: string;
299
+ label: string;
300
+ }
301
+ type RelationFilterPayload = string[] | {
302
+ operator: 'notIn';
303
+ values: string[];
304
+ } | undefined;
305
+ interface RelationFilterPopoverProps {
306
+ isOpen: boolean;
307
+ onClose: () => void;
308
+ column: ColumnDef;
309
+ triggerRect: DOMRect | null;
310
+ options: RelationFilterOption[];
311
+ filterValue?: string[] | {
312
+ operator: 'notIn';
313
+ values: string[];
314
+ };
315
+ selectedValues?: string[];
316
+ onSelectionChange: (payload: RelationFilterPayload) => void;
317
+ isLoading?: boolean;
318
+ }
319
+ declare function RelationFilterPopover({ isOpen, onClose, column, triggerRect, options, filterValue, selectedValues, onSelectionChange, isLoading, }: RelationFilterPopoverProps): react_jsx_runtime.JSX.Element | null;
320
+
321
+ /**
322
+ * checkElementTruncation - Standard browser-accurate truncation detection using the Range API.
323
+ * Measures the exact unconstrained bounding box of rendered text without container padding interference.
324
+ */
325
+ declare const checkElementTruncation: (el: HTMLElement | null) => boolean;
326
+ /**
327
+ * useSmartTooltip - Reusable hook for detecting text truncation and rendering
328
+ * a custom portal tooltip bubble strictly when content is clipped (scrollWidth > el.clientWidth).
329
+ */
330
+ declare function useSmartTooltip<T extends HTMLElement = HTMLElement>(): {
331
+ targetRef: react__default.RefObject<T | null>;
332
+ checkTruncation: () => boolean;
333
+ handleMouseEnter: () => void;
334
+ handleMouseLeave: () => void;
335
+ hideBubble: () => void;
336
+ renderTooltip: (text?: string, dir?: "ltr" | "rtl" | "auto") => react__default.ReactPortal | null;
337
+ isTruncated: boolean;
338
+ showBubble: boolean;
339
+ };
340
+ interface TruncatedTooltipProps {
341
+ text: string;
342
+ tooltipText?: string;
343
+ dir?: 'ltr' | 'rtl' | 'auto';
344
+ className?: string;
345
+ wrapperClassName?: string;
346
+ children?: react__default.ReactNode;
347
+ }
348
+ /**
349
+ * TruncatedTooltip - A text container that applies ellipsis truncation
350
+ * and only displays a custom portal tooltip when text overflows horizontal bounds.
351
+ * Expands to full cell dimensions (w-full h-full min-h-[inherit]) for reliable hover detection.
352
+ * No native browser title attribute is rendered.
353
+ */
354
+ declare const TruncatedTooltip: react__default.FC<TruncatedTooltipProps>;
355
+ interface SmartTextCellProps {
356
+ value: any;
357
+ onChange?: (val: any) => void;
358
+ readOnly?: boolean;
359
+ type?: 'text' | 'number' | 'tel';
360
+ placeholder?: string;
361
+ className?: string;
362
+ prefix?: react__default.ReactNode;
363
+ suffix?: react__default.ReactNode;
364
+ dir?: 'ltr' | 'rtl' | 'auto';
365
+ tooltipText?: string;
366
+ step?: string;
367
+ onFocus?: (e: react__default.FocusEvent<HTMLInputElement>) => void;
368
+ onBlur?: (e: react__default.FocusEvent<HTMLInputElement>) => void;
369
+ }
370
+ /**
371
+ * SmartTextCell - Text cell with intelligent hover tooltip.
372
+ * Only displays the custom portal tooltip bubble if the text is actually
373
+ * clipped/truncated within its visible boundary (scrollWidth > clientWidth).
374
+ * Native title attributes are omitted to prevent duplicate tooltips.
375
+ */
376
+ declare const SmartTextCell: react__default.FC<SmartTextCellProps>;
377
+
378
+ interface PlacementConfig {
379
+ sourceTableId: string;
380
+ sourceDisplayFieldId: string;
381
+ targetTableId: string;
382
+ targetFilterField1Id: string;
383
+ targetFilterField2Id: string;
384
+ targetSelectorFieldId: string;
385
+ targetAssignmentFieldId: string;
386
+ targetFilterDefaults?: Record<string, string>;
387
+ title?: string;
388
+ statusColors?: PlacementStatusSetting[];
389
+ defaultReturnStatusId?: string;
390
+ }
391
+ interface PlacementStatusSetting {
392
+ id: string;
393
+ hex: string;
394
+ label: string;
395
+ }
396
+ declare const DEFAULT_PLACEMENT_STATUS_COLORS: PlacementStatusSetting[];
397
+ /** מחזיר את מזהה הסטטוס ההתחלתי התקף: זה שהוגדר, אחרת "3" אם קיים (תאימות לאחור), אחרת הראשון */
398
+ declare function resolveDefaultStatusId(list: PlacementStatusSetting[], wanted?: string): string;
399
+ interface PlacementSettingsProps {
400
+ config: PlacementConfig;
401
+ onChange: (newConfig: PlacementConfig) => void;
402
+ tables: Array<{
403
+ id: string;
404
+ name: string;
405
+ isGlobal?: boolean;
406
+ fields?: any[];
407
+ columns?: any[];
408
+ }>;
409
+ }
410
+ declare const PlacementStatusEditor: react__default.FC<{
411
+ config: PlacementConfig;
412
+ onChange: (newConfig: PlacementConfig) => void;
413
+ }>;
414
+ declare const PlacementSettings: react__default.FC<PlacementSettingsProps>;
415
+
416
+ interface PlacementStatusColor {
417
+ id: string;
418
+ hex: string;
419
+ label: string;
420
+ }
421
+ type ExecuteAction = (actionType: string, payload: Record<string, any>) => Promise<any>;
422
+ interface PlacementBlockProps {
423
+ settings?: any;
424
+ executeAction?: ExecuteAction;
425
+ componentId?: string;
426
+ className?: string;
427
+ config?: PlacementConfig;
428
+ fetchSourceData?: (tableId: string) => Promise<any[]>;
429
+ fetchTargetData?: (tableId: string) => Promise<any[]>;
430
+ updateAssignment?: (targetRecordId: string, sourceRecordId: string) => Promise<void>;
431
+ }
432
+ declare const PlacementBlock: react__default.FC<PlacementBlockProps>;
433
+
434
+ declare const RELATION_SEP = "::";
435
+ declare const RELATION_LABEL_KEY = "__label";
436
+ declare const NON_TEXT_TYPES: string[];
437
+ interface PlacementColumnOption {
438
+ /** הערך שנשמר ב-config (שם עמודה, או "קשר::עמודה") */
439
+ id: string;
440
+ /** מה שמוצג למשתמש */
441
+ name: string;
442
+ type?: string;
443
+ /** תיאור משני (סוג שדה / מאיזו טבלה) */
444
+ description?: string;
445
+ /** שם עמודת הקשר, אם העמודה באה מטבלה קשורה */
446
+ viaRelation?: string;
447
+ /** שם הטבלה הקשורה, אם העמודה באה מטבלה קשורה */
448
+ fromTableName?: string;
449
+ fromGlobalTable?: boolean;
450
+ }
451
+ type AnyTable = {
452
+ id: string;
453
+ name: string;
454
+ isGlobal?: boolean;
455
+ fields?: any[];
456
+ columns?: any[];
457
+ };
458
+ declare const tableColumns: (table?: {
459
+ fields?: any[];
460
+ columns?: any[];
461
+ } | null) => any[];
462
+ declare const isUserColumn: (c: any) => boolean;
463
+ declare const buildRelationRef: (relationColumn: string, field: string) => string;
464
+ declare function parseColumnRef(ref: string): {
465
+ relationColumn?: string;
466
+ field: string;
467
+ };
468
+ /** שם ידידותי להצגה: "עיר מגורים › שם העיר" */
469
+ declare const columnRefLabel: (ref: string) => string;
470
+ /**
471
+ * בונה את רשימת העמודות לבחירה בטבלה: עמודות הטבלה עצמה,
472
+ * ואחריהן (כשהן מותרות) עמודות מכל טבלה שמקושרת אליה ב-RELATION - כולל טבלאות מערכת.
473
+ * עמודות קשר מוצגות רק ברמה אחת (לא קשר בתוך קשר).
474
+ */
475
+ declare function buildColumnOptions(table: AnyTable | undefined | null, allTables: AnyTable[] | undefined, opts?: {
476
+ textOnly?: boolean;
477
+ includeRelated?: boolean;
478
+ }): PlacementColumnOption[];
479
+ /**
480
+ * מעשיר רשומות בעמודות וירטואליות מהטבלאות הקשורות, לפי relationOptions שחזר מהשרת:
481
+ * data["<קשר>::<עמודה>"] = ערך העמודה ברשומה/ות הקשורות
482
+ * data["<קשר>::__label"] = התווית (displayField) של הרשומה/ות הקשורות
483
+ * הערך המקורי של עמודת הקשר (ה-IDs) נשאר כמות שהוא, כי הוא משמש לשמירה.
484
+ */
485
+ declare function enrichRowsWithRelations<T extends {
486
+ data: Record<string, any>;
487
+ }>(rows: T[], columns: any[], relationOptions: Record<string, any[]> | undefined): T[];
488
+ /** קורא ערך עמודה (רגילה או מטבלה קשורה); עמודת קשר "חשופה" מוצגת לפי התווית שלה ולא לפי ה-ID */
489
+ declare function readColumnValue(data: Record<string, any> | undefined, ref: string): any;
490
+
491
+ interface MetaTemplateComponent {
492
+ type: "HEADER" | "BODY" | "FOOTER" | "BUTTONS" | string;
493
+ format?: "TEXT" | "IMAGE" | "DOCUMENT" | "VIDEO" | string;
494
+ text?: string;
495
+ example?: {
496
+ header_handle?: string[];
497
+ body_text?: string[][];
498
+ [key: string]: any;
499
+ };
500
+ [key: string]: any;
501
+ }
502
+ interface MetaWhatsAppTemplate {
503
+ id: string;
504
+ name: string;
505
+ status: "APPROVED" | "PENDING" | "IN_REVIEW" | "REJECTED" | "PAUSED" | string;
506
+ category: "MARKETING" | "UTILITY" | string;
507
+ language: string;
508
+ components?: MetaTemplateComponent[];
509
+ [key: string]: any;
510
+ }
511
+ interface TargetPhoneOption {
512
+ id: string;
513
+ label: string;
514
+ type: "DIRECT" | "SUBTABLE";
515
+ columnName: string;
516
+ parentColumnName?: string;
517
+ referencedTableId?: string;
518
+ }
519
+ interface PostSendTargetColumnOption {
520
+ id: string;
521
+ targetType: "MAIN" | "SUBTABLE";
522
+ columnName: string;
523
+ parentColumnName?: string;
524
+ referencedTableId?: string;
525
+ type: "BOOLEAN" | "RELATION";
526
+ label: string;
527
+ config?: any;
528
+ }
529
+ interface WhatsAppRecipient {
530
+ targetPhone: string;
531
+ mainRowId: string;
532
+ subtableRowId?: string;
533
+ recordData: Record<string, any>;
534
+ }
535
+ type WhatsAppData = any;
536
+ interface TemplateVariableMappingItem {
537
+ type: "column" | "static";
538
+ value: string;
539
+ }
540
+ type TemplateVariableMapping = Record<string, TemplateVariableMappingItem>;
541
+ interface TextableColumnOption {
542
+ id: string;
543
+ label: string;
544
+ columnName: string;
545
+ source: "MAIN" | "SUBTABLE";
546
+ parentColumnName?: string;
547
+ type?: string;
548
+ }
549
+ interface WhatsAppComponentProps extends Partial<BaseComponentProps<WhatsAppData, WhatsAppConfig>> {
550
+ permissions?: ComponentPermissions;
551
+ orgId?: string;
552
+ organizationId?: string;
553
+ }
554
+
555
+ interface SkippedRecipientDetail {
556
+ mainRowId: string;
557
+ subtableRowId?: string;
558
+ reason?: string;
559
+ }
560
+
561
+ /**
562
+ * WhatsAppComponent
563
+ * High-level orchestrator for the WhatsApp messaging interface.
564
+ * Coordinates template selection, dynamic filters, recipient settings/actions,
565
+ * and the embedded data grid.
566
+ */
567
+ declare function WhatsAppComponent(props: WhatsAppComponentProps): react_jsx_runtime.JSX.Element;
568
+
569
+ interface CreateTemplateModalProps {
570
+ isOpen: boolean;
571
+ onClose: () => void;
572
+ onSuccess: () => void;
573
+ existingTemplateNames?: string[];
574
+ existingTemplates?: TemplateIdentity[];
575
+ executeAction?: (actionType: string, payload: Record<string, any>) => Promise<any>;
576
+ orgId?: string;
577
+ }
578
+ /**
579
+ * CreateTemplateModal
580
+ * Enterprise modal dialog for authoring and submitting WhatsApp message templates
581
+ * with atomic variable deletion, flexible editing, and strict word-to-variable ratio validation.
582
+ */
583
+ declare function CreateTemplateModal({ isOpen, onClose, onSuccess, existingTemplateNames, existingTemplates, executeAction, orgId, }: CreateTemplateModalProps): react_jsx_runtime.JSX.Element | null;
584
+
585
+ interface BroadcastProgressState {
586
+ current: number;
587
+ total: number;
588
+ successful: number;
589
+ failed: number;
590
+ skipped: number;
591
+ percent: number;
592
+ }
593
+ interface BroadcastSummaryState {
594
+ successful: number;
595
+ failed: number;
596
+ skipped: number;
597
+ total: number;
598
+ successfulRowIds: string[];
599
+ failedRowIds: string[];
600
+ skippedRowIds: string[];
601
+ }
602
+ declare function useWhatsAppState(props: WhatsAppComponentProps): {
603
+ orgId: any;
604
+ tableColumns: ColumnDef[];
605
+ rawRecords: RecordData[];
606
+ relationOptions: Record<string, {
607
+ id: string;
608
+ label: string;
609
+ }[]>;
610
+ isLoadingTableData: boolean;
611
+ tableError: string | null;
612
+ loadTableData: () => Promise<void>;
613
+ embeddedTableExecuteAction: (actionType: string, payload: Record<string, any>) => Promise<any>;
614
+ filteredRecords: RecordData[];
615
+ filters: Record<string, string>;
616
+ filterableColumns: ColumnDef[];
617
+ handleFilterChange: (colName: string, val: string) => void;
618
+ handleClearFilters: () => void;
619
+ activeFiltersCount: number;
620
+ selectedRowIds: string[];
621
+ handleSelectionChange: (selectedIds: string[]) => void;
622
+ handleRowToggle: (rowId: string, checked?: boolean) => void;
623
+ toggleRowSelection: (rowId: string, checked?: boolean) => void;
624
+ autoSelectCount: number | null;
625
+ setAutoSelectCount: react.Dispatch<react.SetStateAction<number | null>>;
626
+ filteredData: RecordData[];
627
+ targetPhoneOptions: TargetPhoneOption[];
628
+ phoneColumns: {
629
+ name: string;
630
+ id: string;
631
+ type: "PHONE";
632
+ }[];
633
+ phoneColumnId: string;
634
+ setPhoneColumnId: react.Dispatch<react.SetStateAction<string>>;
635
+ selectedPhoneOption: TargetPhoneOption;
636
+ isScanningPhoneColumns: boolean;
637
+ hasPhoneColumn: boolean;
638
+ subtableFilterableColumns: ColumnDef[];
639
+ selectedSubtableFilterCol: ColumnDef | undefined;
640
+ subtableFilterColumn: string;
641
+ setSubtableFilterColumn: react.Dispatch<react.SetStateAction<string>>;
642
+ subtableFilterValue: string;
643
+ setSubtableFilterValue: react.Dispatch<react.SetStateAction<string>>;
644
+ subtableSendLimit: "ALL" | "PRIMARY" | "FIRST";
645
+ setSubtableSendLimit: react.Dispatch<react.SetStateAction<"ALL" | "PRIMARY" | "FIRST">>;
646
+ getRelationOptionsForCol: (colName: string, parentColName?: string) => {
647
+ id: string;
648
+ label: string;
649
+ }[];
650
+ isSubtableFilterIncomplete: boolean;
651
+ enablePostSendAction: boolean;
652
+ setEnablePostSendAction: react.Dispatch<react.SetStateAction<boolean>>;
653
+ postSendActionColumn: string;
654
+ setPostSendActionColumn: react.Dispatch<react.SetStateAction<string>>;
655
+ postSendActionValue: string;
656
+ setPostSendActionValue: react.Dispatch<react.SetStateAction<string>>;
657
+ postSendActionTargetType: "SUBTABLE" | "MAIN";
658
+ setPostSendActionTargetType: react.Dispatch<react.SetStateAction<"SUBTABLE" | "MAIN">>;
659
+ postSendActionTargetOptions: PostSendTargetColumnOption[];
660
+ selectedPostSendCol: PostSendTargetColumnOption | undefined;
661
+ isPostSendActionIncomplete: boolean;
662
+ preparedRecipients: WhatsAppRecipient[] & {
663
+ skippedRowIds: string[];
664
+ skippedDetails: SkippedRecipientDetail[];
665
+ };
666
+ finalRecipientCount: number;
667
+ validMainRowsCount: number;
668
+ isSendDisabled: boolean;
669
+ isTestDisabled: boolean;
670
+ sendButtonTitle: string;
671
+ testButtonTitle: string;
672
+ requiresMediaHeader: boolean;
673
+ isMediaMissing: boolean;
674
+ templates: MetaWhatsAppTemplate[];
675
+ selectedTemplateId: string | null;
676
+ setSelectedTemplateId: react.Dispatch<react.SetStateAction<string | null>>;
677
+ selectedTemplate: MetaWhatsAppTemplate | null;
678
+ templateBodyText: string;
679
+ templateHeaderComp: MetaTemplateComponent | null;
680
+ headerMediaUrl: string;
681
+ handleHeaderMediaUrlChange: (newUrl: string) => void;
682
+ handleResetHeaderMediaUrl: () => void;
683
+ isUploadingMedia: boolean;
684
+ handleFileUpload: (file: File, templateName?: string) => Promise<void>;
685
+ templateVariables: string[];
686
+ variableMapping: TemplateVariableMapping;
687
+ textableColumnOptions: TextableColumnOption[];
688
+ handleVariableTypeChange: (varKey: string, newType: "column" | "static") => void;
689
+ handleVariableValueChange: (varKey: string, newValue: string) => void;
690
+ isLoadingTemplates: boolean;
691
+ fetchError: string | null;
692
+ isModalOpen: boolean;
693
+ setIsModalOpen: react.Dispatch<react.SetStateAction<boolean>>;
694
+ isDropdownOpen: boolean;
695
+ setIsDropdownOpen: react.Dispatch<react.SetStateAction<boolean>>;
696
+ isDeleting: boolean;
697
+ handleDeleteTemplate: () => Promise<void>;
698
+ existingTemplateNames: string[];
699
+ loadTemplates: () => Promise<void>;
700
+ isTestPromptOpen: boolean;
701
+ setIsTestPromptOpen: react.Dispatch<react.SetStateAction<boolean>>;
702
+ testPhoneNumber: string;
703
+ handleTestPhoneChange: (val: string) => void;
704
+ testFeedback: string | null;
705
+ setTestFeedback: react.Dispatch<react.SetStateAction<string | null>>;
706
+ isSendingTest: boolean;
707
+ isSendingBroadcast: boolean;
708
+ broadcastProgress: BroadcastProgressState;
709
+ broadcastSummary: BroadcastSummaryState | null;
710
+ rowStatusMap: Record<string, {
711
+ status: "success" | "failed" | "skipped" | "partial";
712
+ tooltip: string;
713
+ }>;
714
+ clearBroadcastSummary: () => void;
715
+ handleSendTest: () => Promise<void>;
716
+ handleSendMessages: () => Promise<void>;
717
+ feedback: {
718
+ type: "success" | "error";
719
+ message: string;
720
+ } | null;
721
+ setFeedback: react.Dispatch<react.SetStateAction<{
722
+ type: "success" | "error";
723
+ message: string;
724
+ } | null>>;
725
+ };
726
+
727
+ interface DynamicFiltersBlockProps {
728
+ filterableColumns: ColumnDef[];
729
+ filters: Record<string, string>;
730
+ handleFilterChange: (colName: string, val: string) => void;
731
+ handleClearFilters: () => void;
732
+ activeFiltersCount: number;
733
+ relationOptions: Record<string, any[]>;
734
+ getRelationOptionsForCol?: (colName: string, parentColName?: string) => any[];
735
+ selectedRowIds: string[];
736
+ filteredRecordsCount: number;
737
+ rawRecordsCount: number;
738
+ loadTableData: () => void;
739
+ isLoadingTableData: boolean;
740
+ }
741
+ declare function DynamicFiltersBlock({ filterableColumns, filters, handleFilterChange, handleClearFilters, activeFiltersCount, relationOptions, getRelationOptionsForCol, selectedRowIds, filteredRecordsCount, rawRecordsCount, loadTableData, isLoadingTableData, }: DynamicFiltersBlockProps): react_jsx_runtime.JSX.Element;
742
+
743
+ interface SettingsAndActionsBlockProps {
744
+ isScanningPhoneColumns: boolean;
745
+ targetPhoneOptions: TargetPhoneOption[];
746
+ phoneColumnId: string;
747
+ setPhoneColumnId: (id: string) => void;
748
+ selectedPhoneOption: TargetPhoneOption | null;
749
+ autoSelectCount?: number | null;
750
+ setAutoSelectCount?: (count: number | null) => void;
751
+ subtableFilterColumn: string;
752
+ setSubtableFilterColumn: (col: string) => void;
753
+ subtableFilterValue: string;
754
+ setSubtableFilterValue: (val: string) => void;
755
+ subtableSendLimit: "PRIMARY" | "FIRST" | "ALL";
756
+ setSubtableSendLimit: (limit: "PRIMARY" | "FIRST" | "ALL") => void;
757
+ subtableFilterableColumns: ColumnDef[];
758
+ selectedSubtableFilterCol: ColumnDef | undefined;
759
+ getRelationOptionsForCol: (colName: string, parentColName?: string) => any[];
760
+ relationOptions: Record<string, any[]>;
761
+ enablePostSendAction: boolean;
762
+ setEnablePostSendAction: (enable: boolean) => void;
763
+ postSendActionColumn: string;
764
+ setPostSendActionColumn: (col: string) => void;
765
+ postSendActionValue: string;
766
+ setPostSendActionValue: (val: string) => void;
767
+ postSendActionTargetType: "MAIN" | "SUBTABLE";
768
+ setPostSendActionTargetType: (type: "MAIN" | "SUBTABLE") => void;
769
+ postSendActionTargetOptions: PostSendTargetColumnOption[];
770
+ selectedPostSendOption: PostSendTargetColumnOption | undefined;
771
+ isTestDisabled: boolean;
772
+ testButtonTitle: string;
773
+ setIsTestPromptOpen: (open: boolean) => void;
774
+ isSendDisabled: boolean;
775
+ sendButtonTitle: string;
776
+ handleSendMessages: () => void;
777
+ selectedRowIdsCount: number;
778
+ finalRecipientCount: number;
779
+ validMainRowsCount: number;
780
+ isSendingBroadcast?: boolean;
781
+ broadcastProgress?: BroadcastProgressState;
782
+ broadcastSummary?: BroadcastSummaryState | null;
783
+ clearBroadcastSummary?: () => void;
784
+ }
785
+ declare function SettingsAndActionsBlock({ isScanningPhoneColumns, targetPhoneOptions, phoneColumnId, setPhoneColumnId, selectedPhoneOption, autoSelectCount, setAutoSelectCount, subtableFilterColumn, setSubtableFilterColumn, subtableFilterValue, setSubtableFilterValue, subtableSendLimit, setSubtableSendLimit, subtableFilterableColumns, selectedSubtableFilterCol, getRelationOptionsForCol, relationOptions, enablePostSendAction, setEnablePostSendAction, postSendActionColumn, setPostSendActionColumn, postSendActionValue, setPostSendActionValue, setPostSendActionTargetType, postSendActionTargetOptions, selectedPostSendOption, isTestDisabled, testButtonTitle, setIsTestPromptOpen, isSendDisabled, sendButtonTitle, handleSendMessages, selectedRowIdsCount, finalRecipientCount, validMainRowsCount, isSendingBroadcast, broadcastProgress, broadcastSummary, clearBroadcastSummary, }: SettingsAndActionsBlockProps): react_jsx_runtime.JSX.Element;
786
+
787
+ interface FormBlockProps {
788
+ settings: any;
789
+ executeAction?: (actionType: string, payload: Record<string, any>) => Promise<any>;
790
+ componentId?: string;
791
+ className?: string;
792
+ onSuccess?: () => void;
793
+ }
794
+ declare function FormBlock({ settings: schema, executeAction, componentId, className, onSuccess }: FormBlockProps): react_jsx_runtime.JSX.Element;
795
+
89
796
  /**
90
797
  * UNIVERSE_COMPONENTS
91
- * Dynamic Component Registry dictionary mapping component type strings (e.g., "TABLE")
798
+ * Dynamic Component Registry dictionary mapping component type strings (e.g., "TABLE", "WHATSAPP")
92
799
  * to their respective React component implementation.
93
800
  */
94
801
  declare const UNIVERSE_COMPONENTS: {
95
802
  readonly TABLE: typeof TableBlock;
803
+ readonly WHATSAPP: typeof WhatsAppComponent;
804
+ readonly FORM: typeof FormBlock;
805
+ readonly PLACEMENT: react.FC<PlacementBlockProps>;
96
806
  };
97
807
  type ComponentRegistry = typeof UNIVERSE_COMPONENTS;
98
808
  type UniverseComponentType = keyof ComponentRegistry;
@@ -110,11 +820,11 @@ interface ToastContextType {
110
820
  showError: (error: string | Error | null | undefined, title?: string) => void;
111
821
  removeToast: (id: string) => void;
112
822
  }
113
- declare const ToastContext: React.Context<ToastContextType | undefined>;
823
+ declare const ToastContext: react__default.Context<ToastContextType | undefined>;
114
824
  declare function ToastProvider({ children }: {
115
825
  children: ReactNode;
116
826
  }): react_jsx_runtime.JSX.Element;
117
827
 
118
828
  declare function useToast(): ToastContextType;
119
829
 
120
- export { type BaseComponentProps, type ColumnDef, type ComponentCommunication, type ComponentConfig, type ComponentData, type ComponentPermissions, type ComponentRegistry, type ComponentState, type CreateRecordPayload, type DeleteRecordPayload, type DriveFileValue, type RecordData, type SchemaDef, TableBlock, type TableBlockProps, type TableConfig, type TableData, ToastContext, type ToastContextType, type ToastItem, ToastProvider, UNIVERSE_COMPONENTS, type UniverseComponentType, type UpdateRecordPayload, cn, useToast };
830
+ export { type BaseComponentProps, ColumnHeaderTitle, type ColumnHeaderTitleProps, type ComponentCommunication, type ComponentConfig, type ComponentData, type ComponentPermissions, type ComponentRegistry, type ComponentState, type CreateRecordPayload, CreateTemplateModal, type CreateTemplateModalProps, DEFAULT_PLACEMENT_STATUS_COLORS, DatePickerCell, type DeleteRecordPayload, DynamicFiltersBlock, type DynamicFiltersBlockProps, LinkPickerCell, type MetaTemplateComponent, type MetaWhatsAppTemplate, NON_TEXT_TYPES, type OrgRole, PASTEL_PALETTES, type PastelPalette, PlacementBlock, type PlacementBlockProps, type PlacementColumnOption, type PlacementConfig, PlacementSettings, type PlacementStatusColor, PlacementStatusEditor, type PlacementStatusSetting, RELATION_LABEL_KEY, RELATION_SEP, type RelationFilterOption, type RelationFilterPayload, RelationFilterPopover, type RelationFilterPopoverProps, RelationPickerCell, type RolePermissions, type RowStatusType, SettingsAndActionsBlock, type SettingsAndActionsBlockProps, SmartTextCell, type SplitTitleResult, TableBlock, TableBlockClient, type TableBlockProps, type TableConfig, type TableData, TimePickerCell, ToastContext, type ToastContextType, type ToastItem, ToastProvider, TruncatedTooltip, UNIVERSE_COMPONENTS, type UniverseComponentType, type UpdateRecordPayload, type UseColumnReorderOptions, type UseColumnResizeOptions, WhatsAppComponent, type WhatsAppComponentProps, type WhatsAppData, buildColumnOptions, buildRelationRef, calculateMinColumnWidth, checkElementTruncation, cn, columnRefLabel, enrichRowsWithRelations, formatConflictCellValue, formatPhoneDisplay, formatTimeInput, formatTimeIso, generateWhatsAppLink, getDeterministicPastelBadge, getRolePermissions, isUserColumn, isValidPhoneNumber, measureTextWidth, normalizePhoneForWhatsApp, normalizeRelationValue, openWhatsAppChat, parseColumnRef, parseLinkValue, parseTimeParts, readColumnValue, resolveDefaultStatusId, resolveProtocol, sanitizePhoneNumber, splitHeaderTitle, tableColumns, useColumnReorder, useColumnResize, useSmartTooltip, useToast, useWhatsAppState };