Our pure JavaScript Scheduler component


Post by hlabatf@colas.com »

Hello,

I'm wondering why resourceStore doesn't automatic save the changes.

And after the save, how i can set in the store, the move as commited?

 this._resourceStore = new ResourceStore({
            modelClass: TaskWork,
            //onBeforeCommit: ({ source, changes }: any) => {
            //    console.log(source);
            //    console.log(changes);
            //    return false;
            //},

        createUrl: 'php/createResource',
        readUrl: 'php/readResource',
        updateUrl: 'php/updateResource',
        deleteUrl: 'php/deleteResource',
        autoCommit: true,
        onCommit: () => {

            console.info("Debug");
            this._resourceStore?.project
        },
        onChange: async ( event: any) => {

            console.info("Tree change");
            console.info(event);

            //await this._resourceStore?.project.commitAsync();
           // return false;

        },
        //onBeforeRequest: ({ source, changes }: any) => {
        //    console.log(source);
        //    console.log(changes);
        //    return false;
        //}
    });

For the eventStore, it's working

this._eventStore = new EventStore({
            modelClass: TaskWorkEvent,
            createUrl: 'php/create',
            readUrl: 'php/read',
            updateUrl: 'php/update',
            deleteUrl: 'php/delete',
            onException: async ({ source, exception, action, exceptionType, response, json } : any) => {
                console.warn("Failed to save");
                this._crudManager?.revertChanges();
                this.scheduler?.project.stm.resetQueue();
                //this.scheduler.eventStore.revertChanges()
            },
            onChange: () => {
                this._eventStore?.commit();
            }
        });

Post by tasnim »


Post by hlabatf@colas.com »

Thanks Tasnim.

As you can see, it's what i've done. But eventStore whitout autocommit works, and resourceStore with autocomit doesn't work.


Post by tasnim »

Are you able to reproduce it with the demo I provided above this https://bryntum.com/products/scheduler/examples/php/

Could you please provide the steps to reproduce it?


Post by hlabatf@colas.com »

There is my config:

I'm trying to find the best way to play with the component.

        LocaleManager.locale = 'FrFr';

    const today = DateHelper.clearTime(new Date());

    this._eventStore = new EventStore({
        modelClass: TaskWorkEvent,
        createUrl: 'php/create',
        readUrl: 'php/read',
        updateUrl: 'php/update',
        deleteUrl: 'php/delete',
        onException: async ({ source, exception, action, exceptionType, response, json } : any) => {
            console.warn("Failed to save");
            this._crudManager?.revertChanges();
            this.scheduler?.project.stm.resetQueue();
            //this.scheduler.eventStore.revertChanges()
        },
        onChange: () => {
            this._eventStore?.commit();
        }
    });

    this._resourceStore = new ResourceStore({
        modelClass: TaskWork,
        //onBeforeCommit: ({ source, changes }: any) => {
        //    console.log(source);
        //    console.log(changes);
        //    return false;
        //},

        createUrl: 'php/createResource',
        readUrl: 'php/readResource',
        updateUrl: 'php/updateResource',
        deleteUrl: 'php/deleteResource',
        autoCommit: true,
        onCommit: () => {

            console.info("Debug");
            
        },
        onChange: async ( event: any) => {

            console.info("Tree change");
            console.info(event);

            //await this._resourceStore?.project.commitAsync();
           // return false;

        },

        // Make it read only to prevent changes while committing
        listeners: {
            beforeCommit: () => {
                this.scheduler?.readOnly = true;
            },
            commit: () => {
                this.scheduler?.readOnly = false;
            },
            exception: (event : object) => {
                //processException(event);
                this.scheduler?.readOnly = false;
            }
        }
        //onBeforeRequest: ({ source, changes }: any) => {
        //    console.log(source);
        //    console.log(changes);
        //    return false;
        //}
    });

    this._crudManager = new CrudManager({
        autoLoad: true,
        eventStore: this._eventStore,
        resourceStore: this._resourceStore ,
        //autoSync: false,
        loadUrl: 'templates/data.json',
        //syncUrl: 'template/save',
        validateResponse: true,
        onBeforeSync: ({ source, pack }: any) => {
            console.log(source);
            console.log(pack);
            console.log(source.id);
            
        },
        onRequestFail: ({ source, requestType, response }: any) => {

            source.revertChanges();
            if (this.scheduler) {
                this.scheduler.project.stm.resetQueue();
             //   this.scheduler.eventStore.revertChanges()
             //  this.scheduler.eventStore.project.stm.resetQueue();
              //  console.log(this.scheduler.project.stm.position);
            }

            let serverMessage = response && response.message;
            let exceptionText = `Action "${requestType}" failed. ${serverMessage ? ` Server response: ${serverMessage}` : ''}`;

            BootstrapDangerToast.show(exceptionText);

            console.error(exceptionText);
        }
    });

    this.scheduler = new Scheduler({
        appendTo: document.getElementById('scheduler') ?? document.body,

        enableUndoRedoKeys: true,
        startDate: new Date(2023, 4, 1),
        endDate: new Date(2023, 7, 10),
        viewPreset: 'weekAndMonth',
        stepUnit: 'minute',
        barMargin: 1,
        rowHeight: 50,
        height: '80vh',
        flex: 4,
        // This allows the timeline to be scrolled infinitely in time.
        infiniteScroll: true,
        multiEventSelect: true,
        visibleDate: {
            date: new Date(),
            block: 'center',
            animate: true
        },
        onAfterEventSave: () => {
            console.info("After save");

        },
        tbar: {
            items: {
                scrollTo: {
                    label: 'Scroll to date',
                    inputWidth: '10em',
                    width: 'auto',
                    type: 'datefield',
                    value: today,
                    step: '1d',
                    listeners: {
                        change: ({ userAction, value }: any) => {
                            if (userAction) {
                                this.scheduler?.scrollToDate(DateHelper.set(value, 'hour', 12), { block: 'center', animate: 500 });
                            }
                        }
                    },
                    highlightExternalChange: false
                },
                viewPresetCombo: {
                    type: 'viewpresetcombo',
                    width: '7em'
                },
                checkBoxSnap: {
                    type: 'checkbox',
                    ref: 'snap',
                    label: 'Use snapping',
                    checked: true,
                    onChange: ({ checked }: any) => {
                        if (this.scheduler) {
                            this.scheduler.snap = checked;
                        }
                    }
                },
                sliderResolution: {
                    type: 'slider',
                    ref: 'resolution',
                    width: 130,
                    text: 'Time resolution',
                    showValue: true,
                    min: 5,
                    max: 60,
                    step: 5,
                    value: 30,
                    onChange: ({ value }: any) => {
                        if (this.scheduler) {
                            this.scheduler.timeResolution = value;
                        }
                    }
                },
                sliderZoom: {
                    type: 'slider',
                    ref: 'zoom',
                    width: 130,
                    text: 'Zoom',
                    showValue: true,
                    min: 0,
                    max: 25,
                    value: 15,
                    onInput: ({ value }: any) => {
                        if (this.scheduler) {
                            this.scheduler.zoomLevel = value;
                        }
                    }
                },
                undoRedo: {
                    type: 'undoredo',
                    items: {
                        transactionsCombo: {
                            width: 250,
                            displayValueRenderer(value: any, combo: any) {
                                const stmPos = combo.up('panel', true).project.stm.position || 0;

                                return stmPos + ' undo actions / ' + ((<any>this).store.count - stmPos) + ' redo actions';
                            }
                        }
                    }
                },
                rowHeight: {
                    type: 'slider',
                    ref: 'rowHeight',
                    text: 'Row height',
                    showValue: true,
                    min: 20,
                    onInput: ({ value }: any) => {
                        if (this.scheduler && resourceMargin && barMargin) {
                            this.scheduler.rowHeight = value;
                            // Limit margins to somewhat sane values
                            resourceMargin.maxHeight = barMargin.maxHeight = Math.max(0, (value - 10) / 2);
                        }
                    }
                },
                barMargin: {
                    type: 'slider',
                    ref: 'barMargin',
                    text: 'Bar margin',
                    showValue: true,
                    onInput: ({ value }: any) => {
                        if (this.scheduler) {
                            this.scheduler.barMargin = value;
                        }
                    }
                },
                resourceMargin: {
                    type: 'slider',
                    ref: 'resourceMargin',
                    text: 'Resource margin',
                    showValue: true,
                    max: 20,
                    onInput: ({ value }: any) => {
                        if (this.scheduler) {
                            this.scheduler.resourceMargin = value;
                        }
                    }
                }
            }
        },
        // Render some extra elements for the assignment equipment items
        eventBodyTemplate: (data: any) => {

            let v = data.myObject;

            let logo = 'fa-helmet-safety';

            if (!data.foreman) {
                logo = '';
            }

            let html = `<div class="p-0"><i class="fa-regular ${logo} pe-2"></i><strong>${StringHelper.encodeHtml(data.name)}</strong></div>`;

            if (this.scheduler && this.scheduler.rowHeight >= 30) {
                html += `<div class="p-0">${StringHelper.encodeHtml(data.message)}</div>`;
            }

            return `
            <div class="d-flex flex-column ">
              ${html}
            </div>
                `;
        },
        eventRenderer: ({ eventRecord }: any) => {
            //console.log(eventRecord);
            return {
                name: eventRecord.name || '',
                message: eventRecord.message,
                foreman: eventRecord.data.foreman
                // equipment: eventRecord.equipment.map((itemId) => this.equipmentStore.getById(itemId) || {})
            };
        },
        // Use the CrudManager's stores.
        //eventStore: crudManager.eventStore,
        //resourceStore: crudManager.resourceStore,
        crudManager: this._crudManager,
        features: {
            tree: true,
            cellEdit: false,
            filter: true,
            rowReorder: true,
            mergeCells: true,
            eventMenu: {
                items: {
                    // Add extra items shown for each event
                    move: {
                        text: 'Move event',
                        icon: 'b-fa b-fa-fw b-fa-arrows-alt-h',
                        cls: 'b-separator',
                        weight: 510,
                        // Add submenu
                        menu: {
                            // Submenu items
                            moveLeft: {
                                text: 'Move left',
                                icon: 'b-fa b-fa-fw b-fa-arrow-left',
                                cls: 'b-separator',
                                weight: 511,
                                onItem: ({ eventRecord }: any) => {
                                    eventRecord.startDate = DateHelper.add(eventRecord.startDate, -1, 'hour');
                                }
                            },
                            moveRight: {
                                text: 'Move right',
                                icon: 'b-fa b-fa-fw b-fa-arrow-right',
                                weight: 512,
                                onItem: ({ eventRecord }: any) => {
                                    eventRecord.startDate = DateHelper.add(eventRecord.startDate, 1, 'hour');
                                }
                            }
                        }
                    },
                    split: {
                        text: 'Split',
                        icon: 'b-fa b-fa-fw b-fa-cut',
                        weight: 520,
                        onItem: ({ eventRecord }: any) => {
                            eventRecord.split();
                        }
                    },
                    lock: {
                        text: 'Lock',
                        icon: 'b-fa b-fa-fw b-fa-lock',
                        cls: 'b-separator',
                        weight: 530,
                        onItem: ({ eventRecord }: any) => {
                            eventRecord.locked = true;
                            eventRecord.draggable = false;
                            eventRecord.resizable = false;
                        }
                    },

                    // Edit a built in item
                    editEvent: {
                        text: 'Update'
                    },

                    // Hide a built in item
                    deleteEvent: false
                },

                // Process items before context menu is shown, add or remove or prevent it
                processItems: ({ eventRecord, items }: any) => {
                    if (eventRecord.eventType === 'meeting') {
                        // Add a custom item for meetings
                        items.cancel = {
                            text: 'Cancel',
                            icon: 'b-fa b-fa-fw b-fa-ban',
                            cls: 'b-separator',
                            weight: 540,
                            onItem: ({ eventRecord }: any) => {
                                eventRecord.canceled = true;
                            }
                        };
                    }

                    if (eventRecord.eventType === 'activity') {
                        // Remove "Edit" items for activities
                        items.editEvent = false;

                        // Add a "Done" item
                        items.done = {
                            text: 'Done',
                            icon: 'b-fa b-fa-fw b-fa-check',
                            cls: 'b-separator',
                            weight: 550,
                            onItem: ({ eventRecord }: any) => {
                                eventRecord.done = true;
                            }
                        };
                    }

                    // Not possible to lock canceled or completed events, disable the item
                    if (eventRecord.canceled || eventRecord.done) {
                        items.lock.disabled = true;
                    }

                    // Prevent menu for "locked" event
                    return !eventRecord.locked;
                }
            }
        },
        columns: [
            {
                type: 'tree',
                field: 'name',
                text: 'Name',
                flex: 1
            },
            {
                text: 'Available',
                field: 'available',
                mergeCells: true,
                // Apply some CSS to hide the header text, looks ugly in the narrow header
                cls: 'hide-header-text',

                // Custom renderer for the merged cells, allows you to affected the generated merged cell (the contents are
                // determined using the normal renderer/record value)
                //renderer: ({ domConfig, value } : any) =>  {
                //    // Add a background based on team to the merged range
                //    Object.assign(domConfig.className, {
                //        'bg-info': value === true,
                //        'bg-primary': value === false,
                //    });
                //}
            },
            {
                text: 'Nbr tasks',
                editor: false,
                width: 100,
                renderer: ({ record }: any) => record.events.length || '',
                align: 'center',
                sortable: (a: any, b: any) => a.events.length < b.events.length ? -1 : 1
            },
            {
                text: 'Total Days',
                editor: false,
                width: 100,
                renderer: ({ record }: any) =>
                    record.events.reduce((accumulator: number, object: any) => {
                        return accumulator + object.duration;
                    }, 0) || 0
                ,
                align: 'center',
                sortable: (a: any, b: any) => a.events.length < b.events.length ? -1 : 1
            }
        ],

        subGridConfigs: {
            left: {
                width: 350
            },
        },
        // Scheduler always has a Project instance internally, we need to configure its internal StateTrackingManager
        // so that the UndoRedo widget gets customized titles in its transaction dropdown.
        project: {
            stm: {
                autoRecord: true,

                getTransactionTitle(transaction: any) {
                    const lastAction = transaction.queue[transaction.queue.length - 1];

                    let { type, model } = lastAction;

                    if (lastAction.modelList && lastAction.modelList.length) {
                        model = lastAction.modelList[0];
                    }

                    let title = 'Transaction ' + (<any>this).position;

                    if (type === 'UpdateAction' && model instanceof EventModel) {
                        title = 'Edit flight ' + model.name;
                    }
                    else if (type === 'UpdateAction' && model instanceof ResourceModel) {
                        title = 'Edit gate ' + model.name;
                    }
                    else if (type === 'RemoveAction' && model instanceof EventModel) {
                        title = 'Remove flight ' + model.name;
                    }
                    else if (type === 'RemoveAction' && model instanceof ResourceModel) {
                        title = 'Remove gate ' + model.name;
                    }
                    else if (type === 'AddAction' && model instanceof EventModel) {
                        title = 'Add flight ' + model.name;
                    }
                    else if (type === 'AddAction' && model instanceof DependencyModel) {
                        title = StringHelper.xss`Link ${(<any>model.fromEvent).name} -> ${(<any>model.toEvent).name}`;
                    }

                    return title;
                }
            }
        },
        // Customize feature's keyMap in Scheduler's keyMap
        //keyMap: {
        //    'Ctrl+Shift+C': 'eventCopyPaste.copy',
        //    'Ctrl+Shift+X': 'eventCopyPaste.cut',
        //    'Ctrl+C': null,
        //    'Ctrl+X': null
        //},

        listeners: {
            // Listener called before the built in editor is shown
            beforeEventEdit: ({ eventRecord, resourceRecord }: any) => {

                this._editingEvent = { eventRecord: eventRecord, resourceRecord: resourceRecord };

                const teams = eventRecord.resources;
                // Show custom editor
                //$('#customEditor').modal('show');

                //// Fill its fields
                //if (teams.length === 0) {
                //    // New match being created
                //    $('#home').val(resourceRecord.id);
                //}
                //else {
                //    $('#home').val(teams[0].id);
                //    $('#away').val(teams[1]?.id || '');
                //}
                //$('#startDate').val(DateHelper.format(eventRecord.startDate, 'YYYY-MM-DD'));
                //$('#startTime').val(DateHelper.format(eventRecord.startDate, 'HH:mm'));

                this.name(eventRecord.name);
                this.startDate(eventRecord.startDate);

                this.editor();

                // Prevent built in editor
                return false;
            },

            paint: ({ firstPaint }: any) => {
                if (firstPaint) {
                    console.log('First paint');
                }
            },

            beforeDestroy: () => {
                alert("U are going away?");
                console.log("Before destroy");
            }
        },
    });

    const { rowHeight, barMargin, resourceMargin } = this.scheduler.widgetMap;

And i'm using dataset from Tree example.

{
  "success": true,
  "resources": {
    "rows": [
      {

    "id": 100,
    "name": "Team Alpha",
    "expanded": true,
    "children": [
      {
        "id": 1,
        "name": "Arcady"
      },
      {
        "id": 2,
        "name": "Dave",
        "eventColor": "green",
        "available": false
      },
      {
        "id": 3,
        "name": "Henrik",
        "eventColor": "green",
        "available": true
      },
      {
        "id": 4,
        "name": "Linda",
        "eventColor": "red",
        "available": false,
        "statusMessage": "I'm ok!"
      }
    ]
  },
  {
    "id": 200,
    "name": "Team Beta",
    "expanded": true,
    "children": [
      {
        "id": 5,
        "name": "Maxim",
        "eventColor": "red"
      },
      {
        "id": 6,
        "name": "Mike",
        "eventColor": "red"
      },
      {
        "id": 7,
        "name": "Lee"
      },
      {
        "id": 8,
        "name": "Amit"
      }
    ]
  },
  {
    "id": 300,
    "name": "Team Black Ops",
    "expanded": true,
    "children": [
      {
        "id": 9,
        "name": "Kate",
        "eventColor": "blue"
      },
      {
        "id": 10,
        "name": "Jong",
        "eventColor": "blue"
      },
      {
        "id": 11,
        "name": "Lola"
      },
      {
        "id": 12,
        "name": "Lisa"
      }
    ]
  }
]
  },
  "events": {
    "rows": [
      {
        "id": 1,
        "name": "Hack server (fixed)",
        "iconCls": "b-fa b-fa-server",
        "startDate": "2023-05-08T00:00:00",
        "duration": 5,
        "durationUnit": "d",
        "draggable": false,
        "resizable": true,
        "message": "Use flipper",
        "foreman": {
          "id": 100,
          "login": "New one"
        },
        "test":  "toto"
      },
      {
        "id": 2,
        "name": "Cyber attack",
        "iconCls": "b-fa b-fa-laptop",
        "startDate": "2023-05-09T00:00:00",
        "duration": 12,
        "durationUnit": "d",
        "test": "tata",
        "foreman": {
          "id": 100,
          "login": "New one"
        }
      },
      {
        "id": 3,
        "name": "Port scan",
        "iconCls": "b-fa b-fa-user",
        "startDate": "2023-05-09T00:00:00",
        "duration": 8,
        "durationUnit": "d",
        "test": "tata",
        "foreman": {
          "id": 100,
          "login": "New one"
        }
      },
      {
        "id": 4,
        "name": "Planning",
        "iconCls": "b-fa b-fa-users",
        "startDate": "2023-05-09T00:00:00",
        "duration": 7,
        "durationUnit": "d"
      },
      {
        "id": 5,
        "name": "Enforce firewall",
        "startDate": "2023-05-09T00:00:00",
        "iconCls": "b-fa b-fa-server",
        "duration": 3,
        "durationUnit": "d"
      },
      {
        "id": 6,
        "name": "Backup server",
        "iconCls": "b-fa b-fa-server",
        "startDate": "2023-05-09T00:00:00",
        "cls": "Special",
        "duration": 20,
        "durationUnit": "d"
      },
      {
        "id": 7,
        "name": "Presentation",
        "iconCls": "b-fa b-fa-video",
        "startDate": "2023-05-11T00:00:00",
        "cls": "Special",
        "duration": 15,
        "durationUnit": "d"
      },
      {
        "id": 21,
        "name": "Project Black",
        "duration": 16,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-cog"
      },
      {
        "id": 22,
        "name": "Project X",
        "duration": 12,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-cog"
      },
      {
        "id": 23,
        "name": "X-Mission",
        "duration": 23,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-user-secret"
      },
      {
        "id": 24,
        "name": "Installation",
        "duration": 12,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-circle-check"
      },
      {
        "id": 25,
        "name": "Meeting X",
        "duration": 10,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-question"
      },
      {
        "id": 26,
        "name": "Meeting",
        "duration": 2,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-user-check"
      },
      {
        "id": 27,
        "name": "Fly onsite",
        "duration": 17,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-plane"
      },
      {
        "id": 29,
        "name": "Book flight",
        "duration": 3,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-plane"
      },
      {
        "id": 30,
        "name": "Covert ops",
        "duration": 2,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-phone"
      },
      {
        "id": 31,
        "name": "Covert ops",
        "duration": 1,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-bug"
      },
      {
        "id": 32,
        "name": "Secret mission",
        "duration": 1,
        "durationUnit": "d",
        "iconCls": "b-fa b-fa-fw b-fa-cog"
      }
    ]
  },
  "assignments": {
    "rows": [
      {
        "id": 1,
        "resourceId": 1,
        "eventId": 1
      },
      {
        "id": 2,
        "resourceId": 2,
        "eventId": 1
      },
      {
        "id": 3,
        "resourceId": 3,
        "eventId": 3
      },
      {
        "id": 4,
        "resourceId": 4,
        "eventId": 4
      },
      {
        "id": 5,
        "resourceId": 5,
        "eventId": 5
      },
      {
        "id": 6,
        "resourceId": 6,
        "eventId": 6
      },
      {
        "id": 7,
        "resourceId": 7,
        "eventId": 7
      },
      {
        "id": 8,
        "resourceId": 8,
        "eventId": 8
      }
    ]
  }
}

Post by hlabatf@colas.com »

I've found the issue. I've removed autoLoad from crudManager and create 3 stores (resource, event, assignment).

Doing something like the code below:

        this._assignmentStore = new AssignmentStore({
            autoLoad: true,
            autoCommit: true,
            createUrl: 'php/createAssignment',
            readUrl: 'templates/dataAssignements.json',
            updateUrl: 'php/updateAssignment',
            deleteUrl: 'php/deleteAssignment',
            onException: async ({ source, exception, action, exceptionType, response, json }: any) => {
                console.warn("Failed to save");
                this._crudManager?.revertChanges();
                this.scheduler?.project.stm.resetQueue();
                //this.scheduler.eventStore.revertChanges()
            },
            onChange: () => {
                this._eventStore?.commit();
            }
        });

    this._eventStore = new EventStore({
        autoLoad: true,
        autoCommit: true,
        modelClass: TaskWorkEvent,
        createUrl: 'php/createEvent',
        readUrl: 'templates/dataEvents.json',
        updateUrl: 'php/updateEvent',
        deleteUrl: 'php/deleteEvent',
        onException: async ({ source, exception, action, exceptionType, response, json } : any) => {
            console.warn("Failed to save");
            this._crudManager?.revertChanges();
            this.scheduler?.project.stm.resetQueue();
            //this.scheduler.eventStore.revertChanges()
        },
        onChange: () => {
            this._eventStore?.commit();
        }
    });

    this._resourceStore = new ResourceStore({
        modelClass: TaskWork,
        //onBeforeCommit: ({ source, changes }: any) => {
        //    console.log(source);
        //    console.log(changes);
        //    return false;
        //},

        createUrl: 'php/createResource',
        readUrl: 'templates/dataResources.json',
        updateUrl: 'php/updateResource',
        deleteUrl: 'php/deleteResource',
        // Load and save automatically
        autoLoad: true,
        autoCommit: true,
        // Send as form data and not a JSON payload
        sendAsFormData: true,
        //onCommit: () => {

        //    console.info("Debug");
            
        //},
        //onChange: async ( event: any) => {

        //    console.info("Tree change");
        //    console.info(event);

        //    //await this._resourceStore?.project.commitAsync();
        //   // return false;

        //},

        // Make it read only to prevent changes while committing
        listeners: {
            beforeCommit: () => {
                if(this.scheduler) this.scheduler.readOnly = true;
            },
            commit: () => {
                if (this.scheduler) this.scheduler.readOnly = false;
            },
            exception: (event : object) => {
                //processException(event);
                if (this.scheduler) this.scheduler.readOnly = false;
            }
        }
        //onBeforeRequest: ({ source, changes }: any) => {
        //    console.log(source);
        //    console.log(changes);
        //    return false;
        //}
    });

I wonder if the order of loading (stores) is important?
How to customize payload on readUrl, UpdateUrl, deleteUrl and createUrl?
How to suspend like

this.scheduler.crudManager.suspendAutoSync();

with stores and Url? i have tried

this._resourceStore.autoCommit = false;

but not working.

I have seen, when i want to create a new event, plugin create assignmentFirst and CreateEvent with customId. It is possible to create in one transaction only and customize our id?

Example:
createAssginement -> Payload :

{"data":[{"id":"_generatedModelClass_882ae21a-def5-4c4b-8042-ce873edff58e","resourceId":100,"eventId":"_generatedModelClass_c21ee288-59ff-402b-bbb6-28e24720f282"}]}

After, create event -> payload:

{"data":[{"id":"_generatedModelClass_c21ee288-59ff-402b-bbb6-28e24720f282","startDate":"2023-05-11T00:00:00+02:00","endDate":"2023-05-30T00:00:00+02:00","duration":19,"durationUnit":"day","cls":"","name":"Nouvel événement","exceptionDates":[],"allDay":false,"message":""}]}

Last question, there is a possibility to manage eventStore and assignmentStore using syncUrl and for the resourceStore autoCommit and onChange Event. How i can validate (Brytum component side) all resource changes?


Post by alex.l »

First of all in your text I see both: URLs for stores and crudManager. You can't use both, you need to choose if you want to use crudManager and allow it to manage data load/commit, or make it manually on store level. https://bryntum.com/products/scheduler/docs/guide/Scheduler/data/crud_manager

I wonder if the order of loading (stores) is important?

No, you can load data in order you want.

How to suspend like suspendAutoSync

Yes, you need to disable autoCommit https://bryntum.com/products/scheduler/docs/api/Core/data/AjaxStore#property-autoCommit

but not working.

Please provide a test case. Did you try it in our PHP demo? Do you see this problem in there?

I have seen, when i want to create a new event, plugin create assignmentFirst and CreateEvent with customId. It is possible to create in one transaction only and customize our id?

Do you mean $phantomId? The $phantomId is required to manage persisted and not persisted records. $phantomId is created automatically and have to be replaced to real id by your server side, which will means record is persist. See https://bryntum.com/products/scheduler/docs/guide/Scheduler/data/crud_manager

All the best,
Alex


Post by alex.l »

How to customize payload on readUrl, UpdateUrl, deleteUrl and createUrl?

What exactly you want to customize?

All the best,
Alex


Post Reply