Display a grid at a certain time

I need to display a particular grid from 9 January at 10:00 am. I’ve created a field that contains the day and time, but it doesn’t update itself. I think that by using javascripti I’ll be able to do it. Could you give me a hand with this? Thanks

Hi @NancyM – Are you proposing hiding the grid using page rules? If so, I believe the page would need to be reloaded for the rules to re-run and check the date in order to display the grid. KTL offers the ability to refresh a grid on a timer, but I haven’t seen for a scene.

I may be misunderstanding your requirement :thinking:

I’ve created 2 views, which I’m going to display depending on whether it’s 9 January after 8:00 or before. My difficulty is to have the date and time when the user connects to compare it to 9 January at 8:00.

Oh, yes, I see the challenge you’re facing. As you said, probably a code based solution could be an option. :man_shrugging:

I also believe

1 Like

Hi Nancy

We can share some code that will do this tomorrow. If you have any other requirements that haven’t already been mentioned that would be helpful.

We can create a function that takes a date and time that you can reuse, this will show or hide the view based on the current time.

Craig

Thanks.

Hi Nancy

What format is your date time in?

Craig

Hi Nancy

Have you got time for a quick chat? I have the basic functionality ready but I need a few details. Send me a DM if you want a meeting.

Craig

1 Like

dd/mm/yyyy

Hi Nancy

I have added the code below. This proved to be a bit trickier than I thought as I wanted to make sure the views never show if the date hasn’t been reached. The views now won’t flicker on the screen.

You will need to have a details view on the screen that has the date/time field you want to compare today with.

There are 4 consts at the top of the code that you will need to replace with your view IDs and field ID in the relevant consts.

Ignore the viewsToToggleOff Unless you want to hide views after a date I’ve added this for completeness.

If you have any issues please let me know and we can have a quick meeting to get it working.

Craig

const viewIdOfDateField = 'view_xyz';  // Details view where the date field is located
    const dateFieldId = 'field_123';  // ID of the date field in the view
    const viewIdsToToggleOn = ['view_123', 'view_xyz'];  // Views to show when the date has passed
    const viewIdsToToggleOff = []; // Views to hide when the date has passed

    $(document).on(`knack-view-init.${viewIdOfDateField}`, function ({namespace: viewId}) {
        hideViewsOnDateTime(viewId, dateFieldId, viewIdsToToggleOn, viewIdsToToggleOff)
    });

    function hideViewsOnDateTime(viewIdOfDetails, fieldIdOfDateTime, viewIdsToToggleOn = [], viewIdsToToggleOff = []) {
        setInitialViewState(viewIdsToToggleOn, viewIdsToToggleOff);
        const dateFieldSelector = `.${fieldIdOfDateTime} .kn-detail-body`;
        waitSelector(`#${viewIdOfDetails} ${dateFieldSelector}`).then(() => {
            const currentDateTime = new Date();
            const timeToCompare = new Date();
            const viewElement = $(`#${viewIdOfDetails}`);
            const dateField = viewElement.find(dateFieldSelector);

            // Date Time format: DD/MM/YYYY HH:mm
            const [date, time] = dateField.text().split(' ');
            const [day, month, year] = date.split('/').map(Number);
            const [hours, minutes] = time.split(':').map(Number);


            // Set the next refresh time to HH:mm
            timeToCompare.setHours(hours, minutes, 0, 0);
            timeToCompare.setDate(day);
            timeToCompare.setMonth(month - 1);

            const currentDate = new Date().setHours(0, 0, 0, 0);
            const compareDate = new Date(timeToCompare).setHours(0, 0, 0, 0);
            if (currentDate < compareDate) {
                console.log('Date Not Passed');
                return;
            } else if (currentDateTime < timeToCompare && currentDate === compareDate) {
                // Do something else
                const timeToCompareDifference = timeToCompare - currentDateTime;
                // Set a timeout to refresh the view at HH:mm AM
                setTimeout(function() {
                    toggleViews(viewIdsToToggleOn, viewIdsToToggleOff);
                }, timeToCompareDifference);
            } else if (currentDateTime > timeToCompare) {
                toggleViews(viewIdsToToggleOn, viewIdsToToggleOff);
            }
        })
        .catch(() => {
            console.error(`Failed to find date field ${fieldIdOfDateTime} in view ${viewIdOfDetails}`);
        });
    }

    /** waitSelector Copied from Normand @ KTL */
    //Param: sel is a string, not the jquery object.
    function waitSelector (sel = '', timeout = 5000) {
        return new Promise(function (resolve, reject) {
            if (!sel || $(sel).length) {
                resolve();
                return;
            }

            const intervalId = setInterval(function () {
                if ($(sel).length) {
                    clearTimeout(failsafe);
                    clearInterval(intervalId);
                    resolve();
                    return;
                }
            }, 100);

            const failsafe = setTimeout(function () {
                clearInterval(intervalId);
                reject(sel);
            }, timeout);
        });
    }

    async function toggleViews(viewIdsToShow, viewIdsToHide) {
        const togglePromises = [];

        if (viewIdsToShow) {
            viewIdsToShow.forEach(viewId => {
                togglePromises.push(
                    waitSelector(`#${viewId}`, 10000)
                        .then(() => $(`#${viewId}`).show())
                        .catch(() => {console.error(`Failed to show view ${viewId}`);})
                );
            });
        }

        if (viewIdsToHide) {
            viewIdsToHide.forEach(viewId => {
                togglePromises.push(
                    waitSelector(`#${viewId}`, 10000)
                        .then(() => $(`#${viewId}`).hide())
                        .catch(() => {console.error(`Failed to hide view ${viewId}`);})
                );
            });
        }

        return Promise.all(togglePromises);
    }

    async function setInitialViewState(viewIdsToShow, viewIdsToHide) {
        try {
            const togglePromises = [
                ...viewIdsToShow.map(viewId =>
                    waitSelector(`#${viewId}`, 10000)
                        .then(() => $(`#${viewId}`).hide())
                        .catch(err => console.error(`Failed to hide view ${viewId}:`, err))
                ),
                ...viewIdsToHide.map(viewId =>
                    waitSelector(`#${viewId}`, 10000)
                        .then(() => $(`#${viewId}`).show())
                        .catch(err => console.error(`Failed to show view ${viewId}:`, err))
                )
            ];

            await Promise.all(togglePromises);
        } catch (error) {
            console.error('Error in setInitialViewState:', error);
        }
    }

Blimey :exploding_head: - Great job @CSWinnall

1 Like

Thank you so much for your help, it’s really appreciated. I will try this today. It really is a lot more complex than I had anticipated.

1 Like

Hi,

I got these 2 errors on the console. The view with the date is 203 and the field la date is field_208

image

Maybe is because the date is in a Update Form?

the view to show is 137 and view to hide is 192.

Thanks

At the moment, the display is view 192, when the date and time have passed.

Hi Nancy Yes it’s because it’s a form I’ll update the code

Hi Nancy

Can you try this, This will run when the page loads and when the form is submitted. I think I have put your view IDs and field ID in the correct place but please check.

const viewIdWithDateField = 'view_203';  // Form View where the date field is located
const dateFieldId = 'field_208';  // ID of the date field in the view
const viewIdsToToggleOn = ['view_137'];  // Views to show when the date has passed
const viewIdsToToggleOff = ['view_192']; // Views to hide when the date has passed

// View Init
$(document).on(`knack-view-init.${viewIdWithDateField}`, function ({namespace: viewId}) {
    hideViewsOnDateTime(viewId, dateFieldId, viewIdsToToggleOn, viewIdsToToggleOff)
});

// Form Submit
$(document).on(`knack-form-submit.${viewIdWithDateField}`, function (_, {key: viewId}) {
    hideViewsOnDateTime(viewId, dateFieldId, viewIdsToToggleOn, viewIdsToToggleOff)
});

function hideViewsOnDateTime(viewId, fieldIdOfDateTime, viewIdsToToggleOn = [], viewIdsToToggleOff = []) {
    setInitialViewState(viewIdsToToggleOn, viewIdsToToggleOff);
    const currentDateTime = new Date();
    const timeToCompare = new Date();
    const dateFieldSelector = `#kn-input-${fieldIdOfDateTime} input`;
    waitSelector(`#${viewId} ${dateFieldSelector}`).then(() => {
        const viewElement = $(`#${viewId}`);
        const dateTimeInput = viewElement.find(`#kn-input-${dateFieldId}`);
        const date = dateTimeInput.find('input[name="date"]').val();
        const time = dateTimeInput.find('input[name="time"]').val();

        const [day, month, year] = date.split('/').map(Number);
        const [hours, minutes] = time.split(':').map(Number);

        timeToCompare.setHours(hours, minutes, 0, 0);
        timeToCompare.setDate(day);
        timeToCompare.setMonth(month - 1);

        const currentDate = new Date().setHours(0, 0, 0, 0);
        const compareDate = new Date(timeToCompare).setHours(0, 0, 0, 0);

        if (currentDate < compareDate) {
            console.log('Date Not Passed');
            toggleViews(viewIdsToToggleOff, viewIdsToToggleOn);
        } else if (currentDateTime < timeToCompare && currentDate === compareDate) {
            console.log('Date Equals today')
            const timeToCompareDifference = timeToCompare - currentDateTime;
            setTimeout(() => {
                toggleViews(viewIdsToToggleOn, viewIdsToToggleOff);
            }, timeToCompareDifference);
        } else if (currentDateTime > timeToCompare) {
            console.log('Date Passed');
            toggleViews(viewIdsToToggleOn, viewIdsToToggleOff);
        }
    })
    .catch(() => {
        console.error(`Failed to find date field ${fieldIdOfDateTime} in view ${viewId}`);
    });
}

/** waitSelector Copied from Normand @ KTL */
//Param: sel is a string, not the jquery object.
function waitSelector (sel = '', timeout = 5000) {
    return new Promise(function (resolve, reject) {
        if (!sel || $(sel).length) {
            resolve();
            return;
        }

        const intervalId = setInterval(function () {
            if ($(sel).length) {
                clearTimeout(failsafe);
                clearInterval(intervalId);
                resolve();
                return;
            }
        }, 100);

        const failsafe = setTimeout(function () {
            clearInterval(intervalId);
            reject(sel);
        }, timeout);
    });
}

async function toggleViews(viewIdsToShow, viewIdsToHide) {
    const togglePromises = [];

    if (viewIdsToShow) {
        viewIdsToShow.forEach(viewId => {
            togglePromises.push(
                waitSelector(`#${viewId}`, 15000)
                    .then(() => $(`#${viewId}`).show())
                    .catch(() => {console.error(`Failed to show view ${viewId}`);})
            );
        });
    }

    if (viewIdsToHide) {
        viewIdsToHide.forEach(viewId => {
            togglePromises.push(
                waitSelector(`#${viewId}`, 15000)
                    .then(() => $(`#${viewId}`).hide())
                    .catch(() => {console.error(`Failed to hide view ${viewId}`);})
            );
        });
    }

    return Promise.all(togglePromises);
}

async function setInitialViewState(viewIdsToShow, viewIdsToHide) {
    try {
        const togglePromises = [
            ...viewIdsToShow.map(viewId =>
                waitSelector(`#${viewId}`, 15000)
                    .then(() => $(`#${viewId}`).hide())
                    .catch(err => console.error(`Failed to hide view ${viewId}:`, err))
            ),
            ...viewIdsToHide.map(viewId =>
                waitSelector(`#${viewId}`, 15000)
                    .then(() => $(`#${viewId}`).show())
                    .catch(err => console.error(`Failed to show view ${viewId}:`, err))
            )
        ];

        await Promise.all(togglePromises);
    } catch (error) {
        console.error('Error in setInitialViewState:', error);
    }
}

It’s working now.

Thanks for your time!

1 Like

Here is a slightly simplified version. I had kept a function setInitialViewState that I setup when I began testing which is no longer needed as it has been replaced with toggleViews. I have removed console.logs, which I left in case there were any issues. I have also combined the view event handlers.

There is no need to replace your code unless you want to, I just thought I’d post the improved code:

const viewIdWithDateField = 'view_203';  // Form View where the date field is located
const dateFieldId = 'field_208';  // ID of the date field in the view
const viewIdsToToggleOn = ['view_137'];  // Views to show when the date has passed
const viewIdsToToggleOff = ['view_192']; // Views to hide when the date has passed

// Combined Event Listener for View Init and Form Submit
$(document).on(`knack-view-init.${viewIdWithDateField} knack-form-submit.${viewIdWithDateField}`, async function ({namespace: viewId}) {
    try {
        await hideViewsOnDateTime(viewId, dateFieldId, viewIdsToToggleOn, viewIdsToToggleOff);
    } catch (error) {
        console.error('Error in hideViewsOnDateTime:', error);
    }
});

async function hideViewsOnDateTime(viewId, fieldIdOfDateTime, viewIdsToToggleOn = [], viewIdsToToggleOff = []) {
    await toggleViews(viewIdsToToggleOff, viewIdsToToggleOn);
    const dateFieldSelector = `#kn-input-${fieldIdOfDateTime} input`;

    try {
        await waitSelector(`#${viewId} ${dateFieldSelector}`);
        const dateTimeInput = $(`#${viewId}`).find(`#kn-input-${fieldIdOfDateTime}`);
        const date = dateTimeInput.find('input[name="date"]').val();
        const time = dateTimeInput.find('input[name="time"]').val();

        const currentDateTime = new Date();
        const timeToCompare = new Date();
        const [day, month, year] = date.split('/').map(Number);
        const [hours, minutes] = time.split(':').map(Number);

        timeToCompare.setHours(hours, minutes, 0, 0);
        timeToCompare.setDate(day);
        timeToCompare.setMonth(month - 1);

        const currentDate = new Date().setHours(0, 0, 0, 0);
        const compareDate = new Date(timeToCompare).setHours(0, 0, 0, 0);

        if (currentDate < compareDate) {
            await toggleViews(viewIdsToToggleOff, viewIdsToToggleOn);
        } else if (currentDateTime < timeToCompare && currentDate === compareDate) {
            const timeToCompareDifference = timeToCompare - currentDateTime;
            setTimeout(async () => {
                await toggleViews(viewIdsToToggleOn, viewIdsToToggleOff);
            }, timeToCompareDifference);
        } else if (currentDateTime > timeToCompare) {
            await toggleViews(viewIdsToToggleOn, viewIdsToToggleOff);
        }
    } catch (error) {
        console.error(`Failed to find date field ${fieldIdOfDateTime} in view ${viewId}:`, error);
    }
}

async function waitSelector(sel = '', timeout = 5000) {
    if (!sel || $(sel).length) {
        return;
    }

    return new Promise((resolve, reject) => {
        const intervalId = setInterval(() => {
            if ($(sel).length) {
                clearTimeout(failsafe);
                clearInterval(intervalId);
                resolve();
            }
        }, 100);

        const failsafe = setTimeout(() => {
            clearInterval(intervalId);
            reject(sel);
        }, timeout);
    });
}

async function toggleViews(viewIdsToShow, viewIdsToHide) {
    const togglePromises = [];

    viewIdsToShow.forEach(viewId => {
        togglePromises.push(
            waitSelector(`#${viewId}`, 15000)
                .then(() => $(`#${viewId}`).show())
                .catch(() => console.error(`Failed to show view ${viewId}`))
        );
    });

    viewIdsToHide.forEach(viewId => {
        togglePromises.push(
            waitSelector(`#${viewId}`, 15000)
                .then(() => $(`#${viewId}`).hide())
                .catch(() => console.error(`Failed to hide view ${viewId}`))
        );
    });

    await Promise.all(togglePromises);
}

Craig