Premium support for our pure JavaScript UI components


Post by thamismedina »

Hello, im working on a feature where i need to change the project calendar to one where sunday is not a working day, and i mannage to do that but there is one specific resource that i wish i could ignore when i change the calendar, like if i change the calendar now i change for all resources, is possible to ignora one specific resource when doing that?
this is what i have now and i use the checkbox to change the calendars

<template>
  <div id="scheduler-container"></div>
</template>

<script setup>
import { onMounted } from "vue";
import {
  SchedulerPro,
  PresetManager,
  StringHelper,
  DateHelper,
  ProjectModel,
  Fullscreen,
} from "@bryntum/schedulerpro";

const zoomLevels = {
  minuteAndHour: 1,
  hourAndDay: 1,
};

const eventColors = {
  Ventilation: "blue",
  Heating: "orange",
  Roof: "lime",
  Attic: "lime",
  Basement: "lime",
  Cistern: "purple",
  Gas: "red",
};

onMounted(() => {
  const project = new ProjectModel({
    autoLoad: false,

stm: {
  autoRecord: true,
},
fullscreen: true,
calendar: "general",

transport: {
  load: {
    url: "./data/data.json",
  },
},

// This config enables response validation and dumping of found errors to the browser console.
// It's meant to be used as a development stage helper only so please set it to false for production systems.
validateResponse: true,
  });

  const schedulerConfig = new SchedulerPro({
    appendTo: "scheduler-container",
    presets: PresetManager.records.filter((preset) => zoomLevels[preset.id]),
    project,
    features: {
      stripe: true,
      dependencies: false,
      resourceTimeRanges: true,
      rowReorder: {
        showGrip: true,
      },
    },

startDate: "2020-03-23",
endDate: "2020-03-28",
rowHeight: 90,
barMargin: 15,
eventStyle: "border",
resourceImagePath: "./users/",

columns: [
  {
    type: "resourceInfo",
    text: "Manager",
  },
],

eventRenderer({ eventRecord, renderData }) {
  renderData.eventColor = eventColors[eventRecord.type] || "gray";
  const { iconCls } = renderData;
  renderData.iconCls = null;

  return [
    {
      className: "event-header",
      children: [
        {
          className: "event-sticky-content",
          children: [
            {
              tag: "i",
              className: iconCls,
            },
            {
              html: StringHelper.encodeHtml(eventRecord.name),
            },
          ],
        },
      ],
    },
    {
      className: "event-body",
      html: `<div class="event-sticky-content">${schedulerConfig.formatDuration(
        eventRecord.duration,
        1
      )} work days<i class="b-fa b-fa-arrow-right"></i>${Math.round(
        DateHelper.diff(eventRecord.startDate, eventRecord.endDate, "d")
      )} days</div>`,
    },
  ];
},

tbar: [
  {
    type: "undoredo",
    icon: "b-fa-undo",
    items: {
      transactionsCombo: null,
    },
  },
  {
    type: "checkbox",
    text: "Bloquear domingo",
    onChange: ({ checked }) => {
      const generalCalendar = project.calendarManagerStore.first;
      const sundayCalendar = project.calendarManagerStore.last;
      let calendarType = checked ? generalCalendar : sundayCalendar;
      project.setCalendar(calendarType);
    },
  },
  "->",
  {
    ref: "fullscreenButton",
    type: "button",
    tooltip: "Tela Cheia",
    icon: "b-fa-maximize",
    alignSelf: "end",
    ignoreLearn: true,

    onClick() {
      if (Fullscreen.enabled) {
        const element = document.getElementById("scheduler-container");
        if (element === null) {
          throw new Error("You renamed ID of the scheduler component");
        }
        if (!Fullscreen.isFullscreen) {
          Fullscreen.request(element);
        } else {
          Fullscreen.exit();
        }
      }
    },
  },
],
  });

  project.load().then(async () => {
    await project.commitAsync();

project.calendar = schedulerConfig.calendarManagerStore.value =
  project.calendarManagerStore.last;
  });
});
</script>

<style scoped>
#scheduler-container {
  height: 100vh; /* Adjust height as needed */
  width: 100%;
}
</style>

Post by marcio »

Hey thamismedina,

Thanks for reaching out and welcome to our forums! :)

You can assign a calendar directly to a resource, in your case, to the resource that is the exception. Please check our documentation that explains the calendars logic https://bryntum.com/products/schedulerpro/docs/guide/SchedulerPro/basics/calendars#scheduling-logic-when-using-project-event-and-resource-calenders.
https://bryntum.com/products/schedulerpro/docs/api/SchedulerPro/model/ResourceModel#field-calendar

We also have a demo where you can check how to use the resource calendar approach https://bryntum.com/products/schedulerpro/examples/resource-non-working-time/.

If you need further clarification please let us know.

Best regards,
Márcio

How to ask for help? Please read our Support Policy


Post by thamismedina »

Hi Marcio, thnks for the quick response, i mannaged to do the blocking ignoring one resource, using the calendar for each resource and when changing the calendar with setcalendar, i just ignore the one resource i need to ignore and thats working fine thank u very much for that doc, that helped a lot.
Now i got a problem and i dont know if it is a bug or a miss config but since is related to the block sunday i decided to ask in this forum.
I have a undoredo btn in this project, and it works fine to track event changing but when it comest to calendar changing, the btn is having a weird behavior, for example, my block sunday is a checkbox that starts as checked, so checked: true, when i uncheck , the sunday is now available for all my resources and the class i put to show that the sunday is block is gone, thats the normal behavior nothing wrong until i press the undo btn, now the sunday is again blocked, but the class i put to show that, does not apply, and the checkbox doesnt check, so if now i check again the sunday continues to be blocked but the class to show doesnt apply, only if i uncheck and then check again the class come back, here is my updated code if u want to see, or test for yourself :
note: my calendar is configured in my data, but its something like this:

  "calendars" : {
    "rows" : [
      {
        "id"        : "sunday",
        "name"      : "sunday",
        "unspecifiedTimeIsWorking" : false,
        "intervals" : [
          {
            "recurrentStartDate" : "on Mon",
            "recurrentEndDate"   : "on Mon",
            "isWorking"          : true
          }
        ]
      },      {
        "id"        : "general",
        "name"      : "general",
        "unspecifiedTimeIsWorking" : false,
        "intervals" : [
          {
            "recurrentStartDate" : "on Sun",
            "recurrentEndDate"   : "on Mon",
            "isWorking"          : false,
            "cls": "weekend"
          },
          {
            "recurrentStartDate" : "on Mon",
            "recurrentEndDate"   : "on Sunday",
            "isWorking"          : true
          }
        ]
      }
    ]
  },
<template>
  <div id="scheduler-container"></div>
</template>

<script setup>
import { onMounted } from "vue";
import {
  SchedulerPro,
  DateHelper,
  Tooltip,
  Toast,
  Fullscreen,
} from "@bryntum/schedulerpro";

onMounted(() => {
  const scheduler = new SchedulerPro({
    project: {
      autoLoad: true,
      loadUrl: "./data/data2.json",
      stm: {
        autoRecord: true,
      },
    },

appendTo: "scheduler-container",
startDate: "2020-03-21T07:00",
endDate: "2020-03-29T23:00",
rowHeight: 70,
eventStyle: "hollow",
resourceImagePath: "../_shared/images/users/",

viewPreset: {
  base: "weekAndDay",
  headers: [
    {
      unit: "day",
      dateFormat: "DD/MM/YY",
    },
    {
      unit: "day",
      dateFormat: "ddd",
    },
  ],
},

features: {
  dependencies: false,
  nonWorkingTime: false,
  resourceNonWorkingTime: {
    maxTimeAxisUnit: "day",
  },
  timeRanges: {
    showCurrentTimeLine: true,
  },
  rowReorder: {
    showGrip: true,
  },
},

columns: [
  {
    type: "resourceInfo",
    text: "Worker",
    showEventCount: false,
    showRole: true,
  },
],

tbar: [
  {
    type: "undoredo",
    icon: "b-fa-undo",
    disabled: true,
    items: {
      transactionsCombo: null,
    },
  },
  {
    type: "checkbox",
    text: "Bloquear domingo",
    checked: true,
    onChange: ({ checked }) => {
      console.log("alo", scheduler.resourceStore);
      const resourceInterest = scheduler.resourceStore.map((resource) => {
        if (resource.name !== "George") {
          if (checked) {
            resource.setCalendar("general");
          } else {
            resource.setCalendar("sunday");
          }
        }
      });
      console.log("resource", resourceInterest);
    },
  },
  "->",
  {
    ref: "fullscreenButton",
    type: "button",
    tooltip: "Tela Cheia",
    icon: "b-fa-maximize",
    alignSelf: "end",
    ignoreLearn: true,

    onClick() {
      if (Fullscreen.enabled) {
        const element = document.getElementById("scheduler-container");
        if (element === null) {
          throw new Error("You renamed ID of the scheduler component");
        }
        if (!Fullscreen.isFullscreen) {
          Fullscreen.request(element);
        } else {
          Fullscreen.exit();
        }
      }
    },
  },
],

listeners: {
  paint() {
    const scheduler = this;

    scheduler.rangeTooltip = new Tooltip({
      rootElement: document.body,
      forSelector: ".b-sch-resourcenonworkingtime",
      getHtml: ({ event }) => {
        const rangeRecord =
            scheduler.features.resourceNonWorkingTime.resolveResourceNonWorkingTimeInterval(
              event.target
            ),
          days = DateHelper.as(
            "days",
            rangeRecord.duration,
            rangeRecord.durationUnit
          ),
          text = rangeRecord.name || "Nonworking time";

        return `${text} (${days} ${DateHelper.getLocalizedNameOfUnit(
          "days",
          days !== 1
        )})`;
      },
    });
  },

  resourceNonWorkingTimeClick({ resourceNonWorkingTimeRecord }) {
    Toast.show(
      `You clicked ${
        resourceNonWorkingTimeRecord.name || "a nonworking time block"
      }`
    );
  },

  destroy() {
    this.rangeTooltip.destroy();
  },
},
  });
});
</script>

<style scoped>
#scheduler-container {
  height: 100vh; /* Adjust height as needed */
  width: 100%;
}
</style>

Post by alex.l »

Hi,

StateTrackingManager reverts changes on data level, it doesn't handle custom buttons, how should it know about that? If you added custom UI element, you then need to listen to change of data that you required and update UI.

What custom style are you talking about? Sorry if I missed it in the code, I checked it visually, since it is not runnable app.

All the best,
Alex Lazarev

How to ask for help? Please read our Support Policy

We do not write the code in bounds of forum support. If you need help with development, contact us via bryntum.com/services


Post by thamismedina »

Hello,

I see, so i should listem to data changes when undoredo btn is pressed or the ctrlz and ctrl shift z are pressed so i can see if the calendar got change and update the checkbox, but how can i do it? like how can i listem to this events and change another widget state?

And about the custom style, im using the 'weekend' style to show sunday is blocked, the project starts with the sunday blocked, o checkbox is checked and the class is being apply to the sunday columm, then if i uncheck, the sunday is no longer blocked and the class is gone, like it should be, but if i hit undo btn, the calendar goes back, wich means sunday is now blocked, the checkbox is not checked, and the class does not come back.

Actually, is it possible to make stm ignore the calendar changes so the undoredo is no longer interacting with the changes to the calendar?


Post by marcio »

Hey,

We have https://bryntum.com/products/schedulerpro/docs/api/Core/data/stm/StateTrackingManager#event-restoringStop, which sounds like the event you need to listen to, as it's triggered after the state is restored.

stm: {
	listeners: {
		restoringStop: ev => {
			//You need to add a name to the checkbox in the toolbar, I put a suggestion name just for demonstration purposes
			scheduler.tbar.widgetMap.checkDomingo.check() //or uncheck()
		}
	}
}

To ignore some changes, you can listen to https://bryntum.com/products/schedulerpro/docs/api/Scheduler/model/ProjectModel#event-change, check if the change is related to the calendar, then disable with https://bryntum.com/products/schedulerpro/docs/api/Core/data/stm/StateTrackingManager#function-disable, then after is processed (probably https://bryntum.com/products/schedulerpro/docs/api/Scheduler/model/ProjectModel#event-dataReady event), you can enable it again https://bryntum.com/products/schedulerpro/docs/api/Core/data/stm/StateTrackingManager#function-enable

Best regards,
Márcio

How to ask for help? Please read our Support Policy


Post by thamismedina »

Hello Marcio,

I kind got the stm to ignore the calendar changes in this way :

      {
        type: "checkbox",
        text: "Bloquear domingo",
        checked: true,
        onChange: ({ checked }) => {
          let stm = scheduler.project.getStm();
          stm.disable();
          if (stm.disabled) {
            scheduler.resourceStore.map((resource) => {
              if (resource.name !== "George") {
                if (checked) {
                  resource.setCalendar("general");
                } else {
                  resource.setCalendar("sunday");
                }
              }
            });
          }
          scheduler.widgetMap.tbar.items[1].disabled = true;
          setTimeout(() => {
            stm.enable();
            scheduler.widgetMap.tbar.items[1].disabled = false;
          }, "100");
        },
      },

I dont really know if it is the best way, but it was the only way i could get.
About the setimeout, if i didnt put one the stm was being enable so fast that the change in the calendar was still being tracked, so i decided to put a little delay to that and to avoid multiple clicking i disabled the check for that time, do u think this is a valid aproach?


Post by marcio »

Hey,

That looks like a valid approach.

But to replace the setTimeout, perhaps you could check the dataReady event that I mentioned earlier, which is triggered after the calculations are finished after changing the calendar, and then enable again the checkbox and the STM.

project : {
        listeners: {
            dataReady: ev => {
            	const stm = scheduler.project.getStm();
            	stm.enable();
            }
        }
    }

Best regards,
Márcio

How to ask for help? Please read our Support Policy


Post by thamismedina »

Works like a charm, thank u very much Marcio


Post by thamismedina »

Hello,

I was thinking, its possible to config recurring events to not show sunday as avaliable day based on the resource calendar? for example 1 specific resource does not work on sunday so this day is now block in his calendar, but i can still get recurring events to drop on sunday... is it possible to configure that?


Post Reply