import type { CollectionOptimisticActions, CollectionState, FiltersMap, } from '@wix/bex-core'; import type { EditableTableState } from './EditableTableState'; export interface EditableTableMutationHandlers { /** Factory to create a new empty item with a fresh ID */ createItem: () => T; /** Submit updated items to the server */ submitUpdate: (items: T[]) => Promise; /** Submit newly created items to the server */ submitCreate: (items: T[]) => Promise; /** Submit item deletions to the server */ submitDelete: (items: T[]) => Promise; } export class MutationState { private readonly _handlers: EditableTableMutationHandlers; private readonly _optimisticActions: CollectionOptimisticActions; private readonly _parent: EditableTableState; constructor( parent: EditableTableState, handlers: EditableTableMutationHandlers, createOptimisticActions: ( collection: CollectionState, ) => CollectionOptimisticActions, ) { this._parent = parent; this._handlers = handlers; this._optimisticActions = createOptimisticActions(parent.collection); } init() { return this._optimisticActions.init(); } addItem(afterItemKey?: string): string { const newItem = this._handlers.createItem(); this._optimisticActions.createOne(newItem, { submit: this._handlers.submitCreate, afterItemKey, successToast: 'Item created', errorToast: (_err: unknown, { retry }: { retry: () => void }) => ({ message: 'Failed to create item', action: { onClick: retry, text: 'Retry' }, }), }); return this._parent.collection.itemKey(newItem); } duplicateItem(rowKey: string) { const keyedItem = this._parent.keyedItems.find((ki) => ki.key === rowKey); if (!keyedItem) { return; } const sourceItem = keyedItem.item; const newItem = this._handlers.createItem(); const { itemKey } = this._parent.collection; const newId = itemKey(newItem); const sourceId = itemKey(sourceItem); const duplicate = { ...sourceItem } as any; for (const k of Object.keys(duplicate)) { if (String(duplicate[k]) === sourceId) { duplicate[k] = newId; break; } } this._optimisticActions.createOne(duplicate as T, { submit: this._handlers.submitCreate, afterItemKey: keyedItem.key, successToast: 'Item duplicated', errorToast: (_err: unknown, { retry }: { retry: () => void }) => ({ message: 'Failed to duplicate item', action: { onClick: retry, text: 'Retry' }, }), }); } deleteItems(rowKeys: string[]) { const items = rowKeys .map((key) => this._parent.keyedItems.find((ki) => ki.key === key)?.item) .filter((item): item is T => item != null); if (items.length === 0) { return; } this._optimisticActions.deleteMany(items, { submit: this._handlers.submitDelete, successToast: items.length === 1 ? 'Item deleted' : `${items.length} items deleted`, errorToast: (_err: unknown, { retry }: { retry: () => void }) => ({ message: 'Failed to delete items', action: { onClick: retry, text: 'Retry' }, }), }); } updateItems(updatedItems: T[]) { if (updatedItems.length === 0) { return; } this._optimisticActions.updateMany(updatedItems, { keepPosition: true, submit: this._handlers.submitUpdate, successToast: updatedItems.length === 1 ? 'Item updated' : `${updatedItems.length} items updated`, errorToast: (_err: unknown, { retry }: { retry: () => void }) => ({ message: 'Failed to update items', action: { onClick: retry, text: 'Retry' }, }), }); } }