/** * @typedef {Object} ScheduleBank * @property {number} index * @property {string} description * @property {string} day * @property {string} time * @property {string} soundpath * @property {number} repeat * @property {boolean} enable * @property {string} broadcastZones * @property {string} language */ /** List of Schedulebank data loaded from server * @type {ScheduleBank[]} */ window.schedulebankdata = []; /** * Currently selected schedulebank row in the table * @type {JQuery|null} */ window.selectedschedulerow = null; dtScheduleBank = null; /** * Fill schedulebank table body with values * @param {ScheduleBank[]} vv values to fill */ function fill_schedulebanktablebody(vv) { dtScheduleBank.clear(); if (!Array.isArray(vv) || vv.length === 0) return; dtScheduleBank.rows.add(vv); dtScheduleBank.draw(); $('#schedulebanktable tbody').off('click').on('click', 'tr', function () { // if no data if (!dtScheduleBank) return; // if user click an empty row if (!dtScheduleBank.data().any()) return; const selected = dtScheduleBank.row(this) // toggle behaviour - unselect if already selected if ($(this).hasClass('row-selected')) { $(this).removeClass('row-selected').find('td').css('background-color', ''); window.selectedschedulerow = null; $('#btnRemove').prop('disabled', true); $('#btnEdit').prop('disabled', true); return; } // unselect previously selected row $('#schedulebanktable tbody tr.row-selected').removeClass('row-selected').find('td').css('background-color', ''); $(this).addClass('row-selected').find('td').css('background-color', '#ffeeba'); window.selectedschedulerow = selected.data(); $('#btnRemove').prop('disabled', false); $('#btnEdit').prop('disabled', false); }); $('#tablesize').text("Table Size: " + vv.length); } /** * Convert input date string yyyy-mm-dd to dd/mm/yyyy * @param {String} value from input date, which is in format yyyy-mm-dd * @returns {String} converted date in format dd/mm/yyyy */ function Convert_input_date_to_string(value) { if (value && value.length > 0 && value.includes('-')) { let parts = value.split('-'); if (parts.length === 3) { let year = parts[0]; let month = parts[1]; let day = parts[2]; return `${day}/${month}/${year}`; } } return ""; } /** * Convert string date dd/mm/yyyy to input date yyyy-mm-dd * @param {String} value string date in format dd/mm/yyyy * @returns {String} converted date in format yyyy-mm-dd */ function Convert_string_to_input_date(value) { if (value && value.length > 0 && value.includes('/')) { let parts = value.split('/'); if (parts.length === 3) { let day = parts[0]; let month = parts[1]; let year = parts[2]; return `${year}-${month}-${day}`; } } return ""; } /** * Reload timer bank from server * @param {string} APIURL API URL endpoint, default "ScheduleBank/" */ function reloadTimerBank(APIURL = "ScheduleBank/") { window.schedulebankdata = []; fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { if (Array.isArray(okdata)) { window.schedulebankdata.push(...okdata); selectedschedulerow = null; fill_schedulebanktablebody(window.schedulebankdata); } }, (errdata) => { alert("Error loading schedulebank : " + errdata.message); }); } $(document).ready(function () { console.log("schedulebank.js loaded successfully"); $('#schedulebanktablebody').empty(); selectedschedulerow = null; let $btnClear = $('#btnClear'); let $btnAdd = $('#btnAdd'); let $btnEdit = $('#btnEdit'); let $btnRemove = $('#btnRemove'); let $btnExport = $('#btnExport'); let $btnImport = $('#btnImport'); $btnEdit.prop('disabled', true); $btnRemove.prop('disabled', true); let APIURL = "ScheduleBank/"; if (dtScheduleBank === null) { dtScheduleBank = new DataTable('#schedulebanktable', { data: [], pageLength: 25, columns: [ { title: "No", data: "index" }, { title: "Description", data: "description" }, { title: "Day", data: "day" }, { title: "Time", data: "time" }, { title: "Sound Path", data: "soundpath" }, { title: "Repeat", data: "repeat" }, { title: "Enable", data: "enable" }, { title: "Broadcast Zones", data: "broadcastZones" }, { title: "Language", data: "language" } ] }); } let $schedulemodal = $('#schedulemodal'); // text input let $scheduleid = $schedulemodal.find('#scheduleid'); // text input let $scheduledescription = $schedulemodal.find('#scheduledescription'); // number input 0-23 let $schedulehour = $schedulemodal.find('#schedulehour'); // number input 0-59 let $scheduleminute = $schedulemodal.find('#scheduleminute'); // select2 for message let $schedulemessage = $schedulemodal.find('#schedulemessage'); // number input 0-5 let $schedulerepeat = $schedulemodal.find('#schedulerepeat'); // checkbox let $scheduleenable = $schedulemodal.find('#scheduleenable'); // select2 for broadcastzones let $schedulezones = $schedulemodal.find('#schedulezones'); // radio button for everyday let $scheduleeveryday = $schedulemodal.find('#scheduleeveryday'); // radio button for weekly let $scheduleweekly = $schedulemodal.find('#scheduleweekly'); // select2 for weekly selection let $weeklyselect = $schedulemodal.find('#weeklyselect'); // radio button for specific date let $schedulespecialdate = $schedulemodal.find('#schedulespecialdate'); // date input let $scheduledate = $schedulemodal.find('#scheduledate'); // select2 for language let $languageselect = $schedulemodal.find('#languageselect'); $schedulespecialdate.off('change').on('change', function () { if ($(this).is(':checked')) { $scheduledate.prop('disabled', false); } else { $scheduledate.prop('disabled', true); } }); function clearScheduleModal() { $scheduleid.prop('disabled', true).val(''); $scheduledescription.val(''); $schedulehour.val('0'); $scheduleminute.val('0'); $schedulerepeat.val('1'); $scheduleenable.prop('checked', true); $scheduleeveryday.prop('checked', false); $scheduleweekly.prop('checked', false); $schedulespecialdate.prop('checked', false); $weeklyselect.empty().select2({ data: window.scheduledays.filter(day => day !== 'Everyday').map(day => ({ id: day, text: day })), placeholder: 'Select days of the week', multiple: false, width: '100%', dropdownParent: $('#schedulemodal') }); $languageselect.empty().select2({ data: window.languages.map(lang => ({ id: lang, text: lang })), placeholder: 'Select languages', multiple: true, width: '100%', dropdownParent: $('#schedulemodal') }); $scheduledate.prop('disabled', true).val(''); $schedulezones.empty().select2({ data: window.BroadcastZoneList.map(zone => ({ id: zone.description, text: zone.description })), placeholder: 'Select broadcast zones', multiple: true, width: '100%', dropdownParent: $('#schedulemodal') }); let messageData = [...new Set(window.messagebankdata.filter(mb => !mb.message_Detail.includes('[')).map(mb => `${mb.description} [${mb.aNN_ID}]`))]; $schedulemessage.empty().select2({ placeholder: 'Select message', multiple: false, width: '100%', dropdownParent: $('#schedulemodal'), data: messageData.map(mb => ({ id: mb, text: mb })) }); $scheduleeveryday.off('change').on('change', function () { if ($(this).is(':checked')) { $weeklyselect.prop('disabled', true); $scheduledate.prop('disabled', true); } }); $scheduleweekly.off('change').on('change', function () { if ($(this).is(':checked')) { $weeklyselect.prop('disabled', false); $scheduledate.prop('disabled', true); } else { $weeklyselect.prop('disabled', true); } }); $schedulespecialdate.off('change').on('change', function () { if ($(this).is(':checked')) { $weeklyselect.prop('disabled', true); $scheduledate.prop('disabled', false); } else { $scheduledate.prop('disabled', true); } }); } let $findschedule = $('#findschedule'); // $findschedule.off('input').on('input', function () { // let searchTerm = $findschedule.val().toLowerCase(); // if (searchTerm.length > 0) { // window.selectedschedulerow = null; // let filtered = window.schedulebankdata.filter(item => // item.description.toLowerCase().includes(searchTerm) // || item.soundpath.toLowerCase().includes(searchTerm) // || item.broadcastZones.toLowerCase().includes(searchTerm)); // fill_schedulebanktablebody(filtered); // } else { // window.selectedschedulerow = null; // fill_schedulebanktablebody(window.schedulebankdata); // } // }); reloadTimerBank(APIURL); reloadBroadcastZones(); getLanguages(); getScheduledDays(); reloadMessageBank(); $btnClear.click(() => { DoClear(APIURL, "Timerbank", (okdata) => { reloadTimerBank(APIURL); alert("Success clear schedulebank : " + okdata.message); }, (errdata) => { alert("Error clear schedulebank : " + errdata.message); }); }); $btnAdd.click(() => { $schedulemodal.modal('show'); clearScheduleModal(); $schedulemodal.off('click.scheduleclose').on('click.scheduleclose', '#scheduleclose', function () { $schedulemodal.modal('hide'); }); $schedulemodal.off('click.schedulesave').on('click.schedulesave', '#schedulesave', function () { // Gather form values const Description = $scheduledescription.val(); const Message = $schedulemessage.val(); const Repeat = parseInt($schedulerepeat.val(), 10); const Enable = $scheduleenable.is(':checked'); // Collect selected days let _Day = ""; if ($scheduleeveryday.is(':checked')) { _Day = "Everyday"; } else if ($schedulespecialdate.is(':checked')) { _Day = Convert_input_date_to_string($scheduledate.val()); } else if ($scheduleweekly.is(':checked')) { _Day = $weeklyselect.val(); } const Language = $languageselect.val().join(';'); const broadcastZones = $schedulezones.val().join(';'); // Format time as HH:mm const hour = parseInt($schedulehour.val(), 10); const minute = parseInt($scheduleminute.val(), 10); const _Time = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`; if (Description.length > 0) { if (_Day.length > 0) { if (Message.length > 0) { if (Language.length > 0) { if (broadcastZones.length > 0) { // Prepare object const scheduleObj = { Description: Description, Day: _Day, Time: _Time, Soundpath: Message, Repeat: Repeat, Enable: Enable, BroadcastZones: broadcastZones, Language: Language }; fetchAPI(APIURL + "Add", "POST", {}, scheduleObj, (okdata) => { alert("Success add schedule: " + okdata.message); reloadTimerBank(APIURL); }, (errdata) => { alert("Error add schedule: " + errdata.message); }); $schedulemodal.modal('hide'); } else alert("At least one Broadcast Zone is required"); } else alert("Language is required"); } else alert("Message is required"); } else alert("Day is required"); } else alert("Description is required"); }); }); $btnRemove.click(() => { if (window.selectedschedulerow) { /** @type {ScheduleBank} */ let sr = { index: window.selectedschedulerow.index, description: window.selectedschedulerow.description, day: window.selectedschedulerow.day, time: window.selectedschedulerow.time, soundpath: window.selectedschedulerow.soundpath, repeat: window.selectedschedulerow.repeat, enable: window.selectedschedulerow.enable, broadcastZones: window.selectedschedulerow.broadcastZones, language: window.selectedschedulerow.language } if (confirm(`Are you sure to delete schedule [${sr.index}] Description=${sr.description}?`)) { fetchAPI(APIURL + "DeleteByIndex/" + sr.index, "DELETE", {}, null, (okdata) => { reloadTimerBank(APIURL); alert("Success delete schedule : " + okdata.message); }, (errdata) => { alert("Error delete schedule : " + errdata.message); }); } } }); $btnEdit.click(() => { if (window.selectedschedulerow) { /** @type {ScheduleBank} */ let sr = { index: window.selectedschedulerow.index, Description: window.selectedschedulerow.description, Day: window.selectedschedulerow.day, Time: window.selectedschedulerow.time, Soundpath: window.selectedschedulerow.soundpath, Repeat: window.selectedschedulerow.repeat, Enable: window.selectedschedulerow.enable, BroadcastZones: window.selectedschedulerow.broadcastZones, Language: window.selectedschedulerow.language } if (confirm(`Are you sure to edit schedule [${sr.index}] Description=${sr.Description}?`)) { $schedulemodal.modal('show'); clearScheduleModal(); // fill the form with existing data $scheduleid.val(sr.index); $scheduledescription.val(sr.Description); let [hour, minute] = sr.Time.split(':').map(num => parseInt(num, 10)); $schedulehour.val(hour.toString()); $scheduleminute.val(minute.toString()); $schedulemessage.val(sr.Soundpath).trigger('change'); $schedulerepeat.val(sr.Repeat); $scheduleenable.prop('checked', sr.Enable.toLowerCase() === 'true'); $languageselect.val(sr.Language.split(';')).trigger('change'); $schedulezones.val(sr.BroadcastZones.split(';')).trigger('change'); switch (sr.Day) { case 'Everyday': $scheduleeveryday.click(); break; case 'Sunday': case 'Monday': case 'Tuesday': case 'Wednesday': case 'Thursday': case 'Friday': case 'Saturday': $scheduleweekly.click(); $weeklyselect.val(sr.Day).trigger('change'); break; default: // check if the day is in format dd/mm/yyyy // and set the special date radio button and date input if (/^\d{2}\/\d{2}\/\d{4}$/.test(sr.Day)) { $schedulespecialdate.click(); $scheduledate.val(Convert_string_to_input_date(sr.Day)); } } $schedulemodal.off('click.scheduleclose').on('click.scheduleclose', '#scheduleclose', function () { $schedulemodal.modal('hide'); }); $schedulemodal.off('click.schedulesave').on('click.schedulesave', '#schedulesave', function () { // Gather form values const Description = $scheduledescription.val(); const Soundpath = $schedulemessage.val(); const Repeat = parseInt($schedulerepeat.val(), 10); const Enable = $scheduleenable.is(':checked'); // Collect selected days let Day = ""; if ($scheduleeveryday.is(':checked')) { Day = "Everyday"; } else if ($schedulespecialdate.is(':checked')) { // convert date from yyyy-mm-dd to dd/mm/yyyy Day = Convert_input_date_to_string($scheduledate.val()); } else if ($scheduleweekly.is(':checked')) { Day = $weeklyselect.val(); } // Broadcast zones (assuming comma-separated string) const BroadcastZones = $schedulezones.val().join(';'); const Language = $languageselect.val().join(';'); // Format time as HH:mm const hour = parseInt($schedulehour.val(), 10); const minute = parseInt($scheduleminute.val(), 10); const Time = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`; if (Description && Description.length > 0) { if (Day && Day.length > 0) { if (Soundpath && Soundpath.length > 0) { if (Time && Time.length > 0) { if (Language && Language.length > 0) { if (BroadcastZones && BroadcastZones.length > 0) { // Prepare object const scheduleObj = { Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language }; fetchAPI(APIURL + "UpdateByIndex/" + sr.index, "PATCH", {}, scheduleObj, (okdata) => { alert("Success edit schedule: " + okdata.message); reloadTimerBank(APIURL); }, (errdata) => { alert("Error edit schedule: " + errdata.message); }); $schedulemodal.modal('hide'); } else alert("At least one Broadcast Zone is required"); } else alert("At least one Language is required"); } else alert("Time is invalid"); } else alert("Message is not selected"); } else alert("Day is invalid"); } else alert("Description is empty"); }); } } }); $btnExport.click(() => { DoExport(APIURL, "schedulebank.xlsx", {}); }); $btnImport.click(() => { DoImport(APIURL, (okdata) => { reloadTimerBank(APIURL); alert("Success import schedulebank from XLSX : " + okdata.message); }, (errdata) => { alert("Error importing schedulebank from XLSX : " + errdata.message); }); }); });