• Skip to main content
  • Skip to primary sidebar

DayBack

The Calendar You've Been Waiting For

  • Calendar
    • For Salesforce
      • DayBack Calendar for Salesforce
      • Field Service
      • Calendar Sharing in Salesforce
    • FileMaker Calendar
    • For Google Calendar
    • For Google Sheets
    • For Microsoft 365
    • For Basecamp
    • Integrations
  • Extensions
  • Pricing
  • Blog
  • Support
    • Support Options
    • Documentation
    • Documentation for DayBack Classic (older)
    • Implementation Packages
  • Contact
    • Our Mission
    • Team & Community
    • Careers
    • Contact Us
    • +1 (855) 733-3263
  • Timelines
    • Overview
    • History of the polio vaccine
    • Women’s Suffrage
    • Science Fiction
    • Your Vote in Context: the 2020 US Elections
    • History of Claris FileMaker
  • Sign In

Jul 04 2021

Select Specific Resource Filters when Switching Between Calendars

DayBack can help you filter your events by grouping Resources into Resource Folders. By creating folders and using them as a filter, you can view the availability of specific lists of resources at a glance. Using filters gives you a lot of flexibility, but what if you manage multiple calendars and need a quicker way to switch your filters to a different set of folders? One popular way would be to create bookmarks for specific views of your organization. DayBack can also be set up to automatically select specific resources when you click on a particular calendar.

This method is great if your workflow requires you to manage calendars for multiple teams. By creating a Custom App Action, you can automatically select only the specific resource folders that are relevant to a particular calendar you are viewing.

Example Organization

Suppose your organization has a Sales Support Team calendar and a Management Team calendar. You’ve organized your resources into 3 folders for your Sales Support Team, and 2 folders for your Management Team. You also have an additional folder for team members who work on both teams. You’d like each of your calendars to automatically select the resources in the relevant folders when you switch between calendars. 

Example org setup

Since the Admin Staff folder is set up to be shared by both teams, clicking either the Sales Support Team calendar or the Management Team calendar should also load Admin Staff availability regardless of which calendar you have open.

See It In Action

This video shows how clicking on a calendar automatically selects the appropriate folder as an active filter.

Configuring SetSourceFilters.js Custom Action

By default, DayBack doesn’t change your filters when you select a calendar, but this is very easy to set up with a custom app action. To have DayBack filter events by resource folder when a user selects a particular calendar, you can install the SetSourceFilter.js custom app action. The action will run automatically after the user clicks on a calendar.

Configuring your Custom App Action:

Setting up the configuration for your organization is easy. You will need to change the following section of the JavaScript code. Here is the configuration we created for our example organization:

inputs.calendars = {
  'Sales Support Team' : [
  	'Service Techs',
  	'Jr Service Techs',
  	'Service Managers',
  	'Admin Staff'
  ],
  'Management Team' : [
  	'Sales Team',
    'Campaign Managers',
  	'Admin Staff'
  ]
};

It doesn’t matter which order you list your calendars and folders, so long as the folders are listed under the correct calendar. In the above example, note how the list of folders is unique to each calendar, but the Admin Staff folder appears twice, once for each calendar.

How to install the Custom App Action:

  1. Follow the Custom App Action installation instructions
  2. Select “After Source Selection” as your Action Type
  3. Set “Prevent default action” to No, and enable the action for App and for Shares

SetSourceFilter.js Source Code:

Paste the full code below in the JavaScript box and edit the configuration section to reflect your specific organization’s configuration:

// Set Source Filters V1.1

// Purpose:
// Selects specific resource folders based on a sort selection

// Action Type: After Source Selection
// Prevent Default Action: No

// More info on Before Event Save actions and objects here:
// https://docs.dayback.com/article/140-custom-app-actions

// Declare globals
var options = {}; var inputs = {};

try {

    //----------- Configuration -------------------

        // Seconds to wait to allow this action to run before reporting an error (set to 0 to deactivate)
        options.runTimeout = 8; 
        
        // Define which Resource Folders should be selected when a Calendar is selected
        // for example, when the 'Management Team' calendar is selected, the 
        // Sales Team, Campaign Managers, and Admin Staff Resource folders will be selected
        inputs.calendars = {
            'Sales Support Team' : [
                'Service Techs',
                'Jr Service Techs',
                'Service Managers',
                'Admin Staff'
            ],
            'Management Team' : [
                'Sales Team',
                'Campaign Managers',
                'Admin Staff'
            ]
        };
        
    //----------- End Configuration -------------------

}
catch(error) {
    reportError(error);
}



//----------- The action itself: you may not need to edit this. -------------------


// Action code goes inside this function
function run() {

    // validate configuration 
    if (!validateConfiguration() ) {
        return confirmCallback();
    }

    // determine which calendar we've selected
    var thisCalendar = params.data ? params.data.item : false;
    
    if (thisCalendar) {
        var name = thisCalendar.name;

        // if we've selected a mapped calendar then deselect the other mapped ones.
        if (inputs.calendars[thisCalendar.name] && thisCalendar.status.selected) {
            // determine which mapped calendars are selected
            var selected = selectedMapped(inputs.calendars);

            // deselect any selected ones that aren't the one we just clicked
            for (var i = 0; i < selected.length; i++) {
                if (selected[i].name !== name) {
                    dbk.toggleCalendar(selected[i]);
                }
            }

            // update resource selection bases on calendar selection
            setResourceSelection(thisCalendar,inputs.calendars);
            dbk.resetResources();
        }
    }

    return confirmCallback();

    //----------- Action-specific fuctions -------------------
    
    // configurtion validation helper function
    function validateConfiguration() {
        // make sure we have defined a list of calendars
        if (!inputs.calendars || typeof inputs.calendars !== 'object' || Object.keys(inputs.calendars).length === 0) {
            return reportError({ message: "Please configure a list of calendars"});
        }
    
        var schedules = seedcodeCalendar.get('schedules');
        var calendars = Object.keys(inputs.calendars);

        // Check if defined sources and resource folders are valid        
        calendars.forEach((calendar, i) => {
            var resources = seedcodeCalendar.get('resources');            

            if (schedules.filter(schedule => schedule.name === calendar).length < 1) {
                return reportError({ message: "Calendar '" + calendar + "' is not a valid calendar"});
            }

            for (var c = 0; c < inputs.calendars[calendar].length; c++) {
                if (resources.filter(rec => rec.name === inputs.calendars[calendar][c]).length < 1) {
                    return reportError({ message: "The resource folder '" +  inputs.calendars[calendar][c] + "' is not a valid resource folder"});
                }
            }
        });  

        // config is valid
        return true; 
    }

    // main function to set resource by calendar
    function setResourceSelection(thisCalendar,calendarList) {
        var resources = seedcodeCalendar.get('resources');
        var selectedFolders = calendarList[thisCalendar.name];
        var unselectedFolders = [];
    
        // iterate each calendar that is is being unselected to build unselected folders list
        for (var property in calendarList) {
            if (property != thisCalendar.name) {
                unselectedFolders = unselectedFolders.concat(calendarList[property]);
            }
        }
    
        // deselect all previous folders
        for (var r = 0; r < resources.length; r++) {
            for (var u = 0; u < unselectedFolders.length; u++) {
                if (resources[r].status.selected && resources[r].folderName === unselectedFolders[u]) {
                    resources[r].status.selected = false;
                }
            }
            for (var s = 0; s < selectedFolders.length; s++) {
                if (!resources[r].status.selected && resources[r].folderName === selectedFolders[s]) {
                    resources[r].status.selected = true;
                }
            }
        }
    }

    // helper function for returning an array of selected calendars that are mapped
    function selectedMapped(calendarList) {
        var calendars = seedcodeCalendar.get('schedules');
        var selected = [];
        
        for (var i = 0; i <calendars.length; i++) {
            for (var mapping in calendarList ) {
                if (mapping == calendars[i].name && calendars[i].status.selected) {
                    selected.push(calendars[i]);
                }
            }
        }

        return selected;
    }

} // end run()


//----------- Run function wrapper and helpers - you shouldn't need to edit below this line. -------------------

// Variables used for helper functions below
var timeout;

// Execute the run function as defined above
try {
    if (!options.restrictedToAccounts || 
        !options.restrictedToAccounts.length || 
        (options.restrictedToAccounts && options.restrictedToAccounts.indexOf(inputs.account) > -1)
    ) {
        if (action.preventDefault && options.runTimeout) {
            timeoutCheck();
        }
        run();
    }
    else if (action.preventDefault) {
        confirmCallback();
    }
}
catch(error) {
    reportError(error);
}

// Run confirm callback when preventDefault is true. Used for async actions
function confirmCallback() {
    cancelTimeoutCheck();
    if (action.callbacks.confirm) {
        action.callbacks.confirm();
    }
}

// Run cancel callback when preventDefault is true. Used for async actions
function cancelCallback() {
    cancelTimeoutCheck();
    if (action.callbacks.cancel) {
        action.callbacks.cancel();
    }
}

// Check if the action has run within the specified time limit when preventDefault is enabled
function timeoutCheck() {
    timeout = setTimeout(function() {
        var error = {
            name: 'Timeout',
            message: 'The action was unable to execute within the allotted time and has been stopped'
        };
        reportError(error, true);
    }, (options && options.runTimeout ? options.runTimeout * 1000 : 0) );
}

function cancelTimeoutCheck() {
    if (timeout) {
        clearTimeout(timeout);
    }
}

// Function to report any errors that occur when running this action
// Follows standard javascript error reporter format of an object with name and message properties
function reportError(error) {
    var errorTitle = 'Error Running Custom Action';
    var errorMessage = '<p>There was a problem running the action "<span style="white-space: nowrap">' + action.name + '</span>"</p><p>Error: ' + error.message + '.</p><p>This may result in unexpected behavior of the calendar.</p>';

    if (action.preventDefault && timeout) {
        confirmCallback();
    }
    else {
        cancelCallback();  
    }
    
    setTimeout(function() {
        utilities.showModal(errorTitle, errorMessage, null, null, 'OK', null, null, null, true, null, true);
    }, 1000);
}

Curious about other app actions?

Visit our library of Custom App Actions to view all the cool stuff you can do with DayBack!

Written by Michael Dabrowski · Categorized: Dev · Tagged: Custom Actions, For Developers

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Primary Sidebar

Search

Latest Posts

  • Memory is Making
  • Add Notes and Comments to Events
  • Closed Through the New Year
  • Background Gradients on Horizon View
  • Cut and Paste Rescheduling

Pinned Posts

  • Scarcity: the Science of Resource Scheduling
  • Calendars Tell Stories
  • Time Shame – Plan Your Day Like a Road Trip
  • We Can’t See Late
  • You’re Calendar’s Not a Poster

  • Facebook
  • Instagram
  • LinkedIn
  • Twitter
  • Vimeo

© SeedCode, Inc. 2013–2023
+1 (855) 733-3263
who shot this?

X

View Details
Keep me informed
Which calendar sources interest you?
Interested in a calendar source we haven't listed? Have a question for us? Please let us know.
Loading